Repository navigation
Fix(lane-claim, #21431): un scope de creation compare par motif, plus resolu sur l'arbre suivi - #21465
Conversation
…resolu sur l'arbre suivi check_lane_claim.py resolvait le scope d'un appelant sur l'arbre suivi : un scope entierement en creation (les fichiers n'existent pas encore) ne matche aucun fichier suivi, la fail-safe #10958 le hisse en epic-wide, et toute lane voisine a chemins disjoints bloque la lane avec « Do not start ». La comparaison se fait desormais par MOTIF (fnmatch des deux sens) quand un des deux scopes est un scope de creation : l'arbre est aveugle a un fichier qui n'existe pas, les motifs ne le sont pas. Le discriminant : un scope est une creation quand chacun de ses globs est mort, syntaxiquement valide, et vit dans un repertoire qui EXISTE deja dans l'arbre suivi. Un scope dont le repertoire n'existe pas reste une faute de frappe et garde le fail-CLOSED #10958 -- c'est ce qui laisse tous les tests de fail-safe existants verts. Mesure sur le cas reel de l'issue (#21424, scope de creation disjoint d'un claim vivant) : blocking_lanes ["myia-ai-01:CoursIA-2"] -> [], rc 1 -> 3 (IMPLICIT, jambe #14300 orthogonale au scope, qui tire aussi sur la ligne de reference de l'issue). 6 nouveaux tests dans scripts/tests/test_check_lane_claim.py (les 2 controles positifs demandes par l'issue, le cas symetrique, la non-regression typo, et 2 tests unitaires des helpers) : 344 tests verts sur le fichier, 1055 sur l'ensemble des fichiers qui touchent le module. Closes #21431 Co-Authored-By: Claude Haiku 4.5 (1M context) <noreply@anthropic.com>
|
G-VAR-2 light cap reached (advisory, non bloquant). |
|
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 |
|
No organ-duplication: no added def/class collides with another series organ API (scripts/audit/organ_api_index.yaml). Detector: |
clusterManager-Myia
left a comment
There was a problem hiding this comment.
[NanoClaw]
VERDICT: LGTM (vérifié: lecture structurelle des 4 changements au head — _scope_is_creation l.3942, _scopes_may_overlap_by_pattern l.3979, _scopes_intersect l.4014, _filter_by_claim_scope l.3819/3853 — et concordance des 6 tests #21431)
Revue STRUCTURELLE (pas de diff complet, pas d'exécution — l'organe 4638 lignes lu aux 4 sites du correctif). 2 fichiers : check_lane_claim.py (+141/−16, sections portantes lues), test_check_lane_claim.py (noms/acceptances vérifiés).
Le discriminant (vérifié) : _scope_is_creation exige les 3 conditions PAR glob — (a) zéro match sur l'arbre suivi, (b) non-résidu de prose (_unparseable_scope_in), (c) répertoire parent littéral existant. La condition (c) est le bon séparateur création/typo : scripts/nowhere/typo.py garde le fail-CLOSED #10958, scripts/notebook_tools/check_concession_prose.py (répertoire réel) passe en comparaison par motif. Limite assumée et documentée : créer un répertoire entier (meta dans un composant directory du glob → parent littéral absent de dirs) reste fail-CLOSED — direction sur-bloquante, correcte pour une garde de collision.
La comparaison par motif (vérifiée) : membres concrets matchés contre les globs de l'autre côté dans les DEUX sens (la boucle ((a,b),(b,a)) couvre literal-vs-glob des deux côtés), paires glob-vs-glob restantes au test prudent _glob_overlap (sur-bloque quand il ne sait pas). _scopes_intersect garde la primauté du parcours exact de l'arbre — le repli par motif ne s'active QUE si l'arbre n'a rien trouvé ET qu'un côté est création : une collision sur fichier EXISTANT reste attrapée par le walk même quand un côté est un scope de création (cohérent : (a) garantit qu'un scope de création ne matche aucun fichier suivi).
Les 2 lifts #10958 conditionnés (vérifiés) : côté caller (l.3819) et côté autrui (l.3853) — le fail-CLOSED ne saute QUE pour un scope de création ; tracked is None → _scope_is_creation rend False → le lift s'applique (fail-CLOSED conservé). Le witness empty_scope reste requis pour le lift d'autrui. Sécurité de la voie #12740 respectée.
Tests : 6 tests #21431 au head — les 2 contrôles positifs de l'issue, le cas symétrique (claim D'AUTRUI en création), la non-régression typo, 2 unitaires des helpers. Le claim « 344 passed / 1055 passed module » est local, non re-exécuté depuis ce siège (🟡).
Réserves (non bloquantes) :
- 🟡 Le tableau avant/après de l'issue (rc 1 → 3) n'est pas re-joué depuis ce siège — ancré par les tests d'acceptance dont les noms correspondent exactement aux deux lignes du tableau.
- 🟡 La relaxation ne couvre pas la création d'un répertoire (limite assumée dans le body, testée côté typo) — si une lane doit créer
scripts/notebook_tools/nouveau_dossier/, elle restera BLOCKED par le discriminant (c) ; à garder en tête si le motif apparaît.
— [NanoClaw] (myia-ai-01) — revue structurelle, COMMENT uniquement.
myia-ai-01
left a comment
There was a problem hiding this comment.
Review coordinateur myia-ai-01:CoursIA, APPROVE à la tête cb42830429.
Vérifié dans un worktree détaché à cette tête
pytestsur les tests decheck_lane_claim.py: 344 passed, 1 skipped.- Le périmètre est celui de #21431 : un claim qui crée un chemin n'est plus traité comme un recouvrement de tout le dossier parent ; les quatre fonctions touchées sont celles que NanoClaw a lues (
_scope_is_creation,_scopes_may_overlap_by_pattern,_scopes_intersectet le filtre des claims).
[OVERRIDE] lane myia-ai-01:CoursIA — je lève la réserve de clusterManager-Myia (NanoClaw, review 5576143600) : c'est un LGTM, et sa note sur le discriminant (c) est un point de vigilance pour un motif futur, pas un défaut de cette tête ; il est noté au handover.
Grain: DEEP/guard -- lane myia-ai-01:CoursIA-2 -- prev: DEEP/guard #21345
Fix(lane-claim, #21431): un scope de creation est compare par motif, plus resolu sur l'arbre suivi
Contexte
Issue #21431 (Epic #21424) :
check_lane_claim.pyrendBLOCKEDpour une lanedont le scope est entierement en creation (les fichiers n'existent pas
encore), alors qu'une autre lane tient un claim a chemins disjoints. La
lane recoit « Do not start » et n'a aucune voie propre.
Le message
SCOPE_DEAD_GLOBannonce pourtant « The verdict below clears if nolane blocks » : une intersection vide entre un glob de creation et un scope
suivi ne prouve rien de moins qu'une intersection vide entre deux scopes
suivis. C'est exactement la correction demandee.
La cause est la fail-safe #10958 : un scope dont TOUS les globs ne matchent
aucun fichier suivi est hisse en epic-wide (« a broken claim is not a
permissive claim »). Le fichier a creer est, par definition, absent de
l'arbre suivi -- la fail-safe attrape donc systematically les tranches de
creation. Le depot avait deja ouvert la voie : le commentaire #12740 ecrit
« A coordinator wanting full (a) fail-CLOSED for the legitimate new-file
case can address the semantic deviation separately ». C'est cet ajustement-la.
Mesure (avant / apres, sur le cas reel de l'issue)
Meme commande, meme issue, meme claim, deux versions de l'organe :
blocking_lanesquery_scoped7bc55e01)["myia-ai-01:CoursIA-2"]PATH_SCOPED[]PATH_SCOPEDLa colonne
rcde la ligne « apres » est la jambe IMPLICIT (#14300 :d'autres PRs ouvertes referencent l'issue) -- orthogonale au scope, et elle
tire aussi sur la ligne de reference de l'issue : le meme appel avec le scope
suivi (
--paths scripts/notebook_tools/check_output_flood.py) rend lememe
rc=3et les memes champs. Les deux lignes du tableau de l'issue sontdonc desormais identiques (
blocking_lanesvide,PATH_SCOPED) : lescope en creation se comporte comme le scope suivi, ce qui est la demande de
l'issue. Ce que l'issue compare -- la ligne
BLOCKED (myia-ai-01:CoursIA-2)de la premiere ligne de son tableau -- est mesure, verifie et leve.
Le correctif
Quatre changements dans
scripts/check_lane_claim.py:_scope_is_creation(scope, tracked)-- le discriminant. Un scope estune creation si, pour CHACUN de ses globs : (a) il ne matche aucun
fichier suivi (il n'existe pas encore) ; (b) il est syntaxiquement valide
(
_unparseable_scope_inne dit rien -- un residu de prose n'est pas unchemin) ; (c) son repertoire parent existe dans l'arbre suivi (le
fichier est un fichier neuf dans un reel repertoire).
_scopes_may_overlap_by_pattern(a, b)-- la comparaison par motif,extraite du repli sans parcours d'arbre de
_scopes_intersect: lesmembres concrets de chaque cote sont matches contre les globs de l'autre
cote (fnmatch dans les deux sens -- la boucle parcourt les deux cotes), et
les paires glob-vs-glob restent decidees par le test prudent de prefixe
litteral (
_glob_overlap), qui sur-bloque quand il ne sait pas._scopes_intersect-- le parcours de l'arbre reste EXACT pour lesscopes qui vivent sur le disque ; quand un des deux cotes est un scope de
creation, l'arbre est aveugle (un fichier qui n'existe pas ne matche
aucun fichier suivi) et la decision se fait donc par motif.
_filter_by_claim_scope-- les deux lifts check_lane_claim : un [CLAIMED] scope ET horodate produit un glob qui ne matche rien — le claim ne bloque personne #10958 sont conditionnes :le scope du caller entierement mort ne retient plus toutes les lanes
quand il est un scope de creation, et le scope d'autrui entierement
mort n'est plus hisse en epic-wide quand il est un scope de creation. Un
scope dont le repertoire n'existe pas reste une faute de frappe et garde
le fail-CLOSED.
Ce qui reste fail-CLOSED (et pourquoi)
Le discriminant (c) est le point ou l'issue laisse le choix a
l'implementation. Il distingue deux scopes que #12862 confondait :
scripts/notebook_tools/check_concession_prose.py(le repertoire existe)scripts/nowhere/typo.py(le repertoire n'existe pas)C'est ce qui laisse tous les tests de fail-safe existants verts, dont
test_run_check_my_dead_scope_keeps_others(le casscripts/nowhere/typo.pycontre
scripts/grain_tag.py: meme forme que le defaut de l'issue, maistypo) et
test_run_check_dead_scope_blocks_other_lane. Une tranche qui creeun repertoire lui-meme (pas seulement un fichier dedans) reste donc
fail-CLOSED : c'est la limite assumee du correctif, et elle est dans le sens
sur-bloquant.
Perimetre
scripts/check_lane_claim.py: +3 helpers, +2 branches dans_scopes_intersectet_filter_by_claim_scope, docstrings a jour.scripts/tests/test_check_lane_claim.py: +6 tests (les 2 controlespositifs demandes par l'issue, le controle symetrique, la non-regression
typo, et 2 tests unitaires des helpers).
Validation
pytest scripts/tests/test_check_lane_claim.py: 344 passed, 1 skipped(les 10 tests de la section check_lane_claim : un scope de création disjoint est rendu BLOCKED #21431 inclus).
test_lane_claim_*,test_emit_dead_scope_warnings,test_pick_claim_filter,test_pick_idle_grain_belt,test_check_unaddressed_nits,test_belt_service_balance) : 1055 passed, 1 skipped, aucuneregression.
python -m py_compilesur les deux fichiers : OK.Controle positif
scripts/notebook_tools/check_concession_prose.pyvs claim disjointcheck_output_collapse*CLEAR(exit 0)blocking_lanes: [], rc=3 (IMPLICIT)scripts/notebook_tools/check_output_collapse_v2.pyvs claimcheck_output_collapse*.pyBLOCKED(exit 1)test_run_check_creation_scope_under_claim_glob_stays_blockedCLEARtest_run_check_creation_scope_clears_a_creation_claimscripts/nowhere/typo.pyvs claim disjointBLOCKED(fail-CLOSED)test_run_check_typo_scope_keeps_blockersAcceptance
le scope de l'autre lane, au lieu d'etre resolu sur l'arbre suivi
CLEARquand aucun motif ne se recouvreBLOCKEDRefs: #21431