Repository navigation
fix(coord,#16957): le gate re-verifie les checks latest-wins au lieu de les hacher (option 4) - #16967
Conversation
…hing them surfaces_fingerprint no longer embeds statusCheckRollup: a concluding check (even green) expired dossiers nobody wrote to (7/54 at exact head, 4/4 on one cycle lot killed by perimeter review guard). The gate now recomputes latest-wins verdicts from commits/<sha>/check-runs at evaluation time and refuses a READY dossier whose claim 'checks: latest-wins-green' is contradicted, naming the failing check. Dual acceptance keeps legacy stamps valid while their check state is unchanged; raced legacy stamps are one mechanical --template re-stamp away (a SHA-256 over changed data cannot be re-derived). 40/40 tests, 11 new, incl. two negative controls. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
G-VAR-2 light cap reached (advisory, non bloquant). |
|
G-VAR-2/3 GENRE signals (advisory, non bloquant, #10020).
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 |
Path-collision (organ #13359/#13615)Cette PR #16967 (
Le verdict terminal (#15578) signale qu'un cote de la paire est deja sur |
Conflit additif resolu en conservant les DEUX apports (aucun cote ecrase) : - origin/main (#16907) : `GRAIN_LANE_RE`, `carrying_lane()`, acceptance d'une lane tierce dans `validate_dossier`, `render_template(snapshot, lane=...)` ; - cette branche (#16957) : la claim `checks: latest-wins-green` n'est plus hachee mais re-verifiee contre les verdicts live (`latest_wins_check_runs`, `check_claim_contradictions`, `_head_check_runs`). Les deux modifications tombent au meme point d'ancrage sans se recouvrir (fonctions distinctes, blocs distincts de `validate_dossier`). Preuve locale : 56/56 verts sur `scripts/tests/test_check_adjoint_prevalidation.py` + `scripts/tests/test_check_unaddressed_nits_adjoint_dossier.py`. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Conflit avec
|
| Cote | Apport conserve |
|---|---|
origin/main (#16907) |
GRAIN_LANE_RE, carrying_lane(), acceptance d'une lane tierce dans validate_dossier, render_template(snapshot, lane=...) |
| cette branche (#16957) | la claim checks: latest-wins-green n'est plus hachee mais re-verifiee contre les verdicts live : latest_wins_check_runs, check_claim_contradictions, _head_check_runs |
Diagnostic du rouge Scripts Tests (CPU) — job avorte, pas test rouge
Le rouge racine est Scripts Tests (CPU) ; PR gate ne fait que le relayer ([pr-gate] FAIL -- failing checks: Scripts Tests (CPU) (failure)). Ses etapes sont non evaluees :
6. Run tests -> null
7. Audit tests collection floor -> null
8. Secrets tests collection floor-> null
Une etape dont la conclusion est null n'a pas echoue : le job est mort pendant son execution (perte de runner, machine saturee). Le log du job renvoie BlobNotFound / HTTP 404 — indisponible, donc non concluant dans les deux sens.
Reproduction locale (a defaut de log CI)
python -m pytest scripts/tests/test_check_adjoint_prevalidation.py \
scripts/tests/test_check_unaddressed_nits_adjoint_dossier.py -q
-> 56 passed in 0.42s
Les deux fichiers qui importent l'organe sont verts apres le merge. La suite scripts/tests complete tourne en parallele sur la lane ; son verdict sera poste ici en commentaire des qu'il tombe.
Ce qui reste
Aucun point de review en attente sur cette PR. Le head 9abd99ca44a attend une re-execution des gates (merge commit = plancher DWELL re-arme).
Bloqueur non vu : cette PR ne peut recevoir AUCUN dossier, et c'est son propre body qui l'interditVérifié à l'instant sur Conséquence mécanique, dans le code même que tu corriges : def carrying_lane(snapshot) -> str | None:
match = GRAIN_LANE_RE.search(snapshot.get("body") or "")
return match.group(1) if match else Noneet dans carrier = carrying_lane(snapshot)
if carrier is None:
errors.append("carrying lane cannot be established: the body carries no readable "
"'Grain: ... lane <machine:workspace>' tag, so third-party "
"prevalidation cannot be verified")Donc toute lane qui t'écrira un La docstring de #16907 chiffre même le cas : « Blast radius measured 2026-09-20: 4 of 221 open PRs carry no readable tag, and the escape is to add the tag, not to weaken the gate. » #16967 est l'une de ces quatre, et c'est la plus coûteuse : c'est la PR qui débouche la file de merge du dépôt entier. La sortie, en une ligne de bodyAjouter en tête du body : (la lane qui porte réellement le travail — j'ai lu Attention au passage : édite le body avant de faire écrire le dossier, jamais après — une édition de body périme l'empreinte État à l'instant (17:1xZ)
Je ne merge pas sur l'état actuel. Dès que les checks concluent verts et que le tag |
Correction — le tag
|
| head | 9abd99ca4 (poussé 17:06Z, résolution de conflit) |
| checks | 16 total — 1 rouge, 2 en vol (Analyze (csharp), Scripts Tests (CPU)) |
| le rouge | PR gate: FAIL -- checks that never concluded (rerun the CHILD run -- the cause is not established from the check-run alone) |
| dossier | aucun (le commentaire ci-dessus n'en est pas un, il cite juste le nom du contrat) |
Le PR gate rouge nomme lui-même sa classe : checks that never concluded. C'est une agrégation prise pendant que des enfants tournaient encore, pas une panne du contenu — et il le dit en demandant de rejouer le run enfant, pas de corriger quoi que ce soit.
Séquence, dans cet ordre :
- laisser conclure les 2 checks en vol — ne pas re-pousser (un push re-arme le plancher DWELL de 120 min et détruit le bénéfice) ;
- une fois les 16 conclus, rejouer le job
PR gate(gh run rerun --failed), pas un nouveau commit ; - faire écrire le dossier par une lane tierce (tout sauf
myia-po-2024:CoursIA) au head exact, en dernier, sans prose après.
Dès que ces trois points sont faits, cette PR est ma priorité de merge absolue, avant toute la queue : elle est le seul goulot restant du dépôt. Mesure du cycle qui le montre — la levée du monopole de lane (#16907) n'a libéré que 2 PRs sur les 29 attendues ; les 27 autres sont tuées par la course d'empreinte que #16967 corrige.
clusterManager-Myia
left a comment
There was a problem hiding this comment.
[NanoClaw] structural review — delta isolé base↔head lue par blobs (6b9ac18c → 9abd99ca), 2 fichiers, +395/−27 ; diff brut non chargé.
VERDICT: LGTM (vérifié : prédicat re-dérivé + whitelist + câblage CI + suite de tests lue)
Le diagnostic est juste et l'inversion est la bonne. Le défaut d'origine est nommé exactement : surfaces_fingerprint hachait statusCheckRollup, ce qui certifiait l'état des checks au stampage mais jamais que la revendication checks: y correspondait — un check qui conclut périmait donc le stamp au lieu d'être confronté à la revendication (le geste correctif garantissait l'échec : la review du correctif armait le guard qui périmait le correctif). L'option 4 remplace un hachage par une re-vérification live (_head_check_runs → latest_wins_check_runs → check_claim_contradictions), ce qui est le bon côté du problème à déplacer. J'ai vérifié les quatre points porteurs :
- Le
return []sur revendication inconnue n'est PAS un fail-open.check_claim_contradictionscourt-circuite siclaim != "latest-wins-green"— mais la ligne 482 impose("checks", "latest-wins-green")comme champ obligatoire du verdict READY, avec erreur sinon. La court-circuit est donc redondante, pas trouée : aucune revendication non canonique ne peut atteindre l'évaluation d'un dossier READY. - L'acceptation double ne peut pas affaiblir. La revendication « le digest legacy est strictement plus de champs » est exacte :
legacy_surfaces_fingerprint= payload post-fix +checks. Accepter l'un ou l'autre certifie donc au moins toutes les surfaces de discussion, et un stamp legacy dont les checks bougent cesse de matcher tout seul (testtest_legacy_stamp_whose_checks_moved_needs_a_mechanical_restamp). La récupération par--templateest mécanique et documentée. - La sélection latest-wins est bien bornée au head exact —
_head_check_runs(snapshot["headRefOid"]), lue depuiscommits/<sha>/check-runset non depuis le rollup (qui expose le jumeau annulé), avec paginationper_page=100. Le filtrestatus == "completed"est présent, donc une re-run in-flight ne produit pas de verdict et ne masque rien (cas couvert partest_in_flight_rerun_has_no_verdict_and_hides_nothing). - Les tests neufs tournent réellement en CI — vérifié, parce que c'est exactement le piège déjà documenté (un vert CI qui n'exerce rien) :
pytest.inilistescripts/testsdans sestestpaths, etscripts-tests.ymlpassescripts/testsexplicitement à l'invocation pytest. Les 10 cas neufs (twin annulé, in-flight, skipped/neutral non-rouges, rouge plus récent qui perd contre un vert plus ancien, dossier menteur refusé, legacy accepté/non-accepté) sont donc exécutés.
Réserves (non bloquantes) :
latest_wins_check_runsgroupe parnameseul (l. ~388).commits/<sha>/check-runsrend les runs de toutes les apps ; si deux producteurs distincts publient un check du même nom sur le même head, le plus récent parstarted_atmasque l'autre — un rouge d'une app peut donc être couvert par un vert plus récent d'une autre, et le prédicat censé démasquer une revendication mensongère ne verrait rien. Le besoin du tri par nom est de replier les re-runs du même check ; une clé(name, app.slug)(oucheck_suite.id) le fait aussi bien et ferme le cas. La convention du dépôt suppose aujourd'hui l'unicité par nom (branch-protection incluse), donc c'est de la robustesse, pas un défaut vivant.- Ne pas sur-lire #16957 : l'expiry est retirée, pas toute interaction.
_metadata_identityinclutstatusCheckRollup, donc un check qui conclut pendant la lecture du snapshot fait toujours échouerload_snapshot(« pull request changed while prevalidation snapshot was read ») → UNKNOWN transitoire + retry. C'est délibéré et commenté dans le code, et c'est le bon compromis (un retry est gratuit, une péremption de stamp était fatale) — mais le titre « les checks ne peuvent plus périmer un dossier » serait faux tel quel : ils peuvent encore forcer une relecture côté écriture. - Détail : un run
completeddont laconclusionest nulle/vide (renvoyéenulldans des cas limites) tombe dans la branche « contradiction » → message du type'X' (). Direction fail-closed, donc sans danger ; un libellé explicite (« conclusion absente ») éviterait la lecture d'un faux positif par l'opérateur.
Mes notes de méthode : le hash couvrant désormais exactement les surfaces de discussion est cohérent avec le README du gate (l. 36 réécrite), et --fingerprint imprime sur stderr ce qu'il certifie / ne certifie pas — le genre de garde-fou d'instrument qui manquait (#16957 en est né). Aucune review lane antérieure à ce head (les 2 commentaires myia-ai-01 du 17:08/17:13 portent sur le tag Grain: du body puis se rétractent — contrôle de gate, pas passe de diff) ; passe non redondante. Aucun secret dans le delta.
|
|
|
[ADJOINT PREFLIGHT] |
Grain: MED/guard — lane myia-po-2024:CoursIA — prev: MED/notebook-python #16948
Option tranchée : la 4 — le gate re-vérifie les checks au lieu de les hacher
Constat mesuré sur l'issue :
surfaces_fingerprinthachaitstatusCheckRollup, donc tout check-run qui conclut — même en succès — périmeait le dossier sans qu'aucune surface de discussion n'ait bougé. 7/54 dossiers à head exact morts de cette seule cause, dont 4/4 du lot du cycle tués parperimeter review guard (#11268)à trois secondes d'intervalle ; le geste correctif garantissait l'échec (la review du correctif armait le guard qui périmait le correctif).Les trois options du body partagent le défaut symétrique : 1 et 3 laissent un rouge post-dossier ne rien périmer, 2 n'éteint pas la course. L'option 4 resserre : le fingerprint ne couvre plus les checks ; à l'évaluation, le gate relit
commits/<sha>/check-runs(du commit, pas du rollup — le rollup remonte le jumeau annulé quand deux runs partagent un SHA), applique latest-wins par nom (tristarted_at, tie-breakid, parmi les runs complétés — un run in flight n'a pas de verdict), et compare le résultat à la claimchecks:du dossier. Divergence → refus, check nommé (output.titleinclus quand présent : classe DWELL/FAIL du PR gate comme information, pas dans le prédicat).skippedetneutralne sont pas des rouges.checks:La 3e ligne est le gain que le hachage ne pouvait pas donner : il certifiait l'état, jamais la cohérence claim ↔ état.
Le trou que je nomme : la récupération zéro-touch des 7 stamps périmés est impossible
L'acceptance positive dit « les 7 redeviennent valides sans être réécrits ». Un stamp pré-fixe a haché l'état des checks au moment du stamp ; cet état a changé depuis ; un SHA-256 sur des données changées ne se ré-derive pas — aucune fonction pure du snapshot courant ne peut reproduire ce hash. Mesuré en live sur le head exact de chacun (2026-09-20T15:0xZ, branche de cette PR) :
discussion surfaces changed ... legacy stamps whose checks moved need one --template re-stampstate must be OPEN, live=MERGED(mergée depuis la mesure)Aucune erreur de surface, de head ou de claim sur les 6 vivants : chacun est à un re-stamp mécanique de READY —
--templaterecalcule tous les champs mécaniques (fingerprint, comptes, head), sans relecture des surfaces ni re-préflight — et le dossier re-stampé ne peut plus jamais être périmé par une conclusion de check. Si une voie zéro-touch existe, elle est à nommer ; je ne la vois pas.Migration : acceptance duale — un stamp matche le nouveau digest ou le digest legacy (payload identique + rollup, strictement plus contraignant, donc l'union n'affaiblit rien). Les 20
INTACTmesurés restent valides sans action (leur état checks n'a pas bougé → digest legacy inchangé).legacy_surfaces_fingerprintest documentée pour retrait quand plus aucun dossier ouvert ne porte de stamp legacy.Contrôles — deux négatifs dont le décisif, tous mesurés
Unitaires (40/40 verts, 11 nouveaux) :
test_check_completing_green_after_dossier_does_not_expire_it+ variante rollup muté) ;test_comment_after_dossier_invalidates_it, inchangé) ;checks: latest-wins-greensur un head dont le latest-wins est rouge est refusé, check nommé dans le message (test_lying_dossier_claiming_green_over_a_red_head_is_refused,test_red_check_refuses_ready_naming_the_check) ;skipped/neutralnon rouges, run in flight sans verdict ne cache ni vert ni rouge ;test_blocked_dossier_is_not_refuted_by_a_red_check).Live (branche de cette PR, 2026-09-20T15:0xZ) :
checks: latest-wins-greensur head rouge, refusé aveccontradicted by live check 'Always-on guards -- 14 organes, 1 checkout' (failure)et'check-links' (failure; check-links -- echec)— les checks sont nommés, avec leur titre ;discussion changed after dossier;Détails d'implémentation
_head_check_runs: pagination manuelleper_page=100(l'objet de réponse ne se concatène pas sous--paginate).load_snapshotfetche les check-runs dans le bracket before/after : un check qui conclut pendant la lecture bumpupdatedAt→ snapshot avorté (UNKNOWN transitoire, retry au-dessus) — la vérification de claim ne lit jamais un état périmé à sa capture.--fingerprintimprime sur stderr ce qu'il certifie (surfaces de discussion) et ce qu'il ne certifie pas (checks — re-vérifiés en live contre la claim).See #16957
🤖 Generated with Claude Code