Repository navigation
Fix(ci,#23089): le fetch d'un commit absent ne rend plus le depot superficiel, et la pile refuse de mesurer sur un historique tronque - #23110
Conversation
…erficiel `merge_dwell._ensure_commit_present` faisait `git fetch --depth=1 <sha>` meme dans un clone COMPLET : le depot passait shallow alors que son historique etait complet, et les calculs `main..tete` voyaient ensuite des commits deja merges comme « propres » -- faux « stack top », #22244 et #22252 rendues BLOCKED sur des squash deja merges le 2026-10-28. Le fetch garde `--depth=1` uniquement quand l'historique de la base est REELLEMENT tronque. Le predicat ne s'appuie pas sur `git rev-parse --is-shallow-repository` : ce booleen reste `true` des que `.git/shallow` porte une ligne -- y compris des racines mortes, sans rapport avec la base (six bornes de cette sorte dans le clone du coordinateur, booleen `true` en permanence alors que `main` etait complet : 18 788 commits visibles, le compte de l'API). Seule une borne qui est un ANCETRE de la base tronque son historique. Trois corrections mesurees en chemin : - `_first_parent` lit le parent dans l'objet commit (`git cat-file commit`) et non via `rev-list --parents` : sur une frontiere, le graft masque le parent a la traversee alors que l'objet conserve la ligne `parent` ; - le retrait d'une racine shallow morte supprime le FICHIER `.git/shallow` quand aucune frontiere ne subsiste : le booleen teste la PRESENCE du fichier, pas son contenu -- un fichier laisse en place, meme vide, maintiendrait le depot « shallow » en permanence ; - la porte de pile rend desormais une cause NOMINEE (`stack check unavailable: shallow clone`) au lieu d'inventer des piles sur un historique tronque. Le retrait des racines mortes ne touche a aucune ref ni a aucun commit : une racine qu'aucune ref ne contient n'est atteignable par aucun parcours, et son parent etait deja absent. Tests : 4 nouveaux (clone complet reste complet, clone tronque conserve le `--depth=1`, bornes etrangeres declenchent le retrait, predicat juste sur une borne qui tronque la base) + 2 controles negatifs (racine vivante non retiree, pile refusee sur clone tronque avec cause nommee, pile mesuree sur clone complet) ; 1514 tests verts sur les suites dependantes. Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
|
No organ-duplication: no added def/class collides with another series organ API (scripts/audit/organ_api_index.yaml). Detector: |
|
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 |
|
[ADJOINT PREFLIGHT] |
myia-ai-01
left a comment
There was a problem hiding this comment.
Relu à la tête 9959c5967ce97da1b889eae888f7db2ef0bd4e51. Le prédicat base_history_truncated() est la bonne réponse à #23089 : il ne compte que les bornes qui sont des ancêtres de la base, et c'est la seule question que pose un calcul main..tête. Un point retient la PR.
🔴 La garde de pile ne doit pas écrire dans .git/shallow. build_stack_index() appelle _prune_dead_shallow_roots() à chaque passe. Une lecture de gate modifie donc le dépôt de l'appelant, et le critère « racine morte » est plus étroit que la réalité :
git for-each-ref --contains <sha>ne voit niHEADdétaché, ni lesHEADdes autres worktrees (refs par worktree), ni les reflogs. Sur ai-01, plusieurs worktrees sont détachés sur des têtes de PR.- Dans un checkout superficiel détaché par SHA (
git fetch --depth=1 origin <sha>puischeckout --detach, sans ref nommée), la seule borne est le commit de tête lui-même. Aucune ref ne le contient et son parent est absent : il est classé mort et retiré. Le dépôt annonce alors un parent qui n'existe pas, et legit logou lerev-listsuivant du même job échoue. - Le retrait n'apporte rien à la garde : une borne étrangère à
mainn'est pas un ancêtre de la base, doncbase_history_truncated()rend déjàFalsesans elle.
Correctif demandé : retirer l'appel _prune_dead_shallow_roots() de build_stack_index(). Si le nettoyage reste utile au poste du coordinateur, en faire une commande explicite, lancée à la main (par exemple merge_dwell.py --prune-shallow), dont le critère compte aussi HEAD, les HEAD de chaque git worktree list --porcelain et les reflogs. Ajouter un test négatif : une borne atteinte par un HEAD détaché, sans aucune ref, doit survivre.
Le reste (détection de pile qui nomme sa cause, fetch sans --depth quand la base n'est pas tronquée, fichier qui disparaît plutôt que de se vider) me convient.
Path-collision (organ #13359/#13615)Cette PR #23110 (
|
|
[ADJOINT PREFLIGHT] |
Reserve ai-01 du 2026-10-28 sur #23110 : `build_stack_index()` appelait `_prune_dead_shallow_roots()` a chaque passe, donc une garde reecrivait `.git/shallow` du clone de l'appelant -- un fichier que seul le transport (`clone`/`fetch`) a le droit d'ecrire. - l'appel quitte `build_stack_index()` ; la passe LIT, elle n'ECRIT plus ; - le nettoyage devient la commande explicite `merge_dwell.py --prune-shallow`, lancee a la main (argument argparse, aucune garde ne l'appelle) ; - le critere d'une racine VIVANTE s'elargit au-dela de `for-each-ref` : `HEAD`, le `HEAD` de chaque worktree lie (`-C <path> rev-parse HEAD`, toutes versions de git) et les reflogs comptent aussi, par atteignabilite (`merge-base --is-ancestor`) et non par identite ; - deux tests negatifs : une borne atteinte par un HEAD detache sans ref ni reflog survit ; une borne atteinte par le seul HEAD d'un worktree lie survit. Controle positif mesure : avec `_alive_tips` neutralise, la premiere borne est bien retiree -- les tests ne passent pas par construction. 79 tests `test_merge_dwell.py` verts, 251 `test_check_adjoint_prevalidation.py` verts. `python scripts/ci/merge_dwell.py --prune-shallow` rend rc=0. Part of #23089. See #23110. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
La remarque de la review ai-01 du 2026-10-28 sur Ce qui a change
Deux details de mise en oeuvre :
Un second test negatif couvre le troisieme temoin demande : Preuves
La tete a bouge ( Part of #23089. 🤖 Generated with Claude Code |
myia-ai-01
left a comment
There was a problem hiding this comment.
[OVERRIDE] lane myia-ai-01:CoursIA -- leve ma propre demande de changements (myia-ai-01, review 5682529679 du 2026-10-28 12:25Z sur 9959c596), relue a la tete 7466d7f887a55d13b59091c57cbff1fcc11ea292.
Ce que je demandais, et ce que la tete fait :
- la garde ne doit plus ecrire dans
.git/shallow:build_stack_index()(scripts/check_adjoint_prevalidation.py) n'appelle plus le nettoyage, le commentaire le dit, et la fonction ne fait que lire le predicatbase_history_truncated(); - le nettoyage devient une commande explicite :
merge_dwell.py --prune-shallow, dont le critere de racine vivante compteHEAD, leHEADde chaque worktree lie et les reflogs, par atteignabilite (merge-base --is-ancestor), avec un doute qui ne retire jamais rien (_reachable_fromrendTrueen cas d'echec de lecture) ; - test negatif :
test_une_borne_atteinte_par_un_head_detache_survitettest_une_borne_atteinte_par_le_head_d_un_worktree_lie_survitsont la.
Verification independante en worktree detache a cette tete : test_merge_dwell.py et test_check_adjoint_prevalidation.py, 330 tests passes.
Le dossier exact-head reste a refaire a cette tete par une lane tierce.
|
[ADJOINT PREFLIGHT] |
|
[ADJOINT PREFLIGHT] |
|
[ADJOINT PREFLIGHT] |
Grain: MED/guard — lane myia-ai-01:CoursIA-2 — prev: MED/tooling #23027
Ce que fait ce correctif
merge_dwell._ensure_commit_presentramenait un commit absent pargit fetch --depth=1 --quiet origin <sha>— y compris dans un clonecomplet. Le dépôt passait alors superficiel alors que son historique
était complet, et les calculs
main..têtevoyaient ensuite des squashdéjà mergés comme « propres » : faux « stack top », #22244 et #22252
rendues BLOCKED sur des commits mergés le 2026-10-23.
Trois points, un seul correctif :
--depth=1n'est conservé que dans un dépôt dont l'historique de labase est réellement tronqué ; ailleurs le fetch se fait sans
--depthet ne rend jamais le dépôt superficiel ;rend une cause nommée, jamais un faux sommet de pile ;
absent du dépôt) sont retirées : elles ne masquent aucun historique et
elles seules maintenaient le dépôt « shallow » en permanence.
La preuve directe du chemin (demande 1 de l'issue)
Sur un clone complet jetable (bare d'origine + clone
--single-branch) :.git/shallowaprèsis-shallow-repositorygit fetch --depth=1 origin <sha>truegit fetch origin <sha>falseLe
fetch --depth=1crée donc bien la borne qui rend le dépôtsuperficiel — le chemin est établi avant le correctif, pas après.
Le choix du prédicat (précision du coordinateur du 2026-10-28)
Le coordinateur a signalé que sur ai-01
.git/shallowgarde six SHAaprès
git fetch --unshallow, queis-shallow-repositoryrend donctrueen permanence, et qu'une garde lisant ce booléen seul ne protègerien. Il laissait deux formes possibles : comparer le compte de
origin/mainà l'API, ou ne jamais passer--depthhors CI.La forme retenue est la seconde, generalisée : ne jamais passer
--depthhors d'un dépôt dont l'historique de la base est déjà tronqué.Elle ne lit aucun booléen global. Le prédicat
base_history_truncated()répond à la seule question qui compte pour un calcul
main..tête: uneborne du dépôt est-elle un ancêtre de la base ?
origin/main→ l'historique de la base esttronqué →
--depth=1conservé (comportement du checkout CI, inchangé) ;main(le cas des six SHA d'ai-01) → l'historiquede la base est intact → fetch sans
--depth;comportement d'avant le correctif).
Trois corrections mesurées en chemin
Le parent d'une frontière se lit dans l'objet, pas par
rev-list. Surune frontière shallow, le graft masque le parent à la traversée :
git rev-list --parents -n 1 <sha>rend le seul sha, sans parent. Leparent brut se lit dans l'objet (
git cat-file commit <sha>), quiconserve la ligne
parent. C'est cette ligne qui dit si la frontièretranche un historique réel ou si elle ne masque rien.
Le fichier
.git/shallowdoit disparaître, pas se vider.git rev-parse --is-shallow-repositoryrestetruetant que le fichierexiste, même vide — le booléen teste la présence du fichier, pas son
contenu (mesuré : fichier vidé →
true; fichier supprimé →false).Un retrait qui laissait un fichier vide en place aurait maintenu le dépôt
« shallow » exactement comme les racines qu'il venait de retirer.
Le retrait ne touche à rien. Une racine qu'aucune ref ne contient
n'est atteignable par aucun parcours de l'historique, et son parent était
déjà absent : le parcours s'arrêtait là de toute façon. Seule la réponse
« shallow » du dépôt change. Le retrait est signalé sur stderr, sans
incident : la mesure de la pile reste mesurée.
La cause nommée
check_adjoint_prevalidation.build_stack_index()rend désormais :au lieu d'un verdict de pile fabriqué sur un historique tronqué. La même
garde vaut pour tout calcul
main..têtede l'organe : la passe comparemain..tête, qui est fausse sur un historique tronqué (des commits déjàmergés y apparaissent comme « propres »).
Tests
_ensure_commit_presentsur un sha absentis-shallow=false) — contrôle positif_ensure_commit_present--depth=1est conservé — contrôle négatif.git/shallowne contient que des sha étrangers à l'historique demain--depth=1; les racines mortes sont retirées ; le fichier disparaît ;is-shallowredevientfalseorigin/main)Suites :
test_merge_dwell.py(77) ettest_check_adjoint_prevalidation.py(251) verts ; 1514 tests verts sur l'ensemble des suites dépendantes
(
merge_dwell,check_adjoint_prevalidation,check_unaddressed_nits,merge_ready,pick_idle_grain,pr_gate,approved_without_dossier,prevalidation_order_rule,sweep_stale_pr_branches,dwell_rule_matches_organ,frozen_campaigns,detect_shared_login,check_closure_dossier).Critères d'acceptation de l'issue
--queue, le dépôt n'est plus rendu superficiel parla passe elle-même : le fetch d'un sha absent ne crée plus de borne
dans un dépôt complet, et les racines mortes sont retirées (le
fichier
.git/shallowdisparaît,is-shallowredevientfalse) ;aucun verdict
stack top: elle rend la cause nommée ;merge_dwellet decheck_adjoint_prevalidationrestent verts.Périmètre
scripts/ci/merge_dwell.py,scripts/check_adjoint_prevalidation.pyetleurs suites de tests. Aucun changement de workflow, aucun changement de
politique CI, aucun changement de contenu de dépôt.
See #23089.
🤖 Generated with Claude Code