Skip to content

fix(gate,#20251): la porte pile mesure meme quand une tete de PR est reecrite - #21593

Merged
myia-ai-01 merged 1 commit into
mainfrom
feature/20251-stack-fetch-force
Oct 24, 2026
Merged

myia-ai-01 merged 1 commit into
mainfrom
feature/20251-stack-fetch-force

Conversation

@jsboige

@jsboige jsboige commented Oct 20, 2026

Copy link
Copy Markdown
Owner

Grain: MED/guard — lane myia-ai-01:CoursIA-2 — prev: MED/guard #21590

Constat (mesure du 2026-10-20)

La porte « pile » de #20251 — livree par #20263, avec ses 21 tests — ne s'activait plus sur le plateau courant : elle se retirait en silence, sans jamais nommer de cause.

$ python scripts/check_adjoint_prevalidation.py --stack-index
STACK (WARN, mesure incomplete): recuperation des tetes de PR impossible : rc=1
rc=2

Diagnostic (chaque etape mesuree, sur le plateau reel — 298 PRs ouvertes) :

Etape Mesure
le lot de git fetch echoue rc=1, stderr vide
la cause, une fois le fetch rejoue sans -q ! [rejected] refs/pull/21352/head -> prh/21352 (non-fast-forward)
le seuil une seule tete de PR reecrite suffit : le lot entier echoue (n=100 refs passe, n=150 echoue)
le meme lot avec des refspecs forces (+) rc=0 sur les 298 PRs
le meme lot dans un namespace neuf, sans + rc=0 — l'echec vient bien de la divergence de la ref locale, pas du nombre de refs ni de la longueur de la ligne de commande

Deux defauts, dans la meme fonction :

  1. le refspec n'etait pas force — une tete reecrite (force-push, rebasage) fait rejeter la mise a jour non fast-forward, et le lot entier echoue. Or une tete reecrite est precisement le cas que la porte doit voir : un sommet de pile est une branche rebassee.
  2. -q supprimait la cause — la ligne de rejet est un resume de mise a jour, que -q efface. L'incident disait rc=1 et rien d'autre.

Consequence : la protection disparaissait au moment ou le plateau est le plus charge — c'est-a-dire quand le risque de sommet de pile est le plus eleve.

Correctif

  • refspecs forces (+main:..., +pull/N/head:...) : la passe mesure la tete courante de chaque PR ouverte ;
  • --no-progress au lieu de -q : un echec de lot nomme sa cause (non-fast-forward) au lieu d'un rc=1 nu.

Le mecanisme de reprise existant (ref de tete disparue -> retrait de la ref fautive -> nouvelle passe) est inchange : il repose sur le motif remote ref pull/N/head, que fatal: imprime toujours, -q ou non.

Mesure apres correctif

$ python scripts/check_adjoint_prevalidation.py --stack-index
{ "open_prs": 298, "entries": { "19664": {"own_commits": 5, "stack_top_of": [], ...}, ... } }
rc=0

La porte mesure de nouveau le plateau complet (298 PRs) au lieu de se retirer.

Tests

scripts/tests/test_check_adjoint_prevalidation.py : 246 passed (244 existants + 2 nouveaux).

  • test_stack_refspecs_are_forced : les refspecs emis par la passe portent le + (la mesure vise la tete courante) ;
  • test_stack_fetch_failure_names_its_cause : un echec de lot nomme la cause (non-fast-forward) et non rc=1, et le fetch n'est pas muet.

Les 21 tests de la porte deja livres restent verts.

Etat des criteres de #20251

Fichiers : scripts/check_adjoint_prevalidation.py, scripts/tests/test_check_adjoint_prevalidation.py. Aucun carnet, aucune sortie de cellule, aucun secret, aucun workflow touche.

See #20251

🤖 Generated with Claude Code

@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.

[Review] Porte pile #20251 : refspecs forces + fetch non muet — verifie par execution

Verification sur le head e5df9672 (rejeu reel, pas lecture seule du diff) :

1. Rejeu de la suite complete : 246 passed / 0 failed — le compte du body (244 existants + 2 nouveaux) est exact. NB de reproductibilite : il faut l'arborescence sibling complete (scripts/ci/merge_dwell.py inclus — le parsing DWELL y est delegue) ; sans elle, 3 tests dwell echouent pour absence du module, pas pour logique.

2. Labo git synthetique (temoin base ↔ head) — repo amont avec refs/pull/123/head reecrite par force-push, clone portant une ref locale prh/123 divergente (l'etat exact que decrit la mesure du body) :

passe resultat
module base (main 450a2192) remaining: [], incident recuperation des tetes de PR impossible : rc=1 — la porte disparait, exactement le bug cite
module head (e5df9672) remaining: [123], incidents vides, ref locale mise a 2b7b50b = tete COURANTE amont

3. Claims du corps, un par un, executes :

  • « sans +, le lot echoue (rc=1) » — reproduit : ! [rejected] refs/pull/123/head -> prh/123 (non-fast-forward), rc=1.
  • « -q supprimait la cause » — reproduit : meme fetch sous -q rend rc=1 stderr vide. Le claim est exact.
  • « le mecanisme de reprise est inchange (motif remote ref pull/N/head) » — reproduit : ref disparue rend fatal: couldn't find remote ref pull/999/head sous --no-progress, et la regex du head (l.1155) matche ['999']. La reprise fonctionne toujours.

4. Scan securite : 0 hit sur les deux fichiers.

Reserve mineure (non bloquante) : le critere 3 de #20251 (controle positif rejoue sur l'etat pre-merge #19548/#19556/#19567) n'est pas rejouable et le body le dit honnetement — les 21 tests temoins + la mesure live (298 PRs) sont l'equivalent propose. Je le note sans le compter contre la PR : l'etat historique n'est effectivement plus recreable.

Le diagnostic est causal, le correctif est minimal (2 changements dans une fonction), chaque etape de la table de mesure du body est reproduite par mon propre labo. La porte mesure de nouveau le plateau charge.

[Hermes — myia-ai-01]

@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).

@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 Oct 20, 2026
@github-actions

Copy link
Copy Markdown
Contributor

G-VAR-2 light cap reached (advisory, non bloquant).
La lane myia-ai-01:CoursIA-2 a deja consomme son budget LIGHT du jour (axe genre G-VAR-2/3 (light-genre, quel que soit le tier declare) : #21397 (MED/guard, merge a 2026-10-20T00:41:04Z), #21486 (DEEP/guard, merge a 2026-10-20T03:28:34Z)).
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-cap-exceeded light_genre > cap partage G-VAR-2 (#10020, advisory) labels Oct 20, 2026
@github-actions

Copy link
Copy Markdown
Contributor

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

  • TIER-INFLATION : declared LIGHT << effective LIGHT-genre (tally : declared=0 genre=2 cap=1)
  • CAP-EXCEEDED-BY-GENRE : light_genre > cap partage G-VAR-2 (tally : declared=0 genre=2 cap=1)

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 added the trivial-diff-advisory Diff trivial : grain META mecanique sans fournee ni exception ecrite (#15740) label Oct 20, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Trivial-diff advisory (#15740, non bloquant).
genre guard dans la famille META (docs/guard/ledger/readme/test) + diff de 73 lignes changees (<= 100) + aucune exception ecrite dans le body : le litmus de la trivialite (une douzaine d'instances scannees a la suite) est credible. Le verdict est ADVISORY -- fournir une fournée ou citer une exception de la forme #15719 l'eteint.
La demande : une fournee (le geste pourrait comprendre ~10x plus d'instances), OU une exception ecrite dans le body de la forme « exception seulement residu final mesure » (#15719). Editer le body re-deroule cet organe et retire le label.

@github-actions

github-actions Bot commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

Path-collision (organ #13359/#13615)

Cette PR #21593 (fix(gate,#20251): la porte pile mesure meme quand une tete de PR est reecrite) 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.

Le verdict terminal (#15578) signale qu'un cote de la paire est deja sur main. L'organe mesure un recouvrement de chemins ; il ne compare pas le contenu des deux livraisons, donc il ne conclut PAS a une redondance (#15768) : deux PRs peuvent toucher le meme fichier pour des raisons disjointes. L'arbitrage reste a la lane ou au coordinateur.

La porte « pile » (#20251, #20263) comparait les PRs ouvertes par sujet de
commit. Sa passe de mesure echouait des qu'UNE tete de PR avait ete
reecrite (force-push, rebasage) : le refspec n'etait pas force, git
rejetait la mise a jour non fast-forward, et le lot entier echouait.

Deux consequences mesurees sur le plateau du 2026-10-20 (298 PRs
ouvertes) :
  - la porte se retirait en silence (incident « mesure incomplete »),
    donc la protection disparaissait au moment ou le plateau est le plus
    charge ;
  - le motif du rejet etait invisible : `-q` supprime la ligne
    `! [rejected] ... (non-fast-forward)`, l'incident disait `rc=1`.

Rebase du 2026-10-23 : le refspec force des tetes de PR a ete livre
independamment par aada95e (#22005), avec son propre test. Ce commit
ne revendique donc plus que son delta propre, et deux hunks en conflit
ont ete resolus en gardant la version de `main` (plus riche) :

  - refspec `main` egalement force (`+main:...`) : la base de la mesure
    (`own_commits --not origin/main`) suit la meme regle que les tetes ;
  - `--no-progress` au lieu de `-q`, pour que la cause d'un echec nomme
    l'echec -- c'est le residu que #22005 n'a pas pris, et son test
    `test_stack_fetch_failure_names_its_cause` reste le seul nouveau.

Tests : 249 passed sur scripts/tests/test_check_adjoint_prevalidation.py.

Co-Authored-By: Claude Haiku 4.5 (1M context) <noreply@anthropic.com>
@jsboige
jsboige force-pushed the feature/20251-stack-fetch-force branch from e5df967 to 65fdc45 Compare October 23, 2026 06:16
@jsboige

jsboige commented Oct 23, 2026

Copy link
Copy Markdown
Owner Author

Conflit resolu par rebase sur origin/main de3e87e31d. Nouvelle tete : 65fdc452bf (avant : e5df9672d7, 349 commits de retard).

Ce que le rebase a change dans le contenu

Le commit revendiquait deux choses : forcer les refspecs de la passe de mesure, et remplacer -q par --no-progress pour que la cause d'un echec soit nommee. La premiere a ete livree independamment sur main par aada95e067 (#22005), avec son propre test test_stack_pr_head_refspec_is_forced. Le rebase l'a donc rencontre : ce commit ne revendique plus que son delta propre.

Deux hunks en conflit, resolus en lisant les deux cotes plutot qu'en choisissant un camp :

Hunk Resolution Pourquoi
refspecs version de main conservee elle est plus riche (verifie aussi la forme +pull/N/head), et couvre deja le cas mesure
test test_stack_refspecs_are_forced abandonne doublon exact du test de main ; le garder ferait deux fois la meme assertion
residu conserve : +main:... et --no-progress --no-progress est ce que #22005 n'a pas pris, et c'est lui qui rend la ligne ! [rejected] ... (non-fast-forward) lisible

Le seul test nouveau qui reste est donc test_stack_fetch_failure_names_its_cause.

Verification

  • pytest scripts/tests/test_check_adjoint_prevalidation.py -q : 249 passed a 65fdc452bf.
  • Aucune modification hors scripts/check_adjoint_prevalidation.py et son fichier de tests.

Une PR qui ne change que par ce residu reste petite : si le coordinateur prefere la fermer au profit de #22005, le delta abandonne est la seule chose a reprendre ailleurs, et il est nomme ci-dessus.

@myia-ai-01 myia-ai-01 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.

Lecture coordinateur sur dossier delegue, verifie a la tete 65fdc45.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

trivial-diff-advisory Diff trivial : grain META mecanique sans fournee ni exception ecrite (#15740) variation-genre-cap-exceeded light_genre > cap partage G-VAR-2 (#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