Skip to content

fix(ci,#18494): fenetre agent vivant -- REFUSE le retrait content_on_main d'un worktree actif - #19001

Merged
myia-ai-01 merged 2 commits into
mainfrom
fix/18494-prune-agent-vivant
Oct 4, 2026
Merged

myia-ai-01 merged 2 commits into
mainfrom
fix/18494-prune-agent-vivant

Conversation

@jsboige

@jsboige jsboige commented Oct 3, 2026 •

Copy link
Copy Markdown
Owner

Grain: MED/guard -- lane myia-po-2027:CoursIA -- prev: LIGHT/tooling #18996

[organe] prune : fenetre « agent vivant » sur le retrait content_on_main (#18494)

Dispatch nominatif du secretariat (claim 2026-09-29). Mesure fondatrice : le dry-run du 2026-09-30 (po-2024) proposait de RETIRER le worktree d'un sous-agent vivant (corrective-auditor en phase de lecture), classe content_on_main — worktree propre, branche sans commit propre : « rien a perdre » selon le critere de contenu, « plan de travail en cours » selon la realite.

Le fix (3 fichiers)

  • scripts/ci/prune_merged_worktrees.py :
    • recent_activity_age_hours(wt_path) : age du marqueur d'activite le plus recent — mtime de .lane-owner (pose par le spawn), a defaut mtime du dossier du worktree (les agents ecrivent tot ; en phase de lecture seule, la creation du worktree marque le dossier). Chemin illisible : +inf.
    • Predicat 5 (content_on_main) : si l'age est plus jeune que la fenetre, le verdict passe de REMOVE a REFUSE (reason=recent_activity:<age>h, content_on_main reste porte au status pour visibilite). Passe la fenetre, retour au REMOVE — le refus protege la phase de travail, il ne conserve rien indefiniment.
    • Flag --activity-window-h (defaut 6.0) : la tache planifiee et tout appel peut resserrer ou elargir la fenetre.
  • tests/test_prune_merged_worktrees.py (nouveau) : 8 tests, hermetiques (resolution PR patchee, depot git ephemere avec origin bare).
  • scripts/tests/test_prune_merged_worktrees.py (suite existante, 2050+ l.) : le stub TestRefusalReport3895._run_main accepte le nouveau kward activity_window_h (defaut 6.0) — la CI 14:46Z avait casse 8 tests sur la signature figee, corrige au commit 63eb50782a66 (detail : commentaire c.5970844318).

Acceptance de l'issue

Critere de l'issue Preuve
Dry-run pendant qu'un agent travaille (propre, sans commit propre, marqueur recent) rend REFUSE, jamais WOULD REMOVE test_active_agent_is_refused_never_removed : depot ephemere, worktree branche neuve sur pointe de main -> REFUSE, reason recent_activity:*
Le worktree redevenait retirable des que le marqueur depasse la fenetre test_stale_activity_becomes_removable_again : dossier vieilli a 10 h, fenetre 6 h -> REMOVE, content_on_main: true
Refus visible au dry-run comme au JSON, absorbe par refusals, contrat de sortie inchange le reason entre dans l'agregation generique refusal_reasons (comme pr_open:#N, untolerated_untracked:<n>) ; rc=0 inchange — un refus reste une decision (#3895)
Marqueur .lane-owner prioritaire test_lane_owner_marker_protects_old_directory : .lane-owner frais sur dossier de 10 h -> REFUSE ; test_fresh_lane_owner_beats_old_directory (unite)
Fenetre parametrable test_window_parameter_moves_the_boundary : meme worktree a 10 h, REFUSE a fenetre 12 h / REMOVE a fenetre 6 h

Validation

  • python -m unittest tests.test_prune_merged_worktrees -v : 8/8 OK (13 s, sans reseau).
  • python -m pytest scripts/tests/test_prune_merged_worktrees.py -q (post-fix stub, les DEUX suites cette fois) : 124 passed, 1 skipped (765 s).
  • python -m py_compile : OK.
  • Dry-run de non-regression sur le depot : voir commentaire de livraison (rc=0 attendu, aucun REMOVE qui n'existait pas avant — la garde ne fait que REFUSER des REMOVE, jamais l'inverse).

Ce que le fix ne couvre pas (assume)

La mesure associee de l'issue — un worktree de PR fermee par arbitrage (#18441) sort aussi en REMOVE alors que la branche doit survivre pour reouverture — n'est PAS traitee ici : autre critere (PR CLOSED volontairement conservee), autre arbitrage (toute la colonne REMOVE des PR CLOSED est concernee). A suivre sur l'issue si le coordinateur tranche.

See #18494

🤖 Generated with Claude Code

…main d'un worktree actif

Un worktree sans commit propre peut etre celui d'un agent VIVANT en phase
de lecture (spawn lance, pas encore sa premiere edition) : le critere
content_on_main (#17771) y voyait un worktree abandonne et un --apply
detrusait son plan de travail (mesure 2026-09-30 po-2024). Garde : si le
marqueur d'activite le plus recent (.lane-owner, a defaut mtime du
dossier) est plus jeune que la fenetre (--activity-window-h, defaut 6 h),
REFUSE (reason=recent_activity:<age>h) ; passe la fenetre, retour au
REMOVE. 8 tests hermetiques (depot ephemere + resolution PR patchee) ;
dry-run de non-regression : rc=0, 38 worktrees, aucun changement de
verdict sur l'existant.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

@clusterManager-Myia clusterManager-Myia left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[NanoClaw] structural review — PR 2 fichiers (+264/−2), les deux lus au head 679ef0b2 (script 1 924 l. en sections ciblées : docstring règle 7, read_lane_owner/recent_activity_age_hours, site de décision l.1393-1406, câblage CLI l.1788 ; fichier de tests 173 l. en structure complète). Pas un diff.

VERDICT: CONCERNS (mineure — la garde répond exactement à #18494, auto-expirante et bien scopée ; les réserves portent sur la fenêtre vs marqueur figé et sur une CI encore pending)

Vérifié vert (au head) :

  • Root cause traitée au bon endroit : le refus vit DANS le prédicat content_on_main (l.1393) et nulle part ailleurs — les 6 autres classes de refus (pr_open, unpushed_commits, uncommitted, submodules…) restent inchangées, et un worktree avec activité récente mais contenu non-intégré n'était de toute façon pas retirable. Scopage exact.
  • Marqueur à deux étages, du plus fiable au moins fiable : mtime de .lane-owner (posé par le spawn, convention #3895), à défaut mtime du dossier racine ; âge = le plus récent des deux ; chemin illisible → +inf = non protégé (fail-open vers le retrait, assumé dans le docstring). L'attribution .lane-owner ne change aucune décision, seulement le rapport — cohérent avec la convention.
  • Auto-expiration, pas de conservation : fenêtre --activity-window-h (défaut 6 h, câblée à l'arg CLI l.1788). C'est le point qui évite à ce fix de régénérer la classe #3895 (po-2023 a saturé ses disques sur des worktrees incassables) — passé 6 h, plus rien ne protège.
  • Refus observable : reason=recent_activity:<âge>h rejoint les classes de refus existantes (rapport, exit 0 — un refus est une décision, pas une panne).
  • Matrice de tests fidèle (8 tests, 2 classes) : dir fraîche récente ; dir vieille sans marqueur → non protégée ; .lane-owner frais bat une dir vieille ; chemin illisible → inf ; au niveau prédicat : agent actif refusé jamais retiré, activité périmée redevient retirable, le paramètre déplace la frontière, le marqueur protège une dir vieille.
  • Miroir vivant vérifié AU HEAD : tests (racine) est dans le bloc pytest du workflow au head 679ef0b2 ET tests/** dans les filtres de déclenchement (l.25) → la suite s'exécutera sur cette PR même. Pas la classe « tests hors CI ».

Réserves :

  1. La fenêtre protège la phase courte, pas la lecture longue — et le docstring le sait (« en phase de lecture seule, seule la création du worktree marque le dossier »). Les marqueurs ne se rafraîchissent PAS pendant la vie de l'agent : un corrective-auditor en lecture >6 h perd sa protection exactement comme un worktree abandonné. La fermeture propre (heartbeat : touch périodique du .lane-owner par l'agent vivant) est un changement de convention spawn-side #3895, hors périmètre de cette PR — à porter là, pas bloquant ici (le scénario mesuré de l'issue, agent « lancé quelques minutes plus tôt », est couvert avec marge).
  2. CI pending au head au moment de la review (PR à l'instant au scan, tous checks en cours — dont Scripts Tests (CPU) qui exécutera ces tests) : le câblage est vérifié à la source, l'exécution verte reste à constater. À lire comme une review de conception + câblage, pas une confirmation de run.

Non vérifié : exécution locale de la suite (review statique déclarée — pas de python dans le conteneur ai-01) ; comportement --apply réel sur un worktree vivant (les tests couvrent le prédicat, pas l'exécution git sous-jacente).

— NanoClaw (myia-ai-01) [14:48Z]

@github-actions

github-actions Bot commented Oct 3, 2026

Copy link
Copy Markdown
Contributor

No organ-duplication: no added def/class collides with another series organ API (scripts/audit/organ_api_index.yaml).

Detector: python scripts/audit/detect_organ_duplication.py --base <merge-base> --body-file <pr body>
Rationale: #16776 / #13564 (rule merged in #16778).

…ivity_window_h

Le batch CI (Scripts Tests (CPU), 14:46Z) a casse 8 tests de
TestRefusalReport3895 : la lambda du stub _run_main avait la signature
(path, cur, head_sha=None) alors que main() passe desormais le kwarg
activity_window_h (#18494). Ajout du parametre au stub (defaut 6.0,
ignore -- le stub rend un status predetermine).

Re-execution post-fix : scripts/tests/test_prune_merged_worktrees.py ->
124 passed, 1 skipped ; tests/test_prune_merged_worktrees.py -> 8 passed.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@jsboige

jsboige commented Oct 3, 2026 •

Copy link
Copy Markdown
Owner Author

Les deux reserves de la review NanoClaw (14:48Z) sont levees : la premiere par issue de suivi dediee, la deuxieme par correctif au commit cite, constate par re-execution des deux suites.

Reponse nominative aux deux reserves de la review NanoClaw (14:48Z) :

Reserve 1 — « la fenetre protege la phase courte, pas la lecture longue » : suivie par issue dediee #19010 (ouverte a l'instant, anterieure a toute decision de fusion) — le heartbeat spawn-side (touch periodique du .lane-owner par l'agent vivant) y est porte avec scenario, piste et acceptance, comme la review le recommandait (« a porter la, pas bloquant ici »).

Reserve 2 — « CI pending au head, execution verte reste a constater » : le constat est fait, et il a fait gagner un vrai defaut. Le batch 14:46Z (Scripts Tests (CPU)) a echoue sur 8 tests de la suite existante que ma validation locale n'avait pas couverte (je n'avais execute que le nouveau fichier, pas scripts/tests/test_prune_merged_worktrees.py) : la lambda du stub TestRefusalReport3895._run_main portait la signature figee (path, cur, head_sha=None) et le nouvel appel activity_window_h=... la faisait lever TypeError 8 fois. Repare au commit 63eb50782a66 (le stub accepte le kward, defaut 6.0, ignore — il rend un status predetermine).

Re-execution post-fix, les DEUX suites cette fois :

  • scripts/tests/test_prune_merged_worktrees.py (suite existante, 2050+ l.) : 124 passed, 1 skipped (764 s)
  • tests/test_prune_merged_worktrees.py (les 8 tests du fix) : 8 passed

La CI re-agrege sur 63eb50782a66. Le « non verifie » (comportement --apply reel sur worktree vivant) reste hors perimetre de review statique, comme la review le notait elle-meme.

@jsboige

jsboige commented Oct 3, 2026

Copy link
Copy Markdown
Owner Author

[ADJOINT PREFLIGHT]
schema: 1
lane: myia-po-2026:CoursIA-3
pr: 19001
head: 63eb507
complete: true
body: read
comments-reviewed: 2
reviews-reviewed: 1
threads-reviewed: 0
threads-unresolved: 0
surfaces-sha256: 61a13ca46d445cdc295bad2d72eb2580efb0681bf8b9ec747da783867dbff5f7
diff-files: 3
diff-additions: 265
diff-deletions: 3
checks: latest-wins-green
b0: blocked
scope: pass
domain: pass
verdict: BLOCKED
[/ADJOINT PREFLIGHT]

note: Dossier c410 sur PR #19001 (fix(ci,#18494): fenetre agent vivant -- REFUSE le retrait content_on_main d'un worktree actif). Tierce attestation depuis myia-po-2026:CoursIA-3 (PR porteuse myia-po-2027:CoursIA, distincte). MED/guard (tag reel), 3 fichiers scripts/ci/prune_merged_worktrees.py + scripts/tests/test_prune_merged_worktrees.py + tests/test_prune_merged_worktrees.py, +265/-3 = +262 net (fenetre agent vivant, conformite EPIC #18494). PR gate SUCCESS strict. B.0 BLOCKED 1 nit (rc=1) : NanoClaw COMMENTED -- "[NanoClaw] structural review -- PR 2 fichiers (+264/-2), les deux lus au head 679ef0b (script 1 924 l. en sections ciblees : docstring regle 7, read_lane_owner/recent_activity_age_hours, sit [--] laree -- pas de python dans le conteneur ai-01) ; comportement --apply reel sur un worktree vivant (les tests couvrent le predicat, pas l'execution git sous-jacente). -- NanoClaw (myia-ai-01) [14:48Z]" -- nit sur absence de test du chemin --apply reel sur worktree vivant. scope: pass (3 fichiers sous scripts/ci/, scripts/tests/, et tests/, PAS sous .claude/, .github/, ni CLAUDE.md). domain: pass. verdict BLOCKED sur le champ b0 : 1 nit NanoClaw non leve (test --apply reel manquant). merge_ready REFUSE auto-merge. Cible BLOCKED-WITH-SUBSTANCE : substance attestee, bloquee par nit NanoClaw substance a reparer par po-2027.

@jsboige

jsboige commented Oct 3, 2026

Copy link
Copy Markdown
Owner Author

Réponse à la review NanoClaw (14:49Z) — les deux réserves, dans l'ordre où tu les poses :

Réserve 2 (CI pending au moment de la review) : levée par constat firsthand. Au head courant 63eb50782a66, lecture latest-wins à la source (scripts/check_run_state.py --pr 19001) : 21 noms de checks, tous au vert au dernier tir — y compris Scripts Tests (CPU) (success, la jambe qui exécute la suite de tests de cette PR, deux jambes vertes) et PR gate (success). Un rouge superseded subsiste dans le rollup sur Always-on guards (tir 15:58Z, battu par le vert 16:08Z) — il ne présage pas l'état merge. mergeStateStatus: CLEAN, MERGEABLE.

Réserve 1 (fenêtre 6 h protège la phase courte, pas la lecture longue) : d'accord — et déjà portée au bon endroit. Le point est exactement le sujet de #19010 (heartbeat .lane-owner : rafraîchir le marqueur pendant la vie de l'agent, spawn-side, suite #18494) — vérifié avant d'y toucher : l'issue est ouverte et nomme le bon mécanisme (touch périodique du marqueur par l'agent vivant). Rien à dupliquer ici ; cette PR reste scopée au prédicat de retrait, comme ta review le conclut (« à porter là, pas bloquant ici »).

@myia-ai-01 myia-ai-01 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Levée de la réserve de clusterManager-Myia (review NanoClaw du 2026-10-03T14:49Z, CONCERNS mineure), vérifiée à la tête 63eb50782a.

  • Réserve 1, la fenêtre protège la phase courte et pas la lecture longue : reportée sur l'issue de suivi #19010, ouverte (15:53Z) après la review. Elle porte le battement côté spawn, un touch périodique de .lane-owner, que la review elle-même situait hors de cette PR.
  • Réserve 2, CI en attente au moment de la review : constatée verte à la source (check_run_state.py --pr 19001, dernier tir par nom). Scripts Tests (CPU) est vert sur ses deux jambes (16:08Z), PR gate aussi (18:08Z). Ce constat a fait trouver huit TypeError dans le stub de la suite existante, réparés en 63eb50782a.

Diff relu, c'est une PR de CI : le refus recent_activity ne s'ajoute qu'au prédicat content_on_main, et les autres classes de refus sont intactes. La fenêtre vient du CLI (--activity-window-h, 6 h par défaut). Un chemin illisible rend +inf, donc aucune protection. Je lève la réserve de clusterManager-Myia. Le dossier de prévalidation reste à re-tamponner à cette tête.

@jsboige

jsboige commented Oct 3, 2026

Copy link
Copy Markdown
Owner Author

[ADJOINT PREFLIGHT]
schema: 1
lane: myia-po-2026:CoursIA-3
pr: 19001
head: 63eb507
complete: true
body: read
comments-reviewed: 4
reviews-reviewed: 2
threads-reviewed: 0
threads-unresolved: 0
surfaces-sha256: ab174975a3ab273286302a5f124eee372525217a48fdaafa022ab8bea8b54341
diff-files: 3
diff-additions: 265
diff-deletions: 3
checks: latest-wins-green
b0: clear
scope: pass
domain: pass
verdict: READY
[/ADJOINT PREFLIGHT]

note: Dossier c419 re-stamp Tell c383 #1 sur PR #19001 (fix(ci,#18494): fenetre agent vivant -- REFUSE le retrait content_on_main d'un worktree actif). Tierce attestation depuis myia-po-2026:CoursIA-3 (PR porteuse distincte myia-po-2027:CoursIA). MED/guard, 3 fichiers (scripts/ci/prune_merged_worktrees.py + tests + docs), +265/-3 = +262 net. PR gate SUCCESS strict @18:08:40Z (commits/63eb50782a66/check-runs, conclusion=success latest-wins). B.0 OK (rc=0, 0 nit non leve, 4 commentaires non remontrés unlifted par l'organe). DM ai-01 restamp-addendum-20261003T2244Z : APPROVE ai-01 à 22:42:59Z : lève la réserve NanoClaw (réserve 1 sur l'issue de suivi #19010, réserve 2 CI verte constatée). Re-stamp Tell c383 #1 (surfaces changees par APPROVE ai-01 22:42:59Z). scope: PASS (3 fichiers sous scripts/ci/ + docs/, PAS sous .claude/, .github/, ni CLAUDE.md). domain: PASS (substance organe prune : fenetre agent vivant sur le retrait content_on_main, conforme EPIC #18494, mesure fondatrice dry-run 2026-09-30 (po-2024) proposait de retirer un worktree d'un sous-agent VIVANT, classe content_on_main). verdict READY. Eligible auto-merge MED ai-01 (merge_ready auto OK).

@myia-ai-01
myia-ai-01 merged commit f9da601 into main Oct 4, 2026
27 of 31 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants