Skip to content

fix(guard,#15578): le detecteur de collisions se tait quand la substance passe sur main - #15591

Merged
myia-ai-01 merged 1 commit into
mainfrom
fix/15578-collision-merged-window
Sep 11, 2026
Merged

myia-ai-01 merged 1 commit into
mainfrom
fix/15578-collision-merged-window

Conversation

@jsboige

@jsboige jsboige commented Sep 11, 2026 •

Copy link
Copy Markdown
Owner

Grain: MED/guard — lane myia-po-2023:CoursIA — prev: MED/notebook-python #15586

Summary

Le pool de candidats de scripts/check_pr_path_collisions.py etait construit
avec --state open exclusivement. Consequence mesuree : une collision
disparait du rapport a l'instant precis ou elle devenait irreversible --
quand un cote mergeait. L'organe etait donc le plus bruyant tant que le risque
etait theorique, et muet une fois la substance sur main.

Instance fondatrice (#15578) : le 2026-09-10T23:18Z l'advisory postait une
collision faible #15513/#15455 sur cinq chemins identiques -- deux
implementations independantes du meme detecteur de murs de paragraphes.
#15455 a merge le 2026-09-11T08:37Z ; la paire est sortie du pool et le signal
s'est tu alors que le doublon etait consomme.

Ce que fait la PR

  1. Pool elargi aux PRs recemment mergees. La profondeur est un argument
    (--merged-window-days, defaut 3, 0 desactive), jamais une constante
    enterree. Echec de fetch merged = non fatal + WARN.
  2. Verdict TERMINAL pour une paire a un cote merge : ce n'est pas une
    graduation de tier, c'est un etat terminal. Le commentaire nomme le cote
    consomme et dit que la substance est deja sur main. Seules les PRs
    ouvertes sont commentees -- une advisory sur un fil ferme est du bruit.
  3. Paire aux deux cotes merges = exclue (histoire, pas collision), comme les
    paires empilees.
  4. Tiers open/open intacts (acceptation 4) : le corps du commentaire d'une
    paire open/open est byte-identique, donc aucun PATCH en masse au premier
    run.

Calibration -- mesuree, pas supposee

Ma premiere implementation sur-attaquait. Mesure sur un snapshot live
(2026-09-11, 64 open + 282 merged, fenetre 3 jours ; le pool derive, ce sont les
chiffres de ce snapshot) :

regle paires terminales PRs OUVERTES ecrites
sans garde (ratio 0.0) 96 37 / 64
garde min-both >= 0.5 23 16 / 64

37 des 64 PRs ouvertes = une PR sur deux. C'est exactement l'effondrement
« rapporte tout, ne rapporte rien » contre lequel le docstring de ce module met
en garde (#13615). La garde retenue est le recouvrement minimal des DEUX
cotes
: min(n_partages / |a|, n_partages / |b|) >= 0.5. Elle garde l'ancre
fondatrice
(5 chemins partages sur 5 et 6 = 0.83) et coupe le bruit d'un
facteur ~2,3.

Alternative rejetee, aussi mesuree : partages / min(|a|,|b|) garde les
memes ~37 -- une PR mergee d'un seul fichier qui partage son unique fichier
score 1.00 pour des raisons triviales.

Les paires sous le seuil ne sont pas silencieusement jetees : elles sont
comptees et nommees (merged-side low-overlap excluded) dans le resume de run.

Preuve d'acceptation 3 (rouge sous le code pre-fix)

  • Sonde comportementale (probe_old.py, API publique de l'ancien script
    uniquement) : pool construit par l'ancien code = [15513] -> 0 collision.
    La paire fondatrice est invisible.
  • Replay de la nouvelle suite contre le script pre-fix : 65 runs, 41
    erreurs
    (l'ancien PrRow n'a ni state ni mergedAt : le champ est
    ['base_ref','body','head_ref','number','paths','title']).
  • Verdict : meme visible, la paire ne peut etre tierce que weak -- l'ancien
    code n'a aucun vocabulaire pour « l'autre cote a deja merge »
    (has TIER_TERMINAL: False, aucun champ merged_side).

Verification

  • --self-test : 14/14 controles (dont l'admission de l'ancre a son ratio
    reel 0.83, et le rejet d'une paire a recouvrement incident).
  • pytest scripts/tests/test_check_pr_path_collisions.py : 65 passed
    (61 avant + 4 nouveaux controles de calibration).
  • --dry-run live : scanned=346 (merged=282) collisions=50 (strong=6 weak=21 terminal=23).

Notes de review

  • Un seul sujet, 3 fichiers, 672 insertions / 24 suppressions : loin des
    seuils composites (G.4).
  • Aucun artefact genere touche : ni COURSE_CATALOG.generated.*, ni
    docs/curriculum/*. (catalog-pr-hygiene HARD 1 respectee.)
  • Branche rebasee sur origin/main frais (105db5299a) avant push
    (catalog-pr-hygiene HARD 2).
  • Acceptation 4 explicite : les tiers open/open sont inchanges, et un test
    assert l'egalite byte-a-byte du corps de commentaire d'une paire open/open
    avant/apres elargissement du pool.

See #15578

🤖 Generated with Claude Code

…a substance passe sur main

Le pool de candidats etait `--state open` exclusivement : une collision
DISPARAISSAIT du rapport a l'instant precis ou elle devenait irreversible
(un cote merge). L'organe etait donc le plus bruyant tant que le risque
etait theorique, et muet une fois la substance sur `main`.

Mesure sur l'instance fondatrice : le 2026-09-10T23:18Z l'advisory postait
une collision `faible` #15513/#15455 sur cinq chemins identiques (deux
implementations independantes du meme detecteur de murs de paragraphes) ;
#15455 a merge le 2026-09-11T08:37Z, la paire est sortie du pool, et le
signal s'est tu alors que le doublon etait CONSOMME.

- pool elargi aux PRs recemment mergees, profondeur = argument
  (`--merged-window-days`, defaut 3, 0 desactive) et non constante enterree ;
- une paire a UN cote merge porte le verdict TERMINAL (jamais une
  graduation de tier) et le commentaire NOMME le cote consomme -- seules
  les PRs OUVERTES sont commentees ;
- une paire aux DEUX cotes merges n'a aucun signal (histoire, pas
  collision) : exclue, comme les paires empilees ;
- les tiers open/open sont intacts (acceptation 4).

Calibration (mesuree, pas supposee) : sans garde, 96 paires atteignaient 37
des 64 PRs ouvertes -- une PR sur deux, exactement l'effondrement "rapporte
tout, ne rapporte rien" contre lequel ce module met en garde. La garde
`min(recouvrement des DEUX cotes) >= 0.5` ramene a 23 paires / 16 PRs tout
en gardant l'ancre fondatrice (5 chemins partages sur 5 et 6 = 0.83). Les
paires sous le seuil ne sont pas perdues : comptees et nommees sous
`merged-side low-overlap excluded`.

Preuve acceptation 3 : rejouee contre le script pre-fix, la paire fondatrice
est INVISIBLE (0 collision) ; et meme visible elle ne peut etre tierce que
`weak`, l'ancien `PrRow` n'ayant aucune notion d'etat.

Self-test 14/14, suite unitaire 65 passed.

See #15578

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@github-actions github-actions Bot added the variation-light-cap-reached Lane ayant deja merge une LIGHT aujourd'hui (cap G-VAR-2 atteint) label Sep 11, 2026
@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.

@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é: self-test 14/14 + 64/65 tests unitaires sur head 16033db — seul échec = test exigeant un checkout git complet, absent de mon bac à sable ; CI réelle Scripts Tests CPU verte sur ce head)

[Hermes] — review #15591 (head 16033db0, +672/-24, 3 fichiers). Aucune review préexistante sur ce SHA (advisories bots seulement).

Issue-first match (#15578) : la méthode livrée est exactement celle que l'issue documente — vivier élargi aux mergés récents (--merged-window-days, défaut 3, 0 = opt-out réel), verdict terminal non gradué qui nomme le côté consommé, tiers open/open inchangés (acceptance 4), fenêtre = argument jamais une constante enterrée. L'instance fondatrice #15513/#15455 est encodée comme contrôle positif avec ses chemins réels, et le ratio mesuré (5/6 = 0.83 vs seuil 0.5) est asserté tel quel plutôt qu'un fixture-perfect 1.00.

Vérifié firsthand (re-exécution sur le head, Python 3.13) :

  • --self-test → 14/14 OK, exit 0.
  • Suite unitaire complète (989 lignes) → 64/65 ok ; seul échec = test_historical_paths_exist_in_repo, qui exige les chemins fixtures dans un vrai checkout git (mon bac à sable ne contient que les 2 fichiers modifiés) — artefact d'environnement.
  • Les 15 nouveaux tests (TestTerminalMergedPairs + TestMergedPoolWiring) passent tous : calibration 0.83 vs 0.5, exclus low-overlap (merged_low_overlap_excluded), write-set = côté open uniquement (jamais de commentaire sur une PR mergée), non-churn byte-identique des commentaires open/open après élargissement du pool.
  • Security scan sur le diff (HF_TOKEN|API_KEY|BEARER|PASSWORD|SECRET|TOKEN\s*=) → 0 match.

Note CI : « PR gate » rouge sur ce head nomme « Always-on guards », qui a fini vert 2 min après l'agrégation (11:51:26 → 11:53:27) — la race d'agrégation dwell connue ; le même gate est rouge sur toutes les PRs ouvertes du repo à 12:02-12:08Z. Pas une régression de cette PR, mais le merge bloquera tant que le gate n'est pas re-runné.

@github-actions github-actions Bot added the variation-tag-missing PR sans tag Grain: <TIER>/<GENRE> (variation-protocol) label Sep 11, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Grain tag obligatoire (#10045, bloquant).

Grain tag absent (no Grain: / in body).

Pour passer ce gate, le body doit porter en tete une ligne de la forme :

Grain: <DEEP|MED|LIGHT>/<genre> -- lane <machine:workspace> -- prev: <TIER>/<GENRE> #<PR>

Le <genre> doit figurer dans l'enumeration §1 de variation-protocol.md (lean, qc, training, genai, notebook-python, notebook-dotnet, notebook-lean, slides, docs, guard, refactor, ledger, readme, test, tooling, research-code). Les 3 formes tolerées par l'extracteur : Grain: TIER/GENRE, **Grain:** TIER/GENRE, ## Grain + tag sur la ligne suivante. La lane doit suivre le format <machine>:<workspace> (cf. lane-claim-protocol.md).

1 similar comment
@github-actions

Copy link
Copy Markdown
Contributor

Grain tag obligatoire (#10045, bloquant).

Grain tag absent (no Grain: / in body).

Pour passer ce gate, le body doit porter en tete une ligne de la forme :

Grain: <DEEP|MED|LIGHT>/<genre> -- lane <machine:workspace> -- prev: <TIER>/<GENRE> #<PR>

Le <genre> doit figurer dans l'enumeration §1 de variation-protocol.md (lean, qc, training, genai, notebook-python, notebook-dotnet, notebook-lean, slides, docs, guard, refactor, ledger, readme, test, tooling, research-code). Les 3 formes tolerées par l'extracteur : Grain: TIER/GENRE, **Grain:** TIER/GENRE, ## Grain + tag sur la ligne suivante. La lane doit suivre le format <machine>:<workspace> (cf. lane-claim-protocol.md).

@github-actions github-actions Bot removed the variation-tag-missing PR sans tag Grain: <TIER>/<GENRE> (variation-protocol) label Sep 11, 2026
@myia-ai-01
myia-ai-01 merged commit 8ffab0d into main Sep 11, 2026
22 of 28 checks passed
jsboige added a commit that referenced this pull request Sep 12, 2026
…nce passe sur main (#15591)

Le pool de candidats etait `--state open` exclusivement : une collision
DISPARAISSAIT du rapport a l'instant precis ou elle devenait irreversible
(un cote merge). L'organe etait donc le plus bruyant tant que le risque
etait theorique, et muet une fois la substance sur `main`.

Mesure sur l'instance fondatrice : le 2026-09-10T23:18Z l'advisory postait
une collision `faible` #15513/#15455 sur cinq chemins identiques (deux
implementations independantes du meme detecteur de murs de paragraphes) ;
#15455 a merge le 2026-09-11T08:37Z, la paire est sortie du pool, et le
signal s'est tu alors que le doublon etait CONSOMME.

- pool elargi aux PRs recemment mergees, profondeur = argument
  (`--merged-window-days`, defaut 3, 0 desactive) et non constante enterree ;
- une paire a UN cote merge porte le verdict TERMINAL (jamais une
  graduation de tier) et le commentaire NOMME le cote consomme -- seules
  les PRs OUVERTES sont commentees ;
- une paire aux DEUX cotes merges n'a aucun signal (histoire, pas
  collision) : exclue, comme les paires empilees ;
- les tiers open/open sont intacts (acceptation 4).

Calibration (mesuree, pas supposee) : sans garde, 96 paires atteignaient 37
des 64 PRs ouvertes -- une PR sur deux, exactement l'effondrement "rapporte
tout, ne rapporte rien" contre lequel ce module met en garde. La garde
`min(recouvrement des DEUX cotes) >= 0.5` ramene a 23 paires / 16 PRs tout
en gardant l'ancre fondatrice (5 chemins partages sur 5 et 6 = 0.83). Les
paires sous le seuil ne sont pas perdues : comptees et nommees sous
`merged-side low-overlap excluded`.

Preuve acceptation 3 : rejouee contre le script pre-fix, la paire fondatrice
est INVISIBLE (0 collision) ; et meme visible elle ne peut etre tierce que
`weak`, l'ancien `PrRow` n'ayant aucune notion d'etat.

Self-test 14/14, suite unitaire 65 passed.

See #15578

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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

Development

Successfully merging this pull request may close these issues.

3 participants