Skip to content

Output-collapse ratchet: le canal par-PR ne lit jamais le body de la PR (TRANCHE2 sans --pr-body-file) #21552

Open

Description

@jsboige

Grain: LIGHT/guard — lane myia-po-2027:CoursIA-2

Défaut

Le garde bloquant Output-collapse ratchet (base vs PR) (#15327, bloquant depuis T2 #21424) documente une unique échappatoire : une section ## Output-collapse justification dans le body de la PR listant chaque cellule. Le canal qui publie le verdict par-PR ne lit jamais le body, donc l'échappatoire est structurellement injoignable :

  1. Entrée TRANCHE2 (scripts/ci/fast_lane_registry.py:861, absorbed=True) : argv = ["python", "scripts/notebook_tools/check_output_collapse.py", "{base_ref}"] — aucun --pr-body-file.
  2. fast-lane-shadow.yml : 0 occurrence PR_BODY/pr_body (aucun câblage du corps de PR) et trigger workflow_dispatch seul → github.event.pull_request nul.
  3. Workflow autonome notebook-output-collapse-ratchet.yml : workflow_dispatch seul ; son COLLAPSE_PR_BODY: ${{ github.event.pull_request.body || '' }} (l.94) est vide par construction en mode dispatch.

Le script (_load_pr_body) ne lit le body que via --pr-body-file ou l'env COLLAPSE_PR_BODY — les deux vides dans les deux canaux. Tout run par-PR rend donc 0 justified in body et tout MAGNITUDE est bloquant, justification ou non.

Instance vivante (PR #21037, tête c5063cc5123d)

  • Body avec section ## Output-collapse justification (cellule 7a064277, motif allegement declare).
  • Organe local check_output_collapse.py origin/main --pr-body-file <body> : 1 justified in body | 0 unaddressed, rc=0, « Gate passes » (mesuré 2026-10-20 à la tête exacte).
  • Check-runs CI à la tête : 04:50:45Z (batterie fast-lane) et 06:32:03Z (probe dispatch) — tous deux 0 justified in body, 1 unaddressed, failure.
  • Rouge non levable par aucun geste de lane.

Deuxième défaut (corrigé côté PR, à consigner dans l'organe)

La v1 du body listait la cellule en basename (Search-11-Metaheuristics.ipynb) ; le matching (_unaddressed, clé (row["notebook"], cell) avec row["notebook"] = path complet, l.394) ne la couvrait pas → unaddressed même avec le body lu. Corrigé dans le body de #21037 (chemin complet repo-relative). Suggéré : le message UNADDRESSED de l'organe devrait indiquer la forme attendue (« chemin complet du carnet, pas le basename ») — c'est la chute que tout auteur va refaire.

Correction attendue

Câbler le body dans le canal par-PR : --pr-body-file dans l'argv TRANCHE2 (écriture /tmp/pr_body.txt type always-on-guards.yml, lue en live) ou un input pr_number sur fast-lane-shadow.yml + fetch API. Inventorier au passage les autres gardes TRANCHE2 qui consomment un body.

Part of #21424

Activity

  1. jsboige commented on Oct 20, 2026

    @jsboige
    OwnerAuthor

    [CLAIMED] lane myia-ai-01:CoursIA-2 -- cabler le body de la PR dans le canal par-PR du ratchet Output-collapse, et nommer la forme attendue des cles dans le message UNADDRESSED -- paths: scripts/notebook_tools/check_output_collapse.py, scripts/notebook_tools/tests/test_check_output_collapse.py, .github/workflows/always-on-guards.yml

    Mesure avant claim (firsthand, 2026-10-20) :

    1. Le canal par-PR qui produit le check-run bloquant est always-on-guards.yml -> python scripts/ci/fast_lane.py --shadow. Il porte deja FAST_LANE_PR et passe un corps de PR en memoire (MD_CONTENT_LOSS_PR_BODY, l.1294) pour eviter le motif CodeQL actions/code-injection qu'un printf "${{ body }}" > fichier declencherait. Il ne passe rien pour le ratchet.
    2. _load_pr_body (check_output_collapse.py:1018) traite la valeur de COLLAPSE_PR_BODY comme un chemin de fichier. Donc le workflow dedie, qui lui passe le texte (COLLAPSE_PR_BODY: ${{ github.event.pull_request.body || '' }}, l.94), ouvrait un fichier nomme comme le corps de la PR et retombait sur une chaine vide. Les deux canaux sont muets, pour deux raisons differentes.
    3. Aucun test ne couvre COLLAPSE_PR_BODY (grep sur scripts/tests/ et scripts/notebook_tools/tests/ : 0 occurrence).

    Perimetre : le script (lecture + message), son fichier de tests, et l'env du seul workflow declenche sur pull_request. scripts/ci/fast_lane_registry.py n'est pas touche (PR #21547 ouverte par une autre lane sur ce fichier) : l'argv TRANCHE2 n'a pas besoin de changer, le corps arrive par l'environnement.

  2. jsboige commented on Oct 20, 2026

    @jsboige
    OwnerAuthor

    [DELIVERED] lane myia-ai-01:CoursIA-2 -- PR #21567, tete e35a95b1eb.

    Le defaut est corrige, et il etait plus large que l'enonce de l'issue : deux canaux muets, pour deux raisons differentes.

    1. Le canal par-PR (always-on-guards.yml -> fast_lane.py --shadow, celui qui publie le check-run bloquant) ne passait aucun corps. Il porte maintenant COLLAPSE_PR_BODY, en memoire, meme motif que MD_CONTENT_LOSS_PR_BODY (fix(guard,#8655): md-content-loss dit 'justifie en review' et ne lit aucune review — aucune porte entre 4 % et 75 % #13491).
    2. _load_pr_body traitait COLLAPSE_PR_BODY comme un chemin. Le workflow dedie, qui y met le texte du corps, ouvrait donc un fichier du nom de la PR et retombait sur une chaine vide -- son pull_request etait deja mort. Le lecteur accepte maintenant les deux formes : fichier existant -> lu comme chemin, sinon le texte est le corps.
    3. Le message UNADDRESSED nomme la forme attendue des cles (chemin complet repo-relative, pas le basename), et le resume dit si un corps a ete lu (no body read / N chars read) -- un cablage muet ne se confond plus avec une PR sans justification.

    Controles a la tete exacte de #21037 (c5063cc5123d), l'instance vivante de l'issue :

    • corps absent : rc=1, 0 justified (no body read) | 1 unaddressed (le controle negatif rougit toujours) ;
    • script d'avant, corps en env : rc=1, 0 justified | 1 unaddressed (le defaut, reproduit) ;
    • script corrige, corps en env : rc=0, 1 justified (7810 chars read) | 0 unaddressed, « Gate passes ».

    41 tests unitaires passent (4 neufs sur le canal du corps), self-test de l'organe OK.

    scripts/ci/fast_lane_registry.py n'est pas touche (collision #21547 evitee) ; aucun carnet modifie.

    Lane : myia-ai-01:CoursIA-2, c.2248.

  3. added a commit that references this issue on Oct 21, 2026
  4. jsboige commented on Oct 26, 2026

    @jsboige
    OwnerAuthor

    [CLOSURE PREFLIGHT]
    schema: 1
    lane: myia-po-2028:CoursIA-3
    issue: 21552
    verdict: KEEP
    acceptance:

    Lecture G.9 de l'issue et de ses 2 commentaires, puis verification firsthand sur origin/main.

    Le defaut central est corrige : le ratchet output-collapse lisait un body de PR vide, et ne pouvait donc jamais voir la justification ecrite dans le body. La lecture du body est livree, avec les deux formes attendues (--pr-body-file et la variable d'env in-memory), et le workflow qui cable le garde lui passe bien le body de la PR.

    La deuxieme correction attendue par l'issue n'est pas livree, et c'est pourquoi le verdict est KEEP.

    Ce qui a ete mesure sur main pour etablir cette seconde lacune :

    • git grep sur origin/main trouve sept gardes qui consomment le body d'une PR : detect_md_content_loss.py (MD_CONTENT_LOSS_PR_BODY), check_output_collapse.py (COLLAPSE_PR_BODY + --pr-body-file), check_added_axioms_cliquet.py (LEAN_CLIQUET_PR_BODY), enrich_quality_ci.py (ENRICH_QUALITY_PR_BODY), detect_notebook_plan_loss.py (PLAN_LOSS_PR_BODY), check_credited_examples.py (--pr-body-file) et la garde nuget de l'advisory dotnet (--pr-body-file).
    • Aucun de ces sept n'est nomme dans un artefact unique du depot. Le seul inventaire existant, scripts/ci/always_on_body_organs.sh, couvre la famille always-on des organes de protocole, qui lisent le body pour y chercher des marqueurs (Grain:, prev:, mot-cle de fermeture, genre, lane) -- pas le contenu que les sept gardes de contenu analysent.
    • scripts/ci/fast_lane_registry.py ne cite le canal body qu'a deux endroits, en commentaire, et son bloc TRANCHE2 ne porte aucun drapeau de body dans les argv.

    Pourquoi la lacune n'est pas cosine : un garde qui doit lire le body et ne le recoit pas (workflow non cable, variable d'env absente, garde appele hors workflow) ne signale rien -- il rend un verdict sur un body vide. C'est exactement le mode de panne corrige ici pour un seul garde, et rien dans le depot ne permet aujourd'hui de voir les six autres d'un coup d'oeil.

    Le residu est donc nommement porte par #22603, qui demande l'inventaire et l'organe qui le verifie, sans toucher au comportement d'un garde deja cable.

  5. added a commit that references this issue on Oct 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions