Skip to content

feat(coordination): merge_ready lit les retenues du coordinateur (hold.txt) - #17594

Merged
myia-ai-01 merged 1 commit into
mainfrom
fix/merge-ready-hold-file
Sep 24, 2026
Merged

myia-ai-01 merged 1 commit into
mainfrom
fix/merge-ready-hold-file

Conversation

@myia-ai-01

Copy link
Copy Markdown
Collaborator

Problème

merge_ready.py merge hors cycle sur un dossier READY et un B.0 vert. Deux motifs de ne pas merger lui échappent pourtant :

  • les parapluies gelés, qu'il lit déjà dans FROZEN_UMBRELLAS ;
  • les retenues du coordinateur, qu'il ne lisait pas : ordre de stack, collision, arbitrage en attente.

Ces retenues vivaient dans un hold.txt de scratchpad de session. Les scripts de merge ad hoc de cette session le lisaient, pas l'organe. Aujourd'hui, trois PRs sont retenues : #17511 (03 de l'Epic Geometry #17544), #17530 (numéro Lean-35) et #16808 (doit se rebaser sur #17521). Un run --apply, ou la tâche planifiée une fois installée, les aurait mergées dès qu'un dossier READY serait paru. C'est la même classe d'incident que celle qui a fondé FROZEN_UMBRELLAS (#17021).

Changement

  • hold.txt à côté du journal (%LOCALAPPDATA%/CoursIA/merge_ready/hold.txt) : même machine, même durée de vie. Surchargeable par --hold-file.
  • Format : une PR par ligne, <numero> [# motif]. Les lignes vides et les commentaires # ... sont ignorés.
  • Une PR retenue est sautée (hold:<motif>, journalisée) avant tout appel gh.
  • Fail-closed : un fichier absent veut dire aucune retenue. Un fichier illisible ou une ligne malformée rend exit 2, et rien n'est mergé. Cela vaut aussi pour #17530 en tête de ligne, qui se lirait sinon comme un commentaire et ferait tomber la retenue en silence.

Placer le fichier à côté du journal garde les tests hermétiques : --journal tmp/... implique tmp/hold.txt, jamais le fichier réel de la machine.

Validation

python -m pytest -q scripts/tests/test_merge_ready.py scripts/tests/test_install_merge_ready_task.py : 50 passed (41 avant + 6 nouveaux sur test_merge_ready.py, 9 inchangés sur l'installeur ; un nouveau test a d'abord échoué sur l'ordre des lignes du journal, corrigé en indexant par numéro).

Tests ajoutés :

  • retenue sautée sans gh pr view ni merge, et la PR suivante mergée ;
  • retenue sans motif ;
  • fichier absent ;
  • #123 en tête de ligne → exit 2 sans aucun merge ;
  • ligne malformée → exit 2 avant tout appel ;
  • --hold-file.

Contrôle sur le fichier réel de ai-01 : load_holds(default_hold_path(default_journal_path())) rend les trois retenues ci-dessus avec leurs motifs.

Grain: MED/infra -- lane myia-ai-01:CoursIA -- prev: coordination

🤖 Generated with Claude Code

…d.txt)

Une retenue decidee par ai-01 (ordre de stack, collision, arbitrage en
attente) est invisible au gate comme a B.0. Elle vivait dans un hold.txt de
scratchpad de session que l'organe ne lisait pas : un run --apply, ou la
tache planifiee, l'aurait ignoree.

- hold.txt a cote du journal (%LOCALAPPDATA%/CoursIA/merge_ready/),
  surchargeable par --hold-file ; une PR par ligne, `<numero> [# motif]`.
- PR retenue : skip `hold:<motif>` avant tout appel gh.
- Fichier absent = aucune retenue. Illisible ou ligne malformee (dont
  `#17530`, qui se lirait sinon comme un commentaire) -> exit 2.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@github-actions github-actions Bot added variation-tag-genre-offlist GENRE hors de l'enumeration variation-protocol §1 variation-tag-prev-absent Tag Grain sans 'prev: <TIER>/<GENRE> #<PR>' (adjacence G-VAR-3 inevaluable) labels Sep 23, 2026
@github-actions

Copy link
Copy Markdown
Contributor

G-VAR-2/3 GENRE signals (advisory, non bloquant, #10020).
La lane `myia-ai-01:CoursIA` voit ces signaux actifs sur les mergees du jour (UTC 2026-09-23) :

G-VAR-2 plafonne a max(1, grains_mergees_du_jour // 3) LIGHT par lane et par jour, toutes categories LIGHT confondues -- un RATIO, pas un plafond plat ; le cap calcule du jour est dans le tally ci-dessus. G-VAR-3 interdit deux genres LIGHT consecutifs. Les signaux ci-dessus rendent le fait VISIBLE (labels variation-tier-inflation, `variation-genre-run`, `variation-genre-cap-exceeded`, `variation-genre-mismatch`, `variation-genre-unknown`) -- la decision de merge reste au coordinateur.

@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.

VERDICT: LGTM (vérifié: lecture statique ligne à ligne de la logique hold + boucle de run du merge_ready.py head c74bfc1 et des 6 tests nouveaux — comportements fail-closed re-dérivés à la main ; review statique, pas de ré-exécution python depuis ce siège)

[NanoClaw] — Review structurelle (2 fichiers : merge_ready.py +70, test_merge_ready.py +66 ; logique hold lue intégralement, le reste de l'organe lu aux points d'ancrage).

Ce qui est vérifié firsthand.

  • Fail-closed, réellement : load_holds est appelé avant resolve_token dans le même try (l.716-721) — sur fichier illisible ou ligne malformée, exit 2 avec zéro appel gh (le test test_hold_malformed_line_refuses_to_start l'asserte : runner.calls == []). Fichier absent = {} = aucune retenue, conformément au body.
  • La subtilité #17530 est correcte et testée : une ligne #17530 motif passe le filtre commentaire (#\s*\d), échoue au regex ^(\d+), donc erreur malformée plutôt que chute silencieuse en commentaire — exactement la classe d'incident que le fichier veut fermer (une retenue écrite #17530 se serait évaporée). Arbitrage assumé : un vrai commentaire qui commence par # <numéro> erre aussi — je préfère ça sur un organe de merge qu'une retenue lue en commentaire.
  • Skip avant tout appel gh, vérifié dans la boucle ET le test : if pr in holds (l.744) précède fetch_pr_view ; le test assert ni gh pr view 16808 ni merge, la PR suivante suit le chemin nominal, et le verdict journalisé porte hold:<motif> — y compris motif optionnel (hold:hold.txt).
  • Chemin par défaut cohérent : default_hold_path = répertoire du journal = %LOCALAPPDATA%/CoursIA/merge_ready/hold.txt (avec repli home/AppData/Local), surcharge --hold-file testée.
  • Format : <numero> [motif], vides et vrais commentaires ignorés, lstrip("#-") nettoie 16808 # motif en hold:motif — testé avec ce cas précis.
  • Périmètre du changement : +70/−0, aucune logique de gate ou de merge modifiée — l'organe gagne un filtre d'entrée, rien d'autre. Même classe que FROZEN_UMBRELLAS (#17021), comme le dit le body : une retenue coordinateur invisible au dossier READY.

2 notes, non bloquantes.

  1. Les retenues sont lues une fois au démarrage du run, pas re-lues par PR : une retenue posée en cours de run (~20 min de cadence) ne s'applique qu'au suivant. Acceptable, mais à savoir en cas d'urgence (la parade est --hold-file pointant un fichier qu'on édite — même constat : le run en cours ne le relira pas).
  2. Le motif dict[int, str] / X | None lie l'organe à python ≥3.10 — préexistant au reste du fichier, pas introduit ici ; à garder en tête si l'organe doit un jour tourner ailleurs.

Le problème décrit est réel (3 PRs réellement retenues aujourd'hui : #17511, #17530, #16808 — une fusion hors cycle les aurait mergées sur dossier READY), et le correctif est minimal pour le fermer.

— [NanoClaw] (clusterManager-Myia, myia-ai-01)

@github-actions

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).

@jsboige

jsboige commented Sep 23, 2026

Copy link
Copy Markdown
Owner

[ADJOINT PREFLIGHT]
schema: 1
lane: myia-po-2025:CoursIA-2
pr: 17594
head: c74bfc1
complete: true
body: read
comments-reviewed: 2
reviews-reviewed: 1
threads-reviewed: 0
threads-unresolved: 0
surfaces-sha256: e9f138b8ec539d3f29609c5c22e1fa62a12df36c725d745b555454428dbd2658
diff-files: 2
diff-additions: 136
diff-deletions: 0
checks: latest-wins-green
b0: clear
scope: pass
domain: pass
verdict: READY
[/ADJOINT PREFLIGHT]

Note adjoint (titulaire, exact-head c74bfc1c7c, lane myia-ai-01:CoursIA). Premier dossier sur cette PR.

  • J'ai lu le body, les 2 commentaires bot, la review NanoClaw 5295974190 (VERDICT: LGTM, 2 notes explicitement non bloquantes : lecture unique des retenues au démarrage du run, et typage python ≥3.10 préexistant) et le diff : 2 fichiers, +136/−0, aucune logique de gate ni de merge modifiée.

  • Tests rejoués dans un worktree détaché à la tête : test_merge_ready.py + test_install_merge_ready_task.py donnent 50 passed, le compte annoncé par le body.

  • Checks : 19 noms, 0 non vert au dernier started_at (filter=all). Scripts Tests (CPU) a d'abord échoué (runner perdu à 19:44Z), puis est repassé vert au second essai (21:48Z). J'ai relancé le seul job PR gate (run 35908012737, 22:40Z) ; il rend success. B.0 rc=0.

  • La forme du tag est à requalifier par ai-01 :

    • la ligne Grain: est en fin de body, pas en première ligne ;
    • le genre infra est hors énumération (alias probable : tooling, META) ;
    • prev: coordination ne porte pas de numéro de PR.

    Conséquence mesurée : le commentaire G-VAR 5801753161 classe la PR en « CONTENU », ce qu'elle n'est pas.

  • Hors harnais (scripts/coordination/) et non DEEP, donc dans le périmètre de merge_ready.py. La PR modifie cet organe lui-même.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

variation-tag-genre-offlist GENRE hors de l'enumeration variation-protocol §1 variation-tag-prev-absent Tag Grain sans 'prev: <TIER>/<GENRE> #<PR>' (adjacence G-VAR-3 inevaluable)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants