Skip to content

docs(variation,#15589): le prev: declare n'est pas la cle d'adjacence — l'organe lit la sequence mergee - #15593

Merged
jsboige merged 1 commit into
mainfrom
docs/15589-prev-adjacency-key
Sep 12, 2026
Merged

jsboige merged 1 commit into
mainfrom
docs/15589-prev-adjacency-key

Conversation

@jsboige

@jsboige jsboige commented Sep 11, 2026

Copy link
Copy Markdown
Owner

Grain: LIGHT/docs — lane myia-po-2023:CoursIA — prev: MED/guard #15591

Summary

La ligne 19 de .claude/rules/variation-protocol.md affirmait que « le genre est la clé d'adjacence » — c'est-à-dire que G-VAR-3 se calculerait sur le genre déclaré dans prev:. C'est faux depuis #12095, et le coût est mesuré, pas théorique.

L'organe scripts/ci/variation_adjacency_guard.py résout le prédécesseur réel depuis la séquence mergée de la lane (--merged-prs-file) et rend le genre déclaré dans un champ séparé (declared_prev_genre) précisément pour ne pas s'y fier.

  • Le prev: déclaré est documentaire ; il n'est source de vérité qu'en repli (premier grain, ou échec de fetch — jamais un crash).
  • Le verdict dit toujours sa source : prev_source ∈ {merged-sequence, declared}.
  • Aucune lane ne déplace son adjacence en choisissant sa déclaration.

Pourquoi le champ est périmé par construction

Il est figé à l'ouverture de la PR. Une lane qui merge des grains pendant que sa PR est ouverte le rend faux. #11963 a mesuré prev: MED/guard #11841 — exact à la rédaction, suivi de quatre grains mergés ; le vrai prédécesseur était notebook-python.

Le coût, mesuré publiquement

Le 2026-09-11, au merge-gate, le coordinateur a dérivé l'adjacence à la main depuis les prev: déclarés et posé deux HOLD motivés G-VAR-3, sur #15551 (prev: LIGHT/readme #15550) et #15552 (prev: LIGHT/readme #15551) — une chaîne de trois LIGHT/readme, qualifiée d'« inexemptable par construction ».

L'organe, interrogé ensuite, rend l'inverse sur les deux :

{"guard_pass": true, "blocking": false, "adjacent": false,
 "genre": "readme", "prev_genre": "guard", "prev_pr": 15569,
 "prev_source": "merged-sequence", "declared_prev_genre": "readme",
 "reason": "genres differ (readme vs guard) -- no adjacency"}

Le prédécesseur réel était #15569 (MED/guard), mergé à 11:34:51Z, qui s'interpose dans la séquence et rompt l'adjacence. Deux rétractations ont suivi (issuecomment-5633877872, issuecomment-5633878069). Le défaut n'est donc pas réservé aux workers : il a fait écrire un motif faux au coordinateur, dans le geste même que la règle existe pour outiller.

Conséquence 2 — tranchée en NÉGATIF, avec sa mesure

L'issue proposait : « le garde de tag vérifie que le prev: référence une PR mergée de la même lane, en advisory d'abord ».

Cette piste a déjà été implémentée, mesurée, puis retirée le 2026-09-08. L'invariant PREV-ABANDONED (validate_prev_targets) ne rougit désormais que sur une PR fermée sans merge — lignée abandonnée — jamais sur une PR en vol. Mesure : cinq PRs ouvertes bloquées (#15156, #15190, #15207, #15209, #15210) citant quatre prédécesseurs distincts (#15129, #15175, #15199, #15203) — les quatre OPEN, pas un seul abandonné. Flaguer OPEN punissait exactement le comportement que R1 de proactive-coordination.md impose (« 1 PR entre 2 wakeups = PLANCHER, jamais plafond »).

Seule la relecture trompeuse est un défaut — et elle est traitée par la prose de cette PR, pas par un gate. Ré-introduire le flag serait un retour en arrière sur un correctif mesuré.

Témoin live sur cette PR même

Cette PR en est une démonstration directe. Elle déclare prev: MED/guard #15591 (#15591 est OPEN). L'organe, interrogé sur cette PR avec la séquence mergée réelle :

{"guard_pass": true, "blocking": false, "adjacent": false, "genre": "docs",
 "prev_genre": "notebook-python", "lane": "myia-po-2023:CoursIA",
 "prev_source": "merged-sequence", "declared_prev_genre": "guard",
 "prev_pr": 15448, "reason": "genres differ (docs vs notebook-python) -- no adjacency"}

Le prédécesseur réel est notebook-python (#15448, mergé), pas le guard déclaré. Autrement dit : un agent qui lit la ligne 19 croit que l'adjacence se joue entre docs et guard ; l'organe la joue entre docs et notebook-python. Les deux lectures ne portent pas sur le même fait — c'est exactement le défaut corrigé ici, reproduit à la demande sur la PR qui le corrige.

Acceptance

  • A1 — La ligne 19 ne présente plus le genre déclaré comme la clé d'adjacence, et nomme la séquence mergée. grep -c "le genre est la clé d'adjacence" = 0 ; grep -ci "séquence mergée" = 1.
  • A2 — Le chemin complet de l'organe figure dans la règle. grep -c 'scripts/ci/variation_adjacency_guard' .claude/rules/variation-protocol.md = 1 (> 0).
  • A3 — Le rôle documentaire du prev: est distingué par écrit de son rôle de calcul (mot « documentaire » + la clause de repli + « aucune lane ne déplace son adjacence en choisissant sa déclaration »).
  • A4 — Décision sur la conséquence 2 citée explicitement, en négatif et mesurée, dans le détail §2.1 — pas laissée implicite.

Hors périmètre respecté : le vocabulaire fail-OPEN de canonicalize_genre (#13475) n'est pas touché, non fusionné.

Review Checklist

  • 1. Scope — 2 fichiers, +20/−1, un seul sujet : corriger une prose fausse sur le mécanisme d'adjacence. Loin des seuils composites (G.4).
  • 2. Validation post-fix — check_docs_links.py --check relancé après le dernier commit : OK: No new broken links. (0 pre-existing, 6807 total). Les 4 acceptance sont vérifiées par grep sur le fichier écrit, pas de mémoire.
  • 3. Cohérence pédagogique — sans objet (pas de notebook).
  • 4. Exécution réelle — sans objet (docs-only). Tous les liens relatifs ajoutés résolvent sur disque (vérifié un par un, y compris ../../scripts/ci/variation_adjacency_guard.py).
  • 5. Regression check — aucun artefact généré touché (catalogue + docs/curriculum byte-identiques à main) ; le mécanisme décrit est lu dans le code de l'organe (variation_adjacency_guard.py L364-381, variation_prev_guard.py validate_prev_targets), pas déduit.

Sign-off user requis

.claude/rules/ est concerné → PR + sign-off user (CLAUDE.md §A). Je ne m'auto-autorise pas à modifier la doctrine : cette PR corrige un fait faux (le mécanisme réel du calcul d'adjacence, vérifiable dans le code), elle ne change aucune intention. La doctrine G-VAR-3 elle-même (le ban, la boucle de rattrapage, l'exemption) est inchangée.

Notes de review

  • La règle reste succincte (fichier auto-chargé, cf harnais: redescendre le contexte auto-charge sous 70k tokens (mesure: 98,5k, cible -29%) #15204) : le mécanisme, la mesure et le cas « prev: ouvert » vivent dans le détail §2.1, atteint par un pointeur d'une ligne (harness-hygiene, 3 tiers).
  • Je corrige une nuance de la rédaction proposée par l'issue : prev: déclaré n'est pas purely documentaire — il reste le repli. Écrire « purement documentaire » aurait remplacé une affirmation fausse par une autre.

Closes #15589

🤖 Generated with Claude Code

… — l'organe lit la sequence mergee

La ligne 19 de `.claude/rules/variation-protocol.md` affirmait : « le genre est
la cle d'adjacence ». C'est faux depuis #12095, et le cout est mesure.

L'organe `scripts/ci/variation_adjacency_guard.py` resout le predecesseur reel
depuis la SEQUENCE MERGEE de la lane (`--merged-prs-file`) et rend le genre
declare dans un champ separe (`declared_prev_genre`) precisement pour ne pas
s'y fier. Le `prev:` declare est documentaire, et source de verite seulement en
repli (premier grain, ou echec de fetch -- jamais un crash). Le verdict dit
toujours sa source (`prev_source`).

Pourquoi le champ est fige : il est ecrit a l'ouverture de la PR, donc une lane
qui merge pendant que sa PR est ouverte le perime. #11963 a mesure
`prev: MED/guard #11841` exact a la redaction puis suivi de quatre grains
merges -- le vrai predecesseur etait `notebook-python`.

Le cout, mesure publiquement : le 2026-09-11 le coordinateur a derive
l'adjacence a la main depuis les `prev:` declares et pose deux HOLD G-VAR-3
faux (#15551, #15552), tous deux retractes apres consultation de l'organe
(prev_genre: guard, prev_pr: 15569, prev_source: merged-sequence).

Conséquence 2 de l'issue -- la piste « le garde verifie que le prev: vise une
PR mergee » -- est tranchee EN NEGATIF, avec sa mesure : l'invariant
PREV-ABANDONED (`validate_prev_targets`, `variation_prev_guard.py`) ne rougit
que sur une PR fermee sans merge, jamais sur une PR en vol. L'ancienne lecture
« not merged » bloquait cinq PRs ouvertes citant quatre predecesseurs tous
OPEN, et punissait exactement le comportement que R1 impose.

La regle reste succincte (auto-chargee) : le mecanisme, la mesure et le cas
« prev: ouvert » vivent dans le detail §2.1.

See #15589

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

G-VAR-2 light cap reached (advisory, non bloquant).
La lane myia-po-2023:CoursIA a deja consomme son budget LIGHT du jour (#15371 (merge a 2026-09-11T00:40:01Z)).
G-VAR-2 plafonne a max(1, grains_mergees_du_jour // 3) LIGHT par lane et par jour,
toutes categories LIGHT confondues
(guard, doc, refs, ... partagent un seul budget) :
c'est un RATIO, pas un plafond plat. La decision de merge reste au coordinateur.

@github-actions github-actions Bot added variation-tier-inflation declared LIGHT << effective LIGHT-genre (#10020, advisory) variation-genre-run >= 2 grains consecutifs du meme genre LIGHT pour la lane (#10020, advisory) variation-genre-cap-exceeded light_genre > cap partage G-VAR-2 (#10020, advisory) labels Sep 11, 2026
@github-actions

Copy link
Copy Markdown
Contributor

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

  • TIER-INFLATION : declared LIGHT << effective LIGHT-genre (tally : declared=9 genre=12 cap=8)
  • GENRE-RUN : run consecutif d'un genre LIGHT (voir signals.runs dans le log du job)
  • CAP-EXCEEDED-BY-GENRE : light_genre > cap partage G-VAR-2 (tally : declared=9 genre=12 cap=8)

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.

@github-actions

github-actions Bot commented Sep 11, 2026 •

Copy link
Copy Markdown
Contributor

Path-collision (organ #13359/#13615)

Cette PR #15593 (docs(variation,#15589): le prev: declare n'est pas la cle d'adjacence — l'organe lit la sequence mergee) touche au moins un chemin de fichier aussi modifie par d'autres PRs ouvertes. Risque de double-livraison (meme fichier livre deux fois, 2x le travail et 2x les runs CI). Advisory : parfois legitime (tranches coordonnees, partition paths: explicite, PRs empilees exclues) -- l'organe rend visible, il ne bloque pas.

@jsboige
jsboige merged commit 4793dc3 into main Sep 12, 2026
21 of 22 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

lane-claim-absent Closing issue carries no claim at all (#10223) variation-genre-cap-exceeded light_genre > cap partage G-VAR-2 (#10020, advisory) variation-genre-run >= 2 grains consecutifs du meme genre LIGHT pour la lane (#10020, advisory) variation-light-cap-reached Lane ayant deja merge une LIGHT aujourd'hui (cap G-VAR-2 atteint) variation-tier-inflation declared LIGHT << effective LIGHT-genre (#10020, advisory)

Projects

None yet

1 participant