[DTP-Worker 20260802_043512] Sprint 8 · buffer · Faisabilité : ancrage du DOMAINE DES CODES PROJET (garde _validate_brief + 2 schémas JSON) sur CLAUDE.md §Projets
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,60 @@
|
|||||||
# Activity log · 2026-08-02 · Claude Code DTP Worker
|
# Activity log · 2026-08-02 · Claude Code DTP Worker
|
||||||
|
|
||||||
|
## Sprint 8 · buffer · Faisabilité — ancrage du DOMAINE DES CODES PROJET (garde d'entrée `_validate_brief` + 2 schémas JSON) sur CLAUDE.md §Projets
|
||||||
|
|
||||||
|
**Contexte** — Série anti-dérive Sprint 8 (CLAUDE.md #6 « zéro invention de
|
||||||
|
chiffres »). La classe « constante constitutionnelle recopiée dans une GARDE DE
|
||||||
|
VALIDATION en code, jamais ancrée » (déjà couverte pour le CONTRAT DE FORMAT
|
||||||
|
e-NCF de `ncf.py`) portait un candidat resté NON gaté et paradoxalement au **cœur
|
||||||
|
du Livrable Sprint 3** (« P07 Aqua Terra <1h ») : le **DOMAINE des codes projet
|
||||||
|
valides** du générateur 4 volets. Ce domaine EST exactement l'ensemble
|
||||||
|
`CLAUDE.md §Projets` (P01..P09), recopié À LA MAIN à **QUATRE surfaces**, aucune
|
||||||
|
ancrée, toutes invisibles à `check_artifacts` (ni `.py` ni schémas ne sont des
|
||||||
|
`out/*.json`) :
|
||||||
|
1. `faisabilite/generator/faisabilite_gen.py::_validate_brief` — la GARDE runtime
|
||||||
|
`code[:2] == "P0" and code[2] in "123456789"` (le domaine EFFECTIF) + le message
|
||||||
|
`« … invalide (attendu P01..P09) »` (les bornes) ;
|
||||||
|
2. `faisabilite/version.schema.json` — `properties.projet.pattern "^P0[1-9]$"`
|
||||||
|
(validation du `version.json` par projet) + sa `description` ;
|
||||||
|
3. `faisabilite/generator/brief.schema.json` — même `pattern "^P0[1-9]$"`
|
||||||
|
(validation du brief d'entrée) + sa `description` (qui n'en avait **aucune**).
|
||||||
|
|
||||||
|
**Le « vert trompeur »** — si CLAUDE.md §Projets AJOUTE un 10ᵉ projet (`P10 …`,
|
||||||
|
évènement de portefeuille réel), la garde ET les deux patterns REJETTENT `P10`
|
||||||
|
comme « invalide (attendu P01..P09) » : le générateur **REFUSE de scaffolder un
|
||||||
|
projet LÉGITIMEMENT mandaté** (bug d'exécution) pendant que la suite `tests/`
|
||||||
|
reste VERTE (oracles P01/P02 self-consistants, jamais comparés à §Projets).
|
||||||
|
Symétriquement, RETIRER un projet laisse la garde trop PERMISSIVE (accepte un code
|
||||||
|
démobilisé). Ni `check_artifacts` (schémas/`.py` ≠ `out/*.json`) ni `tests/`
|
||||||
|
(teste la FONCTION `_validate_brief`, pas son ANCRAGE à §Projets) ne mordent.
|
||||||
|
|
||||||
|
**Gate ajouté** (`ci/check_readme_claims.sh`, bloc « Faisabilité/generator ·
|
||||||
|
DOMAINE DES CODES PROJET », juste après « Fiches #10 ») — RE-DÉRIVE le catalogue
|
||||||
|
de codes depuis `CLAUDE.md §Projets` (`\bP\d{2,}\b` sur la ligne `## Projets` ·
|
||||||
|
source unique) puis exige que chaque surface accepte **EXACTEMENT** cet ensemble :
|
||||||
|
(a) la garde `_validate_brief` — reconstruit l'ensemble ACCEPTÉ `{préfixe+chiffre}`
|
||||||
|
depuis le code source et set-diff == §Projets ; le message cite les bornes
|
||||||
|
min..max ; (b)/(c) les 2 patterns JSON — **test COMPORTEMENTAL** (compile le
|
||||||
|
pattern, l'applique à une sonde `§Projets ∪ {P00,P10,P99,P0,PXX,P1,P010,Q01}`,
|
||||||
|
exige accepté == §Projets · robuste à la forme de la classe de caractères) + la
|
||||||
|
`description` DÉCLARE l'ancrage « CLAUDE.md §Projets » et cite les bornes. Une
|
||||||
|
surface INTROUVABLE échoue AUSSI (ancre morte · anti-évaporation #6). Deux
|
||||||
|
descriptions de schéma enrichies pour porter l'ancrage (brief.schema en gagne une).
|
||||||
|
|
||||||
|
**Vérif** — 6 checks verts sur l'arbre propre. **6 morsures** adversariales :
|
||||||
|
M1 garde retire le chiffre `7` (validateur trop restrictif) ⇒ set ≠ · M2 message
|
||||||
|
`P01..P09→P01..P08` ⇒ bornes ≠ · M3 pattern version élargi `^P0[0-9]$` ⇒ accepte
|
||||||
|
`P00` en trop · M4 brief.schema perd l'ancrage §Projets ⇒ ne DÉCLARE plus · **M5
|
||||||
|
SILENT-GREEN : CLAUDE.md §Projets ajoute `P10` SEUL (les 4 surfaces inchangées) ⇒
|
||||||
|
CASCADE sur les 6 lignes (garde accepte≠, 2 patterns manquent P10, message +2
|
||||||
|
descriptions bornes P09≠P10) — preuve que l'autorité est §Projets, pas la
|
||||||
|
cohérence interne** · M6 version.schema perd les bornes de sa description. Les
|
||||||
|
**16 tests unittest** du générateur restent verts (garde inchangée). Gates
|
||||||
|
re-verts : `check_artifacts` · `check_docs` · `guard_constraints` ·
|
||||||
|
`check_ci_integrity` · `check_regression` · `check_readme_claims` tous exit 0.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Sprint 8 · buffer · RBAC SPEC §3 — ancrage de la colonne « Familles rattachées » PAR portail sur le contrat
|
## Sprint 8 · buffer · RBAC SPEC §3 — ancrage de la colonne « Familles rattachées » PAR portail sur le contrat
|
||||||
|
|
||||||
**Contexte** — Série anti-dérive Sprint 8 (CLAUDE.md #6 « zéro invention de
|
**Contexte** — Série anti-dérive Sprint 8 (CLAUDE.md #6 « zéro invention de
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
"required": ["projet"],
|
"required": ["projet"],
|
||||||
"additionalProperties": true,
|
"additionalProperties": true,
|
||||||
"properties": {
|
"properties": {
|
||||||
"projet": { "type": "string", "pattern": "^P0[1-9]$" },
|
"projet": { "type": "string", "pattern": "^P0[1-9]$", "description": "Code projet canonique (P01..P09) · domaine ancré sur CLAUDE.md §Projets." },
|
||||||
"nom": { "type": "string" },
|
"nom": { "type": "string" },
|
||||||
"synthetique": { "type": "boolean", "description": "true = fixture de test, jamais publiable." },
|
"synthetique": { "type": "boolean", "description": "true = fixture de test, jamais publiable." },
|
||||||
"sources": {
|
"sources": {
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
"projet": {
|
"projet": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"pattern": "^P0[1-9]$",
|
"pattern": "^P0[1-9]$",
|
||||||
"description": "Code projet canonique (P01..P09)."
|
"description": "Code projet canonique (P01..P09) · domaine ancré sur CLAUDE.md §Projets."
|
||||||
},
|
},
|
||||||
"template_version": {
|
"template_version": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
|
|||||||
@@ -5343,6 +5343,120 @@ if exp is not None:
|
|||||||
(f" · (pas {exp_excl})" if cites_excl and exp_excl else "")
|
(f" · (pas {exp_excl})" if cites_excl and exp_excl else "")
|
||||||
good(f"Fiches #10 · {label} — cite {_cited} == CLAUDE.md #10")
|
good(f"Fiches #10 · {label} — cite {_cited} == CLAUDE.md #10")
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Faisabilité/generator · DOMAINE DES CODES PROJET (P01..P09) ancré sur §Projets
|
||||||
|
# — même classe d'ANCRAGE que le CONTRAT DE FORMAT e-NCF (`ncf.py`) : une GARDE
|
||||||
|
# DE VALIDATION en CODE qui encode un domaine constitutionnel SANS l'ancrer.
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
# Le générateur 4 volets (Livrable Sprint 3 · « P07 Aqua Terra <1h ») REFUSE tout
|
||||||
|
# brief dont le `projet` n'est pas un code canonique. Le DOMAINE des codes valides
|
||||||
|
# EST exactement l'ensemble des projets de CLAUDE.md §Projets (P01..P09). Il est
|
||||||
|
# recopié À LA MAIN à QUATRE surfaces, AUCUNE ancrée sur §Projets, toutes
|
||||||
|
# invisibles à `check_artifacts` (aucune n'est un `out/*.json`) :
|
||||||
|
# (a) `faisabilite/generator/faisabilite_gen.py::_validate_brief` — la GARDE
|
||||||
|
# runtime `code[:2] == "P0" and code[2] in "123456789"` (le domaine effectif)
|
||||||
|
# + le message `attendu P01..P09` (les bornes) ;
|
||||||
|
# (b) `faisabilite/version.schema.json` — `properties.projet.pattern "^P0[1-9]$"`
|
||||||
|
# (validation JSON du `version.json` par projet) + sa `description` ;
|
||||||
|
# (c) `faisabilite/generator/brief.schema.json` — même `pattern "^P0[1-9]$"`
|
||||||
|
# (validation JSON du brief d'entrée) + sa `description`.
|
||||||
|
# « Vert trompeur » : si CLAUDE.md §Projets AJOUTE un 10ᵉ projet (`P10 …`, évènement
|
||||||
|
# de portefeuille réel), la GARDE et les DEUX patterns REJETTENT `P10` comme
|
||||||
|
# « invalide (attendu P01..P09) » → le générateur REFUSE de scaffolder un projet
|
||||||
|
# LÉGITIMEMENT mandaté (bug d'exécution) pendant que la suite `tests/` reste VERTE
|
||||||
|
# (ses oracles P01/P02 sont self-consistants, jamais comparés à §Projets). Ni
|
||||||
|
# `check_artifacts` (schémas/`.py` ne sont pas des `out/*.json`) ni `tests/` (teste
|
||||||
|
# la FONCTION `_validate_brief`, pas son ANCRAGE) ne mordent. Symétriquement,
|
||||||
|
# RETIRER un projet de §Projets laisse la garde trop PERMISSIVE (accepte un code
|
||||||
|
# démobilisé). On RE-DÉRIVE le domaine de §Projets et on exige que chaque surface
|
||||||
|
# accepte EXACTEMENT cet ensemble (test COMPORTEMENTAL du pattern, robuste à la
|
||||||
|
# forme de la classe de caractères), + la déclaration de l'ancrage « §Projets ».
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
try:
|
||||||
|
_fcm = open("CLAUDE.md", encoding="utf-8").read()
|
||||||
|
_fmp = re.search(r"^## Projets\s*\n(.+)$", _fcm, re.M)
|
||||||
|
if _fmp is None:
|
||||||
|
raise ValueError("section « ## Projets » INTROUVABLE")
|
||||||
|
fpj_codes = sorted(set(re.findall(r"\bP\d{2,}\b", _fmp.group(1))))
|
||||||
|
if not fpj_codes:
|
||||||
|
raise ValueError("aucun code projet PNN parsé")
|
||||||
|
except (OSError, ValueError) as e:
|
||||||
|
bad(f"Faisabilité domaine · CLAUDE.md §Projets illisible/inattendu : {e}")
|
||||||
|
fpj_codes = None
|
||||||
|
|
||||||
|
if fpj_codes is not None:
|
||||||
|
fpj_set = set(fpj_codes); fpj_mn, fpj_mx = fpj_codes[0], fpj_codes[-1]
|
||||||
|
# (a) la garde runtime _validate_brief : préfixe + ensemble de chiffres + message
|
||||||
|
FG = f"{D}/faisabilite/generator/faisabilite_gen.py"
|
||||||
|
try:
|
||||||
|
_fg = open(FG, encoding="utf-8").read()
|
||||||
|
except OSError as e:
|
||||||
|
_fg = None; bad(f"Faisabilité domaine · {FG} illisible : {e}")
|
||||||
|
if _fg is not None:
|
||||||
|
_mpre = re.search(r'code\[:2\]\s*==\s*"([^"]+)"', _fg)
|
||||||
|
_mdig = re.search(r'code\[2\]\s*in\s*"([^"]+)"', _fg)
|
||||||
|
if not (_mpre and _mdig):
|
||||||
|
bad("Faisabilité domaine · _validate_brief : préfixe/ensemble-chiffres du "
|
||||||
|
"code projet INTROUVABLE (ancre morte) → la garde a changé de forme")
|
||||||
|
else:
|
||||||
|
_accept = {f"{_mpre.group(1)}{d}" for d in _mdig.group(1)}
|
||||||
|
_miss = sorted(fpj_set - _accept); _extra = sorted(_accept - fpj_set)
|
||||||
|
if _miss or _extra:
|
||||||
|
bad(f"Faisabilité domaine · _validate_brief accepte {sorted(_accept)} ≠ "
|
||||||
|
f"CLAUDE.md §Projets{' · manquants=' + str(_miss) if _miss else ''}"
|
||||||
|
f"{' · en trop=' + str(_extra) if _extra else ''} → aligner la garde "
|
||||||
|
f"(ou CLAUDE.md §Projets)")
|
||||||
|
else:
|
||||||
|
good(f"Faisabilité domaine · _validate_brief accepte exactement les "
|
||||||
|
f"{len(_accept)} codes {fpj_mn}..{fpj_mx} de CLAUDE.md §Projets")
|
||||||
|
_mmsg = re.search(r'brief\.projet invalide \(attendu ([^)]+)\)', _fg)
|
||||||
|
if not _mmsg:
|
||||||
|
bad("Faisabilité domaine · message « brief.projet invalide (attendu …) » "
|
||||||
|
"INTROUVABLE (ancre morte)")
|
||||||
|
elif fpj_mn not in _mmsg.group(1) or fpj_mx not in _mmsg.group(1):
|
||||||
|
bad(f"Faisabilité domaine · message « attendu {_mmsg.group(1)} » ne cite pas "
|
||||||
|
f"les bornes {fpj_mn}..{fpj_mx} de CLAUDE.md §Projets")
|
||||||
|
else:
|
||||||
|
good(f"Faisabilité domaine · message d'erreur cite les bornes "
|
||||||
|
f"{fpj_mn}..{fpj_mx} == CLAUDE.md §Projets")
|
||||||
|
# (b)+(c) les 2 schémas JSON : le pattern accepte EXACTEMENT §Projets (comportemental)
|
||||||
|
_probe = fpj_set | {"P00", "P10", "P99", "P0", "PXX", "P1", "P010", "Q01"}
|
||||||
|
for _rel in ("version.schema.json", "generator/brief.schema.json"):
|
||||||
|
SP = f"{D}/faisabilite/{_rel}"
|
||||||
|
try:
|
||||||
|
_sj = json.load(open(SP, encoding="utf-8"))
|
||||||
|
except (OSError, ValueError) as e:
|
||||||
|
bad(f"Faisabilité domaine · {_rel} illisible/inattendu : {e}"); continue
|
||||||
|
_prop = (_sj.get("properties") or {}).get("projet") or {}
|
||||||
|
_pat = _prop.get("pattern")
|
||||||
|
if not _pat:
|
||||||
|
bad(f"Faisabilité domaine · {_rel} : properties.projet.pattern INTROUVABLE")
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
_rx = re.compile(_pat)
|
||||||
|
except re.error as e:
|
||||||
|
bad(f"Faisabilité domaine · {_rel} pattern {_pat!r} illégal : {e}"); _rx = None
|
||||||
|
if _rx is not None:
|
||||||
|
_acc = {c for c in _probe if _rx.fullmatch(c)}
|
||||||
|
_miss = sorted(fpj_set - _acc); _extra = sorted(_acc - fpj_set)
|
||||||
|
if _miss or _extra:
|
||||||
|
bad(f"Faisabilité domaine · {_rel} pattern {_pat!r} accepte {sorted(_acc)}"
|
||||||
|
f" ≠ CLAUDE.md §Projets{' · manquants=' + str(_miss) if _miss else ''}"
|
||||||
|
f"{' · en trop=' + str(_extra) if _extra else ''}")
|
||||||
|
else:
|
||||||
|
good(f"Faisabilité domaine · {_rel} pattern accepte exactement les "
|
||||||
|
f"{len(fpj_set)} codes de §Projets")
|
||||||
|
_desc = _prop.get("description", "") or ""
|
||||||
|
if not re.search(r"CLAUDE\.md\s*§?\s*Projets", _desc):
|
||||||
|
bad(f"Faisabilité domaine · {_rel} : description projet ne DÉCLARE pas "
|
||||||
|
f"l'ancrage « CLAUDE.md §Projets » : {_desc!r}")
|
||||||
|
elif fpj_mn not in _desc or fpj_mx not in _desc:
|
||||||
|
bad(f"Faisabilité domaine · {_rel} : description ne cite pas les bornes "
|
||||||
|
f"{fpj_mn}..{fpj_mx} : {_desc!r}")
|
||||||
|
else:
|
||||||
|
good(f"Faisabilité domaine · {_rel} description ancre {fpj_mn}..{fpj_mx} "
|
||||||
|
f"sur CLAUDE.md §Projets")
|
||||||
|
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
# TEMPLATE FAISABILITÉ CANONIQUE v1.0 · PARAMÈTRES #9/#10 — même classe d'ANCRAGE
|
# TEMPLATE FAISABILITÉ CANONIQUE v1.0 · PARAMÈTRES #9/#10 — même classe d'ANCRAGE
|
||||||
# que « paramètres canoniques » (generator) et « Fiches #10 » ci-dessus, sur une
|
# que « paramètres canoniques » (generator) et « Fiches #10 » ci-dessus, sur une
|
||||||
|
|||||||
Reference in New Issue
Block a user