[DTP-Worker] Sprint 8 · buffer · Fiches agents/colonne « Job CI » : chaque table de livrables de 03_agents/*/AGENT.md NOMME entre backticks le job Gitea Actions qui exécute la suite du module (rbac-tests · fiscal-ecf-tests · frontend-portails-tests…) — nom SAISI À LA MAIN dont la source de vérité est la clé jobs: de .gitea/workflows/ci.yml — mais AUCUN gate ne liait le NOM écrit dans la fiche au job réel. check_ci_integrity.sh prouve que chaque job est CÂBLÉ dans gate.needs (INV-A/B) et les blocs amont de check_readme_claims recomputent les COMPTES de tests par suite ; RIEN ne vérifiait que le token DÉSIGNE un job existant. Renommer un job dans ci.yml (rbac-tests→rbac-role-tests) ou mal recopier demo-scenario-tests en demo-scenarios-tests laissait la fiche pointer un job FANTÔME pendant que le CI reste VERT (le vrai job tourne sous son nouveau nom) — la doc d'identité de l'agent ment en silence.
Gate ajouté (bloc « Fiches agents · colonne Job CI ») : on RECOMPUTE l'ensemble des jobs depuis la clé `jobs:` de ci.yml (la section `on:` push/pull_request/workflow_dispatch est AVANT `jobs:` et donc exclue ; jamais une liste à la main) et on exige que chaque token `*-tests` cité dans une fiche y figure. Direction fiche→ci.yml (le consommateur) : ci.yml peut définir des jobs non cités (légitime), une fiche ne peut pas citer un job absent ; la disparition totale de la colonne échoue AUSSI. 33 références vérifiées == jobs ci.yml ; 2 morsures (token de fiche `seo-tests`→`seo-suite-tests` fantôme · job ci.yml `rbac-tests` renommé → mord SIMULTANÉMENT les 4 fiches qui le citent : erpnext_backend/ifc_speckle/mobile/rendu), restauré vert, 7 gates re-verts. ci/README.md + activity log MAJ. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+22
-1
File diff suppressed because one or more lines are too long
@@ -3702,6 +3702,77 @@ if b4 is not None:
|
||||
+ (f" · absents={miss}" if miss else "")
|
||||
+ (f" · en trop={extra}" if extra else "") + " → aligner le test")
|
||||
|
||||
# ============================================================================
|
||||
# Fiches agents · colonne « Job CI » — chaque nom de job cité DOIT exister dans
|
||||
# .gitea/workflows/ci.yml (même classe de « vert trompeur », côté câblage CI).
|
||||
# ----------------------------------------------------------------------------
|
||||
# Chaque table de livrables des fiches 03_agents/*/AGENT.md porte une colonne
|
||||
# « Job CI » qui NOMME, mot pour mot et entre backticks, le job Gitea Actions qui
|
||||
# exécute la suite du module (`rbac-tests`, `fiscal-ecf-tests`, …). Ce nom est
|
||||
# SAISI À LA MAIN. La source de vérité est la section `jobs:` de ci.yml. Les
|
||||
# gates existants ne couvraient PAS ce lien : `check_ci_integrity.sh` prouve que
|
||||
# chaque job de ci.yml est bien CÂBLÉ dans `gate.needs` (INV-A/B), et les blocs
|
||||
# amont recomputent les COMPTES de tests par suite — mais RIEN ne vérifiait que
|
||||
# le nom écrit dans la fiche DÉSIGNE un job réel. Renommer un job dans ci.yml
|
||||
# (ex. `rbac-tests` → `rbac-role-tests`, ou `demo-scenario-tests` mal recopié en
|
||||
# `demo-scenarios-tests`) laisse la fiche pointer un job FANTÔME : le CI reste
|
||||
# vert (le vrai job tourne sous son nouveau nom), la doc d'identité de l'agent
|
||||
# ment en silence. On RECOMPUTE ici l'ensemble des jobs depuis la section `jobs:`
|
||||
# de ci.yml (jamais une liste à la main) et on exige que chaque token `*-tests`
|
||||
# cité dans une fiche y figure. Direction fiche→ci.yml (le consommateur) : ci.yml
|
||||
# peut définir des jobs non cités (légitime), mais une fiche ne peut pas citer un
|
||||
# job absent.
|
||||
# ============================================================================
|
||||
CI_YML = ".gitea/workflows/ci.yml"
|
||||
try:
|
||||
ci_lines = open(CI_YML, encoding="utf-8").read().splitlines()
|
||||
except OSError as e:
|
||||
bad(f"Job CI fiches · {CI_YML} illisible : {e}")
|
||||
ci_lines = None
|
||||
|
||||
if ci_lines is not None:
|
||||
# Ensemble des jobs = clés à 2 espaces sous la clé top-level `jobs:`
|
||||
# (la section `on:` — push/pull_request/workflow_dispatch — est AVANT `jobs:`
|
||||
# et donc naturellement exclue). Une clé top-level `^\S` clôt la section.
|
||||
jobs = set()
|
||||
in_jobs = False
|
||||
for line in ci_lines:
|
||||
if re.match(r"^jobs:\s*$", line):
|
||||
in_jobs = True
|
||||
continue
|
||||
if in_jobs and re.match(r"^\S", line): # nouvelle clé top-level
|
||||
break
|
||||
if in_jobs:
|
||||
mj = re.match(r"^ ([A-Za-z0-9_-]+):\s*$", line)
|
||||
if mj:
|
||||
jobs.add(mj.group(1))
|
||||
if not jobs:
|
||||
bad(f"Job CI fiches · aucun job détecté dans la section `jobs:` de "
|
||||
f"{CI_YML} — parse cassé ou fichier vidé (régression de câblage).")
|
||||
|
||||
fiches_ci = subprocess.run(
|
||||
["git", "ls-files", "03_agents/*/AGENT.md"],
|
||||
capture_output=True, text=True).stdout.split()
|
||||
tok_re = re.compile(r"`([a-z0-9]+(?:-[a-z0-9]+)*-tests)`")
|
||||
seen_jobrefs = 0
|
||||
for af in fiches_ci:
|
||||
try:
|
||||
flines = open(af, encoding="utf-8").read().splitlines()
|
||||
except OSError as e:
|
||||
bad(f"Job CI fiches · {af} illisible : {e}"); continue
|
||||
for ln, line in enumerate(flines, 1):
|
||||
for tok in tok_re.findall(line):
|
||||
seen_jobrefs += 1
|
||||
if jobs and tok in jobs:
|
||||
good(f"Job CI · {af}:{ln} · `{tok}` == job ci.yml")
|
||||
elif jobs:
|
||||
bad(f"Job CI · {af}:{ln} · `{tok}` ABSENT de la section "
|
||||
f"`jobs:` de {CI_YML} → job renommé/supprimé, fiche "
|
||||
f"pointe un job fantôme (régénérer la fiche ou ci.yml)")
|
||||
if seen_jobrefs == 0:
|
||||
bad("Job CI fiches · AUCUN token `*-tests` trouvé dans les fiches — la "
|
||||
"disparition de la colonne « Job CI » est elle-même une régression.")
|
||||
|
||||
sys.exit(1 if FAIL else 0)
|
||||
PY
|
||||
rc=$?
|
||||
|
||||
Reference in New Issue
Block a user