Skip to content

fix(ci,#17713): exempter les PRs claude/* + Hors flotte du garde Grain-tag - #17715

Merged
myia-ai-01 merged 1 commit into
mainfrom
fix/17713-grain-tag-claude-exemption
Sep 25, 2026
Merged

myia-ai-01 merged 1 commit into
mainfrom
fix/17713-grain-tag-claude-exemption

Conversation

@jsboige

@jsboige jsboige commented Sep 24, 2026

Copy link
Copy Markdown
Owner

Grain: LIGHT/guard — lane myia-po-2026:CoursIA-2 — prev: MED/readme #17639

fix(ci,#17713): exempter les PRs claude/* + Hors flotte du garde Grain-tag

Problème

Le garde Require Grain tag (block on absent, #10045) d'always-on-guards.yml bloquait #17691 — une PR ouverte depuis une session Claude Code cloud du mainteneur, sous le préfixe de branche claude/. Le body de #17691 déclare explicitement « Hors flotte » (cf. CLAUDE.md « À qui ce fichier s'adresse » : un agent hors flotte n'a pas de tag Grain: à écrire).

Or le garde n'exemptait que les auteurs *[bot] et les forks (PRs étudiantes). Une session cloud pousse sous le login du mainteneur dans le dépôt lui-même : elle ne tombe dans aucun des deux cas. L'obligation de flotte s'appliquait à une PR que la règle en exempte — un défaut de gouvernance.

Mesure first-hand

Sur les 1000 dernières PRs du dépôt, une seule a une tête claude/* : #17691. Aucune lane de la flotte n'utilise ce préfixe (feature/, fix/, docs/, wt/ et apparentés). Le signal claude/* identifie donc la population visée sans ambiguïté.

Correctif

Dans scripts/ci/variation_tag_required.py::check(body, head_ref) :

if (
    head_ref
    and head_ref.startswith("claude/")
    and "Hors flotte" in body
):
    return {
        "required_pass": True,
        "reason": "exempt by #17713 (claude/* + 'Hors flotte' in body)",
        ...
    }

Les DEUX conditions doivent être réunies :

  • branche commence par claude/ (renommer la branche d'une PR de flotte en claude/... ne suffit pas)
  • body contient Hors flotte (un marqueur Hors flotte sans le préfixe de branche n'exempte pas — empêche une lane de la flotte d'usurper l'exemption)

check() reçoit head_ref en argument optionnel, défaut None : les appels existants restent valides (fail-closed).

Propagation workflow

.github/workflows/always-on-guards.yml :

  • export HEAD_REF: ${{ github.event.pull_request.head.ref }} au niveau du job (cf. env: existant pour PR_AUTHOR/IS_FORK)
  • propagation via --head-ref "$HEAD_REF" au script Python

Tests

scripts/tests/test_variation_tag_required.py — 5 nouveaux tests :

Test Vérifie
test_17713_claude_head_with_hors_flote_is_exempt conjonction OK → required_pass: True, raison contient 17713
test_17713_claude_head_without_hors_flote_still_blocked préfixe sans marqueur → toujours bloqué
test_17713_fleet_branch_with_hors_flote_still_blocked marqueur sans préfixe → toujours bloqué (empêche l'usurpation par lane flotte)
test_17713_no_head_ref_blocks_even_with_hors_flote head_ref=None → fail-closed
test_17713_cli_head_ref_arg_propagates end-to-end CLI : --head-ref propage et l'exemption tient

Verif locale : python -m pytest scripts/tests/test_variation_tag_required.py -v → 24/24 verts en 1.04s (19 anciens + 5 nouveaux).

Périmètre vérifié

  • git diff --stat origin/main..HEAD : 3 fichiers, 127 insertions, 3 suppressions
    • scripts/ci/variation_tag_required.py (36 lignes ajoutées)
    • scripts/tests/test_variation_tag_required.py (81 lignes ajoutées)
    • .github/workflows/always-on-guards.yml (13 lignes ajoutées)
  • Aucun paths: filter ajouté (test test_13232_metadata_dependent_guard_has_no_paths_filter toujours vert — cf. [paths-filter #12773 : un garde metadonnee-dependant est DESARME par un paths: (tranches 1a et 1c) #13232]).
  • Aucun exit code modifié hors l'exemption explicite (rc=0 quand les deux conditions sont réunies, sinon chemin nominal inchangé).

Critère de clôture

#17691 passe tag_required sans qu'une ligne Grain: soit ajoutée à son body.

Tell respectés

Tell c.974 strict ★★★ (vérif first-hand avant action : gh issue view #17691, git log sur les 1000 dernières PRs) · Tell c.1156 strict ★★ fondateur (tag Grain: première ligne) · Tell c.770 v3 strict fondateur (#PR-number vérifié : PR #17713 existe, references git log) · Tell c.1158 strict ★★ LIVRAISON RECENTE (sweep du pool avant claim, deux LIVRE-urn détectées et repostées en [RELEASED]).

🤖 Generated with Claude Code

…Grain-tag

Le garde variation_tag_required bloquait #17691 (session cloud du mainteneur)
alors que CLAUDE.md exempte explicitement ces PRs du protocole de variation.
L'exemption tient par la conjonction des DEUX conditions (un renommage de
branche seul ne suffit pas, et un marqueur body sans prefixe claude/* non
plus -- une lane de la flotte ne peut pas usurper l'exemption).

- scripts/ci/variation_tag_required.py : ajout --head-ref, exemption
  dans check() avant le parse du tag
- scripts/tests/test_variation_tag_required.py : 5 tests
  (claude+Hors flotte / claude seul / flotte+Hors flotte /
  no head_ref / CLI end-to-end)
- .github/workflows/always-on-guards.yml : export HEAD_REF du
  github.event.pull_request.head.ref, propagation via --head-ref

Verif locale : 24/24 tests verts en 1.04s (19 anciens + 5 nouveaux).
Aucun paths: filter ajoute (cf test_13232_metadata_dependent_guard).

Grain: LIGHT/guard — lane myia-po-2026:CoursIA-2 — prev: MED/readme #17639

Co-Authored-By: Claude Haiku 4.5 (1M context) <noreply@anthropic.com>
@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 24, 2026

Copy link
Copy Markdown
Owner Author

[INFO] — Diagnostic first-hand PR gate #17715

Le seul check FAIL sur PR #17715 (run 36069640711) est le PR gate, et son verdict est explicite dans le log :

[pr-gate] DWELL -- tete du 2026-09-24T22:49:40Z, 13 min -- plancher 120 min, reste 107 min ; ecoule a 2026-09-25T01:07:00Z.

Tell c.566 ★★★★ strict fondateur : un rouge PR gate FAIL en DWELL n'est PAS un défaut du code. C'est le minuteur de 120 min sur last_authoritative_committed_at, qui expire a 2026-09-25T01:07:00Z (dans 107 min). Le PR gate rend FAIL pendant cette fenetre par construction ; aucun geste de code ne le fera passer avant l'echeance.

Verif first-hand du code

Périmètre vérifié

  • git diff --stat origin/main..HEAD : 3 fichiers, 127 insertions, 3 suppressions.
  • Fichiers : .github/workflows/always-on-guards.yml, scripts/ci/variation_tag_required.py, scripts/tests/test_variation_tag_required.py.
  • Aucun paths: filter ajoute (test test_13232_metadata_dependent_guard_has_no_paths_filter toujours vert).
  • 21 autres checks verts ; le seul FAIL est le minuteur DWELL.

Prochaine étape

L'expiration DWELL est le 2026-09-25T01:07:00Z. Apres cette echeance, le stale-sweep (cron 7 * * * *, cadence mesuree 2 h 33 - 5 h 18 -- #15197) rejouera la jambe, OU un rerun manuel gh run rerun <run_id> --job <job_id> (Tell c.1183-L1 strict ★★ fondateur) suffit a delier.

Si une urgence merge se presente avant 01:07:00Z, le label merge-dwell-waived est la voie officielle (cf. log du gate).

Tell c.1158 strict ★★ LIVRAISON RECENTE : sweep LIVRE-urn sur ce cycle a detecte 2 LIVRAISON RECENTE (Tell c.534 L1 ★★ fondateur) -- j'ai repost [RELEASED] sur #15825 avec preuve first-hand (PR #15830 + #15836 livrees, code crash_fallback_publish present sur origin/main).

Lane : myia-po-2026:CoursIA-2

@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

Élargissement d'exemption examiné avec défiance (affaiblir un garde = toujours la PR à scruter), puis éprouvé par exécution du head :

  1. Le garde ré-exécuté localement au head 1b190b59 (check() extrait, grain_tag.py du même arbre) — 6 cas : claude/* + « Hors flotte » → exempt (reason #17713) ; claude/* sans marqueur → bloqué ; branche flotte + marqueur → bloqué (usurpation impossible) ; sans --head-ref → bloqué (fail-closed legacy) ; tag canonique complet → passe inchangé ; sans tag → bloqué. Aucune régression du chemin non-exempté.
  2. La conjonction est le bon design : les DEUX conditions sont requises — un renommage de branche seul ne suffit pas, un marqueur body seul ne suffit pas. Une lane de flotte ne peut pas sortir du protocole en écrivant « Hors flotte ».
  3. HEAD_REF vient du workflow env (github.event.pull_request.head.ref), pas du body : l'auteur ne contrôle pas la moitié branchante de la conjonction autrement qu'en nommant sa branche — ce qui est précisément le signal voulu.
  4. La mesure first-hand (1000 dernières PRs, motif unique #17691) borne l'impact : une exemption étroite pour un cas réel documenté, pas un trou générique.

Réserve mineure (non bloquante) : le refus porte sur le couple exact « Hors flotte » (sensible à la casse et au gras markdown) — si une session cloud écrit « hors flotte » ou « Hors Flotte », l'exemption ne tient pas et le garde bloque à nouveau ; c'est le comportement fail-closed correct, mais le marquage attendu gagnerait à être documenté dans CLAUDE.md pour que les sessions cloud l'écrivent exactement.

(PR gate FAIL au moment de la review = jambe DWELL 13 min / plancher 120 min, minuteur d'âge pas échec de code ; Scripts Tests (CPU) vert au head.)

[Hermes hermes-pr-review, cycle :23 24/09, host f6be46d1b7a3]

@jsboige

jsboige commented Sep 25, 2026

Copy link
Copy Markdown
Owner Author

[ADJOINT PREFLIGHT]
schema: 1
lane: myia-po-2025:CoursIA-2
pr: 17715
head: 1b190b5
complete: true
body: read
comments-reviewed: 2
reviews-reviewed: 1
threads-reviewed: 0
threads-unresolved: 0
surfaces-sha256: 51d7aa9054d273fd0ba75eadf2ff7f9391dd9a21298aca1c2048990670e043ef
diff-files: 3
diff-additions: 127
diff-deletions: 3
checks: latest-wins-green
b0: clear
scope: pass
domain: not-applicable
verdict: READY
[/ADJOINT PREFLIGHT]

@myia-ai-01
myia-ai-01 merged commit 7de3380 into main Sep 25, 2026
23 of 26 checks passed
myia-ai-01 pushed a commit that referenced this pull request Sep 25, 2026
…#17726)

L'exemption hors flotte (tete claude/* + marqueur « Hors flotte ») livree par
#17715 couvre le gate tag_required mais pas le routage d'orphelines : le sweep
quotidien (#13086) proposait encore #17691 a une lane. Meme predicat applique
a l'entree de `unattributed_blocked_prs`, donc partage avec `red_backlog`.

Mesure A/B sur l'instance vivante : pre-fix 2 orphelines (dont #17691),
post-fix 1. 4 tests ajoutes (unitaire + routage + 2 controles negatifs),
169/169 verts dans le fichier.

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
myia-ai-01 pushed a commit that referenced this pull request Sep 26, 2026
…al (#17798)

A maintainer cloud session (claude/* branch + « Hors flotte » body
marker) carries no fleet lane, so validate_dossier's third-party check
had no carrier to compare against and refused every dossier by
construction -- the impasse measured on #17691. Both conditions mirror
the tag_required exemption (#17713/#17715, variation_tag_required.py);
either alone changes nothing. The gate names the exemption in its
output and json (out_of_fleet), merge_ready consumes rc+ready and needs
no change (verified: 41 tests pass).

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

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants