[DTP-Worker] Sprint 8 · buffer L75 · Traçabilité non vérifiée : les n° de ligne roadmap_line de la matrice d'acceptation pouvaient pointer à côté en silence (roadmap éditée) → INV11 ancrage roadmap (dérive 8+7 du fichier · #6) + parse_roadmap_anchors + 6 tests ; régénéré 558→564

INV10 ne garantissait que « roadmap_line est un entier positif ». Ajout de
parse_roadmap_anchors (deps) qui DÉRIVE la structure réelle de la roadmap, et
d'INV11 qui exige que chaque roadmap_line pointe RÉELLEMENT son bullet
(DELIVERABLE du sprint SX · k-ième bullet métrique) et que le « 8 + 7 » soit
dérivé du fichier, pas figé. Morsure prouvée sur le spec réel (S1=999, M3=200).
Régénéré consommateurs : regression 558→564 (run/plan/MANIFEST), quality_report
(acceptance 31→37 méthodes, 100/100 inchangé), fiches QA + Backend, README
acceptance (10→11 invariants). 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-07-31 06:12:15 +00:00
parent 11f1250b29
commit 25541c365d
11 changed files with 273 additions and 22 deletions
@@ -21,7 +21,9 @@ module tracé non gated → génération refusée.
Anti-invention (#6) : les nombres des énoncés (<1h, 95/100, 7 dashboards, 2
stores) sont des CITATIONS verbatim de la roadmap ; la matrice ne PRÉTEND aucune
mesure de performance ; tout hors-périmètre porte une source.
mesure de performance ; tout hors-périmètre porte une source. Le n° de ligne
(`roadmap_line`) de chaque promesse est VÉRIFIÉ (INV11) contre la structure réelle
de la roadmap — il ne peut pas pointer à côté en silence si la roadmap est éditée.
Produit :
- acceptance_matrix.json → la matrice (une ligne par promesse : preuves
@@ -218,6 +220,51 @@ def validate_bundle(bundle: dict, spec: dict, ci: dict, audit: dict) -> list[str
if any(not isinstance(x, int) or x < 1 for x in lines):
errors.append(f"roadmap_line: valeur non positive {lines}.")
# INV11 · ANCRAGE ROADMAP (traçabilité VÉRIFIÉE, pas déclarée · #6). INV10 ne
# garantit que « roadmap_line est un entier » : un n° de ligne qui pointe à
# côté (roadmap éditée, lignes décalées) reste « vert » alors qu'il cite un
# bullet qui n'existe plus. On DÉRIVE la structure réelle de la roadmap et on
# exige que chaque roadmap_line pointe RÉELLEMENT le bullet qu'il prétend :
# · nombre de bullets DELIVERABLE == nombre de livrables de sprint du spec,
# et nombre de bullets métrique == nombre de métriques (le « 8 + 7 » n'est
# plus un compte figé mais dérivé du fichier roadmap) ;
# · chaque livrable SX pointe le bullet **DELIVERABLE** du sprint SX ;
# · chaque métrique (dans l'ordre) pointe le k-ième bullet « - ✅ ».
try:
anchors = deps.parse_roadmap_anchors(spec["roadmap_ref"])
except OSError as e:
errors.append(f"roadmap: {spec.get('roadmap_ref')!r} illisible ({e}).")
else:
dmap = anchors["deliverables"]
specd = spec.get("sprint_deliverables", [])
specm = spec.get("mvp_metrics", [])
if len(dmap) != len(specd):
errors.append(
f"roadmap: {len(dmap)} bullet(s) DELIVERABLE dans la roadmap ≠ "
f"{len(specd)} livrable(s) de sprint du spec (promesse ajoutée/retirée non tracée).")
if len(anchors["metrics"]) != len(specm):
errors.append(
f"roadmap: {len(anchors['metrics'])} bullet(s) métrique dans la roadmap ≠ "
f"{len(specm)} métrique(s) du spec (promesse ajoutée/retirée non tracée).")
for d in specd:
want = dmap.get(d["sprint"])
if want is None:
errors.append(
f"roadmap: {d['id']}: aucun bullet DELIVERABLE pour le sprint {d['sprint']}.")
elif d["roadmap_line"] != want:
errors.append(
f"roadmap: {d['id']}: roadmap_line={d['roadmap_line']} ne pointe pas "
f"le DELIVERABLE de {d['sprint']} (ligne {want}).")
for k, d in enumerate(specm):
want = anchors["metrics"][k] if k < len(anchors["metrics"]) else None
if want is None:
errors.append(
f"roadmap: {d['id']}: aucun {k + 1}e bullet métrique dans la roadmap.")
elif d["roadmap_line"] != want:
errors.append(
f"roadmap: {d['id']}: roadmap_line={d['roadmap_line']}{k + 1}e bullet "
f"métrique (ligne {want}).")
return errors