[DTP-Worker 20260802_090530] Sprint 8 · buffer · Angle mort gate « comptes tests par suite » : la cellule rbac (fiche pointe une SPEC, pas un README) était non vérifiée

row_re du gate check_readme_claims §comptes-par-suite exigeait un lien …/README.md ;
la fiche erpnext_backend pointe rbac/ via RBAC_50_ROLES_SPEC.md (son point d'entrée
contrat), donc sa cellule « Tests | 10 | » échappait au recompute depuis
regression_plan.json. Mutation-test 10→99 : gate restait VERT (vrai angle mort,
classe « vert trompeur »). Extension : accepte tout *.md d'entrée du dossier module
(/[\w.-]+.md) ; le garde path-not-in-auth protège l'élargissement. rbac désormais
vue+mordue ; 7 gates verts.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Claude Code DTP Worker
2026-08-02 09:10:39 +00:00
parent 448c310398
commit ca15e7e12b
2 changed files with 46 additions and 4 deletions
+38
View File
@@ -941,3 +941,41 @@ marqueur (`proscrites`/`interdits`/`bannie`) donc `guard_constraints` les laisse
passer. **Vérif** : 7 gates re-verts (guard_constraints · check_docs · passer. **Vérif** : 7 gates re-verts (guard_constraints · check_docs ·
check_readme_claims · check_ci_integrity · check_artifacts · check_regression · check_readme_claims · check_ci_integrity · check_artifacts · check_regression ·
validate_json tous exit 0). validate_json tous exit 0).
---
## Sprint 8 · buffer · Angle mort du gate « comptes de tests par suite » : la cellule `rbac` (fiche pointant une SPEC, pas un README)
**Démarche — vérifier AVANT de conclure « couvert » (mémoire `verify-uncovered-before-gating`).**
La série récente ayant épuisé les surfaces driftables évidentes, j'ai audité la
**complétude d'un gate existant** plutôt que d'en empiler un neuf (CLAUDE.md #5).
**Constat.** Le gate `check_readme_claims.sh` §« Fiches agents · comptes de tests
PAR SUITE (colonne « Tests ») » recompute chaque cellule `| N |` des tables de
livrables des `03_agents/*/AGENT.md` depuis l'autorité `regression_plan.json`
(`plan.suites[path].test_methods`). Son `row_re` exigeait pourtant que le lien de
la ligne pointe `…/README.md`. **Or une ligne y échappe** : la fiche
`erpnext_backend` (l.28) pointe le module `rbac/` via son **point d'entrée
`RBAC_50_ROLES_SPEC.md`** (le contrat 50-rôles, pas un README) — donc son
`row_re` ne mordait pas, et sa cellule « Tests | 10 | » restait **non vérifiée**.
**Mutation-test (preuve du trou).** Cellule `rbac` `10 → 99` sur la fiche →
`check_readme_claims` restait **VERT** (exit 0), aucune ligne `rbac` évaluée. C'est
exactement la classe « vert trompeur » que cette section du gate existe pour tuer
(un régénérateur de fiche pourrait y écrire un faux compte sans que la CI bronche).
La somme prose « RBAC 60 tests (10+11+12+11+16) » est **order-indépendante** → ne
contraint pas la cellule-table `rbac` individuelle. Vrai angle mort.
**Correctif (extension du gate, pas gate N+1).** `row_re` accepte désormais tout
`*.md` d'entrée du dossier module (`/[\w.-]+\.md` au lieu de `/README\.md`), pas
seulement README — le garde `path not in auth` (déjà présent) protège de tout
élargissement fortuit : seules les lignes pointant une **suite gatée connue** sont
évaluées, les autres restent ignorées. Une seule ligne entre ainsi dans le
périmètre : `rbac`. Commentaire mis à jour pour documenter le cas SPEC.
**Vérif.** (1) Arbre propre VERT + la ligne `rbac` est désormais **VUE**
(`AGENT.md:28 · rbac — Tests 10 == source (10)`). (2) Mutation `10→99` désormais
**MORDUE** (`Tests 99 MAIS source dit 10 → régénérer la fiche`, exit 1). (3)
Restauration → exit 0. **7 gates re-verts** : check_ci_integrity · check_artifacts ·
check_docs · check_readme_claims · check_regression · guard_constraints ·
validate_json tous exit 0.
+8 -4
View File
@@ -631,11 +631,15 @@ self_mod = plan["coverage"].get("self_module_excluded")
if count_tests is not None and self_mod: if count_tests is not None and self_mod:
auth[self_mod] = count_tests(f"{D}/{self_mod}/tests")[1] auth[self_mod] = count_tests(f"{D}/{self_mod}/tests")[1]
# Ligne de table Tests : lien vers 05_deliverables_mvp/<path>/README.md + dernier # Ligne de table Tests : lien vers 05_deliverables_mvp/<path>/<doc>.md + dernier
# champ numérique. On ne vérifie QUE les lignes pointant une suite gatée connue # champ numérique. Le <doc>.md est l'ENTRÉE du module — le plus souvent README.md,
# (auth) — toute autre ligne de table est ignorée (pas notre périmètre). # mais parfois une SPEC (la fiche `rbac` pointe RBAC_50_ROLES_SPEC.md, son point
# d'entrée) : on accepte donc tout `*.md` du dossier module, sinon cette cellule-là
# reste un angle mort (mutation 10→99 non mordue). On ne vérifie QUE les lignes
# pointant une suite gatée connue (auth) — toute autre ligne est ignorée (hors
# périmètre), le garde `path not in auth` protège de tout élargissement fortuit.
row_re = re.compile( row_re = re.compile(
r"^\|\s*\[`\w+/`\]\([^)]*?05_deliverables_mvp/([\w/]+?)/README\.md\)" r"^\|\s*\[`\w+/`\]\([^)]*?05_deliverables_mvp/([\w/]+?)/[\w.-]+\.md\)"
r".*\|\s*(\d+)\s*\|\s*$") r".*\|\s*(\d+)\s*\|\s*$")
fiches = subprocess.run(["git", "ls-files", "03_agents/*/AGENT.md"], fiches = subprocess.run(["git", "ls-files", "03_agents/*/AGENT.md"],
capture_output=True, text=True).stdout.split() capture_output=True, text=True).stdout.split()