Compare commits
4 Commits
9d30939db9
...
1a44dfe1c0
| Author | SHA1 | Date | |
|---|---|---|---|
| 1a44dfe1c0 | |||
| 1441693e25 | |||
| db91a6bb5b | |||
| 9fa464dc84 |
@@ -9,6 +9,21 @@
|
|||||||
# ============================================================================
|
# ============================================================================
|
||||||
name: CI
|
name: CI
|
||||||
|
|
||||||
|
# Mode UTF-8 forcé pour TOUS les jobs (builds + suites + gates) — indépendance
|
||||||
|
# LOCALE du runner. Chaque générateur clôt son `build` par un print(✅ / accents
|
||||||
|
# français) vers stdout, dont l'encodage suit la locale. Sur un runner à locale
|
||||||
|
# ASCII (image minimale : LC_ALL=C sans coercition PEP 538, PYTHONUTF8 non posé),
|
||||||
|
# ce print lève UnicodeEncodeError APRÈS écriture des fichiers → `check-artifacts`
|
||||||
|
# voit un exit non-nul (« build a échoué ») et les 25 suites plantent : tout le
|
||||||
|
# gate passe ROUGE en silence le jour où l'image runner change, alors qu'il est
|
||||||
|
# VERT sur le poste dev (UTF-8) — classe « le runner n'est pas ton poste ».
|
||||||
|
# PYTHONUTF8=1 (PEP 540) impose stdout/stdin/défaut-fichier en UTF-8 partout,
|
||||||
|
# indépendamment de LANG/LC_ALL → tous les jobs deviennent locale-robustes.
|
||||||
|
# Verrouillé par ci/check_ci_integrity.sh (INV-C) pour ne pas disparaître en
|
||||||
|
# silence. Miroir local : run_ci.sh exporte la même variable.
|
||||||
|
env:
|
||||||
|
PYTHONUTF8: "1"
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [main]
|
branches: [main]
|
||||||
|
|||||||
@@ -1,5 +1,90 @@
|
|||||||
# Activity Log · 2026-08-05 · Claude Code DTP Worker
|
# Activity Log · 2026-08-05 · Claude Code DTP Worker
|
||||||
|
|
||||||
|
## Session 181325 · FIX RÉEL défaut locale-runner : le gate est ROUGE en silence sous locale ASCII → **VERROUILLÉ UTF-8**
|
||||||
|
|
||||||
|
**Choix de tâche.** `./run_ci.sh` au démarrage = **33 PASS**, arbre propre, roadmap
|
||||||
|
`ROADMAP_8_WEEKS_OR_LESS.md` **intégralement livrée/gatée in-repo** : les 15 promesses de
|
||||||
|
l'`acceptance_matrix` sont **toutes `status=in_repo`**, chaque reste (BIM runtime, builds EAS,
|
||||||
|
dépôts ONAPI, déploiement prod) explicitement `out_of_scope` VPS (#8). Un fan-out Explore
|
||||||
|
docstring-vs-code / prose-vs-numérique sur les livrables → **0 dérive** (repo propre). J'ai donc
|
||||||
|
attaqué un **axe de robustesse absent de la mémoire** et, contrairement aux 3 sweeps déterministes
|
||||||
|
précédents (hash-seed / locale-TZ / forward-compat, tous **CLEAN → attestation**), celui-ci a
|
||||||
|
révélé un **défaut RÉEL présent**.
|
||||||
|
|
||||||
|
**La faille — la coercition PEP 538 masquait le vrai cas.** Le sweep locale-TZ (`154314`) a rejoué
|
||||||
|
les builds sous `LANG=C`, mais Python **coerce silencieusement `C` → `C.UTF-8`** (PEP 538) : sous
|
||||||
|
`LANG=C` seul, `getpreferredencoding()` = **UTF-8** → le mode d'échec ASCII n'a **jamais été
|
||||||
|
exercé**. En neutralisant la coercition (`PYTHONUTF8=0 PYTHONCOERCECLOCALE=0 LC_ALL=C`),
|
||||||
|
l'encodage par défaut redevient **`ANSI_X3.4-1968` (ASCII)** — la config d'un **runner minimal**.
|
||||||
|
|
||||||
|
**Constat — 20 des 21 générateurs `build` PLANTENT.** Réplique de la boucle `check_artifacts` sous
|
||||||
|
locale ASCII neutralisée : **20/21 builds crashent** (`UnicodeEncodeError`), les 25 suites
|
||||||
|
échouent aussi. Cause : chaque générateur clôt `cmd_build` par un `print("✅ Bundle … généré" /
|
||||||
|
accents)` vers **stdout**, dont l'encodage suit la locale. Les fichiers sont écrits sans souci
|
||||||
|
(`encoding='utf-8'` explicite partout : 37 writes annotés, 0 write nu), mais le **print cosmétique
|
||||||
|
final** lève `UnicodeEncodeError` **après** écriture → exit non-nul → `check-artifacts` voit
|
||||||
|
« build a échoué » **ROUGE**. Vert aujourd'hui (runner UTF-8), **rouge en silence** le jour où
|
||||||
|
l'image runner change = classe « le runner n'est pas ton poste », mais **défaut, pas attestation**.
|
||||||
|
|
||||||
|
**Correctif — single-source à la frontière d'invocation, 0 fichier de production.** `PYTHONUTF8=1`
|
||||||
|
(PEP 540) impose stdout/stdin/défaut-fichier en UTF-8 **quelle que soit la locale**. Posé **une
|
||||||
|
fois** en `env:` **au niveau workflow** de `ci.yml` → s'applique à **tous les jobs** (21 builds +
|
||||||
|
25 suites + gates statiques). Miroir local : `run_ci.sh` `export PYTHONUTF8=1`. **Aucun des 20
|
||||||
|
modules touché** (anti-churn #5 — la fragilité est un contrat d'environnement d'invocation, pas 20
|
||||||
|
bugs à patcher un par un ; le fix vit là où le runner est configuré, comme le gate `cd`-ait déjà
|
||||||
|
dans le module pour fixer le contrat de CWD).
|
||||||
|
|
||||||
|
**Verrou — l'absence du fix est INVISIBLE sur runner UTF-8** (retirer l'`env:` reste vert
|
||||||
|
aujourd'hui) : classe de « vert trompeur » exactement traquée par ce dépôt. J'ancre donc le fix dans
|
||||||
|
le gate **existant** `check_ci_integrity.sh` (**INV-C**, pas de nouveau fichier gate #5) : il lit
|
||||||
|
l'`env:` top-level de `ci.yml` et exige `PYTHONUTF8: "1"`. **Teeth mutation-vérifié** : retrait de
|
||||||
|
la ligne `env:` → INV-C **mord** (exit 1) ; restauré → vert. INV-A/INV-B intacts (l'`env:` est
|
||||||
|
avant `jobs:` → hors du parseur de jobs).
|
||||||
|
|
||||||
|
**Preuve end-to-end.** `run_ci.sh` sous `env -u PYTHONUTF8 PYTHONCOERCECLOCALE=0 LC_ALL=C` (config
|
||||||
|
qui était **totalement ROUGE** avant) = **33 PASS · 0 FAIL** grâce au `export` miroir. Build-loop
|
||||||
|
sous `PYTHONUTF8=1 + LC_ALL=C` = **21 artefacts byte-identiques** (0 dérive, le fix ne change aucun
|
||||||
|
octet de sortie). `run_ci.sh` normal = **33 PASS**.
|
||||||
|
|
||||||
|
**Portée / anti-churn.** 4 fichiers : `ci.yml` (env), `run_ci.sh` (miroir), `check_ci_integrity.sh`
|
||||||
|
(INV-C verrou), `ci/README.md` (§2 détail INV-C + « Deux → Trois invariants »). **0 fichier de
|
||||||
|
production**, **0 artefact rebuild** (aucun `out/` touché → pas de rebuild `quality_report`, cf.
|
||||||
|
[[audit4big-rebuild-after-doc-edits]]), **0 chiffre inventé** (#6 — 20/21 dérivés de la boucle),
|
||||||
|
**0 commande VPS** (#8). run_ci = **33 PASS · 0 FAIL · 0 SKIP**. Mémoire neuve
|
||||||
|
*utf8-locale-runner-fix* (sœur de [[locale-tz-determinism-sweep]] dont elle **corrige l'angle mort**).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Session 164319 · FIX régression RED : `quality_report.json` périmé après édition de doc → **VERT**
|
||||||
|
|
||||||
|
**Constat au démarrage.** `./run_ci.sh` = **32 PASS · 1 FAIL** — `check-artifacts` RED sur
|
||||||
|
`qa/audit_4big/quality_report.json` (« DÉRIVE : le fichier commité diffère du build frais »).
|
||||||
|
Le tree HEAD `9d30939` (« Auto exec · session 161319 ») était commité **RED**. Cause racine :
|
||||||
|
cette session a grossi `05_deliverables_mvp/qa/regression/README.md` (+11 lignes, 4396→4948 octets)
|
||||||
|
mais **n'a pas rejoué** `audit_4big build`. Or l'audit 4Big **score le contenu des docs** et
|
||||||
|
consigne la taille de chaque README dans son champ `evidence` → drift mécanique, exactement la
|
||||||
|
classe [[audit4big-rebuild-after-doc-edits]] (« rebuild quality_report LAST, après toute édition de
|
||||||
|
prose »).
|
||||||
|
|
||||||
|
**Fix.** `python3 audit_4big_gen.py build -o out` → seul champ modifié :
|
||||||
|
`evidence: "README.md (4396 octets)"` → `"(4948 octets)"`. **Verdict inchangé PASS · 24/24 modules
|
||||||
|
≥ 95 (min 100)**, `coverage.ok=true` : la régression était purement l'echo d'une taille de fichier,
|
||||||
|
pas une perte de qualité. `MANIFEST.json` non touché (cohérent → `check_artifacts` revenu VERT).
|
||||||
|
|
||||||
|
**Recompute indépendant (aucun chiffre saisi · #6).** Diff commité↔build frais = **1 ligne** (le
|
||||||
|
byte-count). `run_ci.sh` post-fix = **33 PASS · 0 FAIL · 0 SKIP** (8 statiques + 25 suites).
|
||||||
|
|
||||||
|
**Portée / anti-churn.** **0 fichier de production édité** (seul l'artefact régénéré), **0 gate
|
||||||
|
ajouté** (#5 — la dérive était **déjà détectée** par `check_artifacts`, c'est la discipline
|
||||||
|
« rebuild LAST » qui a manqué, pas une gate), **0 chiffre saisi** (#6 — 4948 dérivé de la taille
|
||||||
|
réelle du README par le générateur), **aucune commande VPS** (#8). Classe connue en mémoire ;
|
||||||
|
attestation que le gate `check_artifacts` **mord sur le tree commité** (RED opposable, pas seulement
|
||||||
|
pré-commit) — jumelle de l'incident `144304`.
|
||||||
|
|
||||||
|
**Vérif.** `git diff --stat` = 1 fichier / 1 ligne · `./run_ci.sh` → **33 PASS · 0 FAIL · 0 SKIP**.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Session 154314 · SWEEP neuf : indépendance LOCALE / TIMEZONE des artefacts `build` → **CLEAN**
|
## Session 154314 · SWEEP neuf : indépendance LOCALE / TIMEZONE des artefacts `build` → **CLEAN**
|
||||||
|
|
||||||
**Contexte + choix de tâche.** `./run_ci.sh` au démarrage : **33 PASS · 0 FAIL · 0 SKIP**, arbre
|
**Contexte + choix de tâche.** `./run_ci.sh` au démarrage : **33 PASS · 0 FAIL · 0 SKIP**, arbre
|
||||||
@@ -1452,3 +1537,85 @@ sur l'arbre édité : `check_docs` ✅ (liens internes OK), `guard_constraints`
|
|||||||
daily_report `2026-08-05.md` (section `144304`) + ce journal. Classe [[two-logging-channels]].
|
daily_report `2026-08-05.md` (section `144304`) + ce journal. Classe [[two-logging-channels]].
|
||||||
|
|
||||||
**Vérif.** `./run_ci.sh` → **33 PASS · 0 FAIL · 0 SKIP** (aucun fichier gaté touché).
|
**Vérif.** `./run_ci.sh` → **33 PASS · 0 FAIL · 0 SKIP** (aucun fichier gaté touché).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Session `20260805_171321` · Sweep docstring-vs-code étendu aux helpers `*lib/` — CLEAN, 1 faux-positif neuf réfuté
|
||||||
|
|
||||||
|
**Constat.** `run_ci.sh` → **33 PASS · 0 FAIL · 0 SKIP**. Roadmap fonctionnelle
|
||||||
|
intégralement livrée+gatée ; restes hors périmètre worker (VPS/EAS/ONAPI · #8). Aucune
|
||||||
|
tâche fonctionnelle in-repo résiduelle. Valeur honnête du jour : étendre la veille
|
||||||
|
d'intégrité `docstring-vs-code` (classe RÉCURRENTE/non-close, cf. [[docstring-vs-code-drift]])
|
||||||
|
à la **surface qu'un balayage scopé `*_gen.py` rate** : les paquets d'aide `*lib/`.
|
||||||
|
|
||||||
|
**Sweep exécuté (lecture-seule).** Fan-out `Explore` scopé UNIQUEMENT aux helpers
|
||||||
|
`seolib` · `publiciste/lib` · `deploylib` · `dvlib` · `finlib` · `wflib` · `commlib` ·
|
||||||
|
`ecflib` · `genlib` — docstrings module + fonction, directions A (annonce non-implémentée)
|
||||||
|
ET B (omet une sortie réelle) → **0 dérive réelle**.
|
||||||
|
|
||||||
|
**Faux-positif neuf réfuté (vérifié en source + contre le spec).** Le seul candidat
|
||||||
|
remonté : `crm/financement_bancaire/finlib/gate.py:58`, docstring de `apport_requis_usd`
|
||||||
|
« Apport requis = prix × taux (**20 % résident RD, 30 % étranger**) », soupçonnée de
|
||||||
|
« coder en dur une constante que le code lit du spec ». **NON — accurate-to-contract** :
|
||||||
|
la parenthèse **illustre les valeurs COURANTES du spec**, vérifiées égales à
|
||||||
|
`financement_spec.json` (`resident_rd`=20 · `etranger`=30) ; elle reflète la ligne 8 de la
|
||||||
|
MÊME docstring module (jugée correcte) ; les lignes 15-18 posent déjà le cadre « JAMAIS
|
||||||
|
codés en dur, viennent du contrat ». Le code (l.59-64) lit bien `cfg.taux_apport_pct[...]`.
|
||||||
|
→ « docstring citant la valeur courante d'un nombre spec-driven en illustration » =
|
||||||
|
même famille que l'exclusion pédagogique/illustrative. **LAISSÉ** (anti-churn #5) — pas
|
||||||
|
d'harmonisation, pas de re-flag. Leçon consignée en mémoire : vérifier la valeur COURANTE
|
||||||
|
du spec avant de qualifier de contradiction-hardcode.
|
||||||
|
|
||||||
|
**Portée / anti-churn.** **0 fichier de production édité**, **0 artefact reconstruit**
|
||||||
|
(aucun `out/*.json` touché → pas de rebuild `quality_report`, cf.
|
||||||
|
[[audit4big-rebuild-after-doc-edits]]), **0 gate ajouté** (#5 — les docstrings sont de la
|
||||||
|
prose, faible valeur à gater ; la parenthèse est de surcroît exacte), **0 chiffre saisi à
|
||||||
|
la main** (#6 — les 20/30 sont lus du spec, non retapés), **aucune commande VPS** (#8 —
|
||||||
|
100 % local). Éditions : mémoire `docstring-vs-code-drift.md` (note du faux-positif) + ce
|
||||||
|
journal.
|
||||||
|
|
||||||
|
**Vérif.** `./run_ci.sh` → **33 PASS · 0 FAIL · 0 SKIP** (aucun fichier gaté touché).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Session 20260805_174324 · GATE NEUF — prose #9 des modules bancable + commissions ancrée sur CLAUDE.md
|
||||||
|
|
||||||
|
**Contexte.** Repo vert au démarrage (33 PASS · 0 FAIL · 0 SKIP), tous modules livrés,
|
||||||
|
roadmap 8-sem sans tâche-feature ouverte. Sonde ciblée sur la classe **claude-md-constant-anchor-gate** :
|
||||||
|
les constantes canoniques #9 (3 % édition · 8.5 % marketing · 52 % point d'équilibre)
|
||||||
|
recopiées hors du module generator.
|
||||||
|
|
||||||
|
**Constat (mutation-prouvé).** `sed` #9 `8.5 %→9 %` sur `CLAUDE.md` → le gate
|
||||||
|
`check_readme_claims.sh` fait rougir **8 surfaces generator/template** (model.py CANONICAL,
|
||||||
|
README, renderer `_CANONICAL_MARKERS`, oracle test, 2 docstrings scorer, template intro/§5.2/§7)
|
||||||
|
— l'ancre est vivante. **MAIS 4 copies restaient périmées EN SILENCE**, dans DEUX modules
|
||||||
|
DISTINCTS, chacune se réclamant explicitement « (#9) » / « CLAUDE.md #9/#10 » :
|
||||||
|
- (a) `faisabilite/bancable/README.md` — « 3 % édition » et « 8.5 % marketing » (#9)
|
||||||
|
- (b) `faisabilite/bancable/banclib/finance.py` — MÊME phrase, en docstring
|
||||||
|
- (c) `faisabilite/bancable/banclib/deps.py` — « CLAUDE.md #9/#10 (3 %/8.5 %/52 % · USD+DOP · Cardnet · Letter US) » (6 marqueurs)
|
||||||
|
- (d) `crm/commissions/README.md` — « pourcentages canoniques (3 % édition · 8.5 % marketing · 52 % point d'équilibre) »
|
||||||
|
|
||||||
|
Corriger le generator ne TOUCHE pas ces modules → invention #6 latente, de la classe déjà
|
||||||
|
fermée 10× (publiciste #10 · fiscal moneda · fiches #10 · template canonique).
|
||||||
|
|
||||||
|
**Fix.** Extension du bloc d'ancrage existant dans `ci/check_readme_claims.sh` (juste après
|
||||||
|
« Template canonique »), **réutilisant `exp`** déjà recomputé des lignes #9/#10 de `CLAUDE.md`
|
||||||
|
(zéro re-parse, zéro duplication). Un claim absent échoue AUSSI (traçabilité #6). Documenté
|
||||||
|
en `ci/README.md` §2 (paragraphe « paramètres canoniques »), pas de nouvelle ligne au
|
||||||
|
tableau §1 (cf. [[ci-readme-table-detail-in-section2]]).
|
||||||
|
|
||||||
|
**Teeth (mutation-vérifié, arbre restauré à chaque fois).**
|
||||||
|
- `#9 8.5 %→9 %` → mord les **4** surfaces.
|
||||||
|
- `#9 52 %→55 %` → mord **(c)+(d) SEULEMENT** ; (a)/(b) ne citent qu'édition+marketing ⇒
|
||||||
|
correctement épargnées = **ciblage précis** (pas de faux-rouge).
|
||||||
|
- `#10 Cardnet→Azul` → mord l'énumération 6-marqueurs de (c).
|
||||||
|
- Arbre restauré → gate exit=0, les 4 surfaces re-vertes.
|
||||||
|
|
||||||
|
**Portée / anti-churn.** **0 fichier de production édité** (seuls `ci/check_readme_claims.sh`
|
||||||
|
+ `ci/README.md`), **0 artefact reconstruit** (aucun `out/*.json` ni module doc scoré par
|
||||||
|
audit_4big → pas de rebuild `quality_report`), **0 chiffre saisi à la main** (#6 — les 6
|
||||||
|
valeurs sont recomputées de `CLAUDE.md`, jamais retapées), **aucune commande VPS** (#8 —
|
||||||
|
100 % local). Classe RÉCURRENTE (chaque module distinct qui recopie une constante #9/#10
|
||||||
|
citée est une surface à ancrer) : nouvelle sous-surface (bancable + commissions) close.
|
||||||
|
|
||||||
|
**Vérif.** `./run_ci.sh` → **33 PASS · 0 FAIL · 0 SKIP**.
|
||||||
|
|||||||
@@ -984,7 +984,7 @@
|
|||||||
"criterion": "DOC",
|
"criterion": "DOC",
|
||||||
"weight": 20,
|
"weight": 20,
|
||||||
"passed": true,
|
"passed": true,
|
||||||
"evidence": "README.md (4396 octets)"
|
"evidence": "README.md (4948 octets)"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"criterion": "CONTRAT",
|
"criterion": "CONTRAT",
|
||||||
|
|||||||
+28
-1
@@ -181,7 +181,7 @@ puis commit.
|
|||||||
### `check_ci_integrity.sh`
|
### `check_ci_integrity.sh`
|
||||||
Prouve, en lisant `.gitea/workflows/ci.yml`, que le **câblage** du workflow tient —
|
Prouve, en lisant `.gitea/workflows/ci.yml`, que le **câblage** du workflow tient —
|
||||||
car le job `gate` est le **seul verrou de merge** : un check absent de son `needs:`
|
car le job `gate` est le **seul verrou de merge** : un check absent de son `needs:`
|
||||||
ne bloque **rien**, même rouge. Deux invariants :
|
ne bloque **rien**, même rouge. Trois invariants :
|
||||||
- **INV-A** — `gate.needs` == { tous les jobs définis } − { `gate` } − { jobs manuels }
|
- **INV-A** — `gate.needs` == { tous les jobs définis } − { `gate` } − { jobs manuels }
|
||||||
(un job manuel = gardé par `if: … workflow_dispatch …`, ex. `e2e-baseline`,
|
(un job manuel = gardé par `if: … workflow_dispatch …`, ex. `e2e-baseline`,
|
||||||
légitimement hors du gate push/PR car il exige un serveur live). Détecte un job
|
légitimement hors du gate push/PR car il exige un serveur live). Détecte un job
|
||||||
@@ -193,6 +193,21 @@ ne bloque **rien**, même rouge. Deux invariants :
|
|||||||
script (lib partagée `ci/lib.sh` — jamais un job propre). Un nouveau gate statique
|
script (lib partagée `ci/lib.sh` — jamais un job propre). Un nouveau gate statique
|
||||||
**non câblé** (script mort) ou **décâblé**, ou une **lib morte** (sourcée par
|
**non câblé** (script mort) ou **décâblé**, ou une **lib morte** (sourcée par
|
||||||
personne), casse le check.
|
personne), casse le check.
|
||||||
|
- **INV-C** — le workflow force le **mode UTF-8 de Python** (`env: PYTHONUTF8: "1"`
|
||||||
|
au niveau workflow → tous les jobs). Motif : chaque générateur clôt son `build`
|
||||||
|
par un `print("✅ …" / accents français)` vers stdout, dont l'encodage suit la
|
||||||
|
**locale du runner**. Sur une image runner ASCII (`LC_ALL=C` sans coercition
|
||||||
|
PEP 538, `PYTHONUTF8` non posé), ce print lève `UnicodeEncodeError` **après**
|
||||||
|
écriture des fichiers → `check-artifacts` voit un exit non-nul et les 25 suites
|
||||||
|
plantent : **tout le gate passe ROUGE le jour où l'image runner change**, alors
|
||||||
|
qu'il est VERT sur le poste dev (UTF-8) — classe « le runner n'est pas ton poste »
|
||||||
|
(sœur des sweeps hash-seed / locale-TZ / forward-compat, mais ici un **défaut
|
||||||
|
réel présent**, pas une simple attestation). `PYTHONUTF8=1` (PEP 540) impose
|
||||||
|
stdout/stdin/défaut-fichier en UTF-8 **partout** → 20 builds qui crashaient +
|
||||||
|
les suites redeviennent locale-robustes (prouvé : `run_ci.sh` sous
|
||||||
|
`LC_ALL=C` neutralisé = 33 PASS). Comme l'absence de cet `env:` est **invisible**
|
||||||
|
sur un runner UTF-8, INV-C l'**ancre** : le retirer casse ce gate au lieu de
|
||||||
|
dériver en silence. `run_ci.sh` **exporte la même variable** (miroir fidèle).
|
||||||
|
|
||||||
Ferme le trou laissé par les couvertures existantes (`audit_4big/registry`,
|
Ferme le trou laissé par les couvertures existantes (`audit_4big/registry`,
|
||||||
`qa/regression/discovery`) qui ne prouvent l'appartenance au gate que des jobs de
|
`qa/regression/discovery`) qui ne prouvent l'appartenance au gate que des jobs de
|
||||||
@@ -1196,6 +1211,18 @@ vérifiées : renderer `Cardnet→Azul` (absent/en-trop) · test oracle retire `
|
|||||||
mord SIMULTANÉMENT model.py + renderer + test + les DEUX docstrings** (la valeur
|
mord SIMULTANÉMENT model.py + renderer + test + les DEUX docstrings** (la valeur
|
||||||
même de l'ancrage) ; restauré = vert.
|
même de l'ancrage) ; restauré = vert.
|
||||||
|
|
||||||
|
Même classe enfin sur QUATRE copies **hors du module generator** — la prose #9 de
|
||||||
|
DEUX modules distincts qui se réclame explicitement « (#9) » / « CLAUDE.md #9/#10 »
|
||||||
|
mais qu'aucun gate ne liait à la contrainte (corriger le generator ne les touche pas) :
|
||||||
|
(a) `faisabilite/bancable/README.md` et (b) sa docstring `banclib/finance.py`
|
||||||
|
(« 3 % édition » et « 8.5 % marketing » (#9)), (c) `banclib/deps.py`
|
||||||
|
(« CLAUDE.md #9/#10 (3 %/8.5 %/52 % · USD+DOP · Cardnet · Letter US) », 6 marqueurs),
|
||||||
|
(d) `crm/commissions/README.md` (« pourcentages canoniques (3 % édition · 8.5 %
|
||||||
|
marketing · 52 % point d'équilibre) »). On RÉUTILISE `exp` (zéro re-parse). Morsures
|
||||||
|
vérifiées : `#9 8.5 %→9 %` mord les 4 · `#9 52 %→55 %` mord (c)+(d) SEULS ((a)/(b) ne
|
||||||
|
citent qu'édition+marketing ⇒ correctement épargnées, ciblage précis) · `#10
|
||||||
|
Cardnet→Azul` mord l'énumération (c). Un claim absent échoue AUSSI (traçabilité #6).
|
||||||
|
|
||||||
Même classe enfin sur la **fiche d'identité du rôle RBAC mobile** — la fiche agent
|
Même classe enfin sur la **fiche d'identité du rôle RBAC mobile** — la fiche agent
|
||||||
`03_agents/mobile/AGENT.md`. Les builds/soumissions stores étant **hors-repo** (#8),
|
`03_agents/mobile/AGENT.md`. Les builds/soumissions stores étant **hors-repo** (#8),
|
||||||
l'agent mobile le reconnaît et déclare que son **seul ancrage in-repo vérifiable** est
|
l'agent mobile le reconnaît et déclare que son **seul ancrage in-repo vérifiable** est
|
||||||
|
|||||||
@@ -148,6 +148,28 @@ while IFS= read -r script; do
|
|||||||
fi
|
fi
|
||||||
done < <(git ls-files 'ci/*.sh' | grep -vx 'ci/check_ci_integrity.sh')
|
done < <(git ls-files 'ci/*.sh' | grep -vx 'ci/check_ci_integrity.sh')
|
||||||
|
|
||||||
|
# --- INV-C : le workflow force le mode UTF-8 de Python (indépendance locale) --
|
||||||
|
# Chaque générateur clôt son `build` par un print(✅/accents) vers stdout ; sous
|
||||||
|
# une locale ASCII (runner minimal : LC_ALL=C, sans coercition PEP 538), ce print
|
||||||
|
# lève UnicodeEncodeError → `check-artifacts` ROUGE et les 25 suites plantent,
|
||||||
|
# EN SILENCE le jour où l'image runner change (VERT aujourd'hui car UTF-8). Le
|
||||||
|
# correctif est un `env: PYTHONUTF8: "1"` au niveau WORKFLOW (s'applique à tous
|
||||||
|
# les jobs). Comme son absence est invisible sur un runner UTF-8, on l'ancre ici :
|
||||||
|
# le retirer casse ce gate au lieu de dériver silencieusement. On lit l'`env:`
|
||||||
|
# top-level (indenté 2 espaces, AVANT `jobs:` → hors du parseur de jobs).
|
||||||
|
echo "== INV-C · le workflow force PYTHONUTF8 (I/O UTF-8 · indépendance locale runner) =="
|
||||||
|
utf8_mode="$(awk '
|
||||||
|
/^jobs:[[:space:]]*$/ { exit } # ne considérer que l’en-tête (avant jobs:)
|
||||||
|
/^env:[[:space:]]*$/ { inenv=1; next }
|
||||||
|
inenv && /^[^[:space:]]/ { inenv=0 } # fin du bloc env: (clé non indentée)
|
||||||
|
inenv && match($0, /^[[:space:]]+PYTHONUTF8:[[:space:]]*["'\'']?1["'\'']?[[:space:]]*$/) { print "1" }
|
||||||
|
' "$CI")"
|
||||||
|
if [[ "$utf8_mode" == "1" ]]; then
|
||||||
|
ok "workflow env: PYTHONUTF8=\"1\" → stdout/défaut-fichier UTF-8 sur tout runner (builds + suites locale-robustes)"
|
||||||
|
else
|
||||||
|
report "workflow SANS \`env: PYTHONUTF8: \"1\"\` top-level → gate locale-fragile (RED silencieux sur runner ASCII · cf. print ✅ en fin de build)"
|
||||||
|
fi
|
||||||
|
|
||||||
echo
|
echo
|
||||||
if [[ "$FAIL" -eq 0 ]]; then
|
if [[ "$FAIL" -eq 0 ]]; then
|
||||||
echo -e "\033[32m✅ Câblage CI intègre (gate agrège tout job non-manuel · gates statiques verrouillés).\033[0m"
|
echo -e "\033[32m✅ Câblage CI intègre (gate agrège tout job non-manuel · gates statiques verrouillés).\033[0m"
|
||||||
|
|||||||
@@ -5864,6 +5864,99 @@ if exp is not None:
|
|||||||
+ (f" · absents={miss}" if miss else "")
|
+ (f" · absents={miss}" if miss else "")
|
||||||
+ (f" · en trop={extra}" if extra else "") + " → régénérer le template")
|
+ (f" · en trop={extra}" if extra else "") + " → régénérer le template")
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Bancable + Commissions · PROSE #9 (« pourcentages canoniques ») ANCRÉE sur
|
||||||
|
# CLAUDE.md — même classe d'ANCRAGE que « paramètres canoniques » (generator) et
|
||||||
|
# « Template canonique » ci-dessus, mais sur des MODULES DISTINCTS
|
||||||
|
# (faisabilite/bancable, crm/commissions) dont la doc RECOPIE à la main les
|
||||||
|
# pourcentages #9 en se réclamant EXPLICITEMENT « (#9) » / « CLAUDE.md #9/#10 »,
|
||||||
|
# sans qu'aucun gate ne les liât à la contrainte.
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
# Le bloc « paramètres canoniques » ancre le SEUL module generator (model.py /
|
||||||
|
# README / renderer / scorer / test) + le TEMPLATE. Restent QUATRE copies
|
||||||
|
# data-derived dans DEUX autres modules, toutes invisibles à check_artifacts
|
||||||
|
# (prose/docstrings hors out/) et non attrapées par leurs tests/ (FONCTIONS) :
|
||||||
|
# (a) faisabilite/bancable/README.md — « 3 % édition » et « 8.5 % marketing » (#9)
|
||||||
|
# (b) faisabilite/bancable/banclib/finance.py — MÊME phrase, en docstring
|
||||||
|
# (c) faisabilite/bancable/banclib/deps.py — « CLAUDE.md #9/#10 (3 %/8.5 %/52 %·…) »
|
||||||
|
# (d) crm/commissions/README.md — « (3 % édition · 8.5 % marketing · 52 % …) »
|
||||||
|
# La mutation #9 (ex. 8.5 %→9 %) fait DÉJÀ rougir les 8 surfaces generator/template,
|
||||||
|
# MAIS ces 4 copies-là restent périmées EN SILENCE : corriger le generator ne les
|
||||||
|
# TOUCHE pas (modules distincts) — invention #6 latente, de la classe déjà fermée
|
||||||
|
# 10× pour publiciste #10 / fiscal moneda / fiches #10. On RÉUTILISE `exp`
|
||||||
|
# (recompute #9 depuis CLAUDE.md · zéro duplication). Un claim absent échoue AUSSI.
|
||||||
|
# ============================================================================
|
||||||
|
if exp is not None:
|
||||||
|
_p_edt = _pnorm(exp["frais_edition_pct"]); _p_mkt = _pnorm(exp["marketing_pct"])
|
||||||
|
_p_eql = _pnorm(exp["point_equilibre_pct"])
|
||||||
|
_want6b = {_pnorm(exp[k]) for k in _num_keys} | {_ai(exp[k]) for k in _txt_keys}
|
||||||
|
# (a)+(b) même phrase « « E % édition » et « M % marketing » (#9) »
|
||||||
|
_duo_re = re.compile(r"«\s*([\d.]+)\s*%\s*[ée]dition\s*»\s*et\s*"
|
||||||
|
r"«\s*([\d.]+)\s*%\s*marketing\s*»\s*\(#9\)")
|
||||||
|
for _lbl, _rel in (("bancable/README", "faisabilite/bancable/README.md"),
|
||||||
|
("bancable/finance.py", "faisabilite/bancable/banclib/finance.py")):
|
||||||
|
_txt = read_norm(f"{D}/{_rel}")
|
||||||
|
if _txt is None:
|
||||||
|
bad(f"Canoniques prose #9 · {_lbl} illisible"); continue
|
||||||
|
_mm = _duo_re.search(_txt)
|
||||||
|
if _mm is None:
|
||||||
|
bad(f"Canoniques prose #9 · {_lbl} — phrase « « N % édition » et "
|
||||||
|
f"« N % marketing » (#9) » INTROUVABLE (traçabilité #6)")
|
||||||
|
else:
|
||||||
|
_got = {_pnorm(float(_mm.group(1))), _pnorm(float(_mm.group(2)))}
|
||||||
|
_want = {_p_edt, _p_mkt}
|
||||||
|
if _got == _want:
|
||||||
|
good(f"Canoniques prose #9 · {_lbl} — {_p_edt} édition · {_p_mkt} "
|
||||||
|
f"marketing == CLAUDE.md #9")
|
||||||
|
else:
|
||||||
|
bad(f"Canoniques prose #9 · {_lbl} — cite {sorted(_got)} MAIS CLAUDE.md "
|
||||||
|
f"{sorted(_want)} → aligner (ou CLAUDE.md)")
|
||||||
|
# (c) deps.py « CLAUDE.md #9/#10 (3 % / 8.5 % / 52 % · USD+DOP · Cardnet · Letter US) »
|
||||||
|
_dep = read_norm(f"{D}/faisabilite/bancable/banclib/deps.py")
|
||||||
|
if _dep is None:
|
||||||
|
bad("Canoniques prose #9 · bancable/deps.py illisible")
|
||||||
|
else:
|
||||||
|
_md = re.search(r"CLAUDE\.md #9/#10\s*\(([^)]+)\)", _dep)
|
||||||
|
if _md is None:
|
||||||
|
bad("Canoniques prose #9 · bancable/deps.py — « CLAUDE.md #9/#10 (…) » "
|
||||||
|
"INTROUVABLE (traçabilité #6)")
|
||||||
|
else:
|
||||||
|
_got = set()
|
||||||
|
for _tok in re.split(r"[/·]", _md.group(1)):
|
||||||
|
_p = _pct(_tok)
|
||||||
|
_got.add(_pnorm(_p) if _p is not None else _ai(_tok))
|
||||||
|
_got.discard("")
|
||||||
|
if _got == _want6b:
|
||||||
|
good("Canoniques prose #9 · bancable/deps.py — 6 marqueurs == "
|
||||||
|
"CLAUDE.md #9/#10")
|
||||||
|
else:
|
||||||
|
_miss = sorted(_want6b - _got); _extra = sorted(_got - _want6b)
|
||||||
|
bad(f"Canoniques prose #9 · bancable/deps.py — énumère {sorted(_got)} MAIS "
|
||||||
|
f"CLAUDE.md {sorted(_want6b)}"
|
||||||
|
+ (f" · absents={_miss}" if _miss else "")
|
||||||
|
+ (f" · en trop={_extra}" if _extra else "") + " → aligner (ou CLAUDE.md)")
|
||||||
|
# (d) commissions/README « pourcentages canoniques (E % édition · M % marketing · Q % …) »
|
||||||
|
_com = read_norm(f"{D}/crm/commissions/README.md")
|
||||||
|
if _com is None:
|
||||||
|
bad("Canoniques prose #9 · commissions/README illisible")
|
||||||
|
else:
|
||||||
|
_mc = re.search(r"pourcentages canoniques\s*\(([^)]*%[^)]*)\)", _com)
|
||||||
|
if _mc is None:
|
||||||
|
bad("Canoniques prose #9 · commissions/README — « pourcentages canoniques "
|
||||||
|
"(… %) » INTROUVABLE (traçabilité #6)")
|
||||||
|
else:
|
||||||
|
_got = {_pnorm(float(_x)) for _x in re.findall(r"([\d.]+)\s*%", _mc.group(1))}
|
||||||
|
_want = {_p_edt, _p_mkt, _p_eql}
|
||||||
|
if _got == _want:
|
||||||
|
good(f"Canoniques prose #9 · commissions/README — {sorted(_want)} == "
|
||||||
|
f"CLAUDE.md #9")
|
||||||
|
else:
|
||||||
|
_miss = sorted(_want - _got); _extra = sorted(_got - _want)
|
||||||
|
bad(f"Canoniques prose #9 · commissions/README — cite {sorted(_got)} MAIS "
|
||||||
|
f"CLAUDE.md {sorted(_want)}"
|
||||||
|
+ (f" · absents={_miss}" if _miss else "")
|
||||||
|
+ (f" · en trop={_extra}" if _extra else "") + " → aligner (ou CLAUDE.md)")
|
||||||
|
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
# Fiscal · ecf_dgii — le JEU DE DEVISES `moneda` (`USD`/`DOP`) ANCRÉ sur CLAUDE.md
|
# Fiscal · ecf_dgii — le JEU DE DEVISES `moneda` (`USD`/`DOP`) ANCRÉ sur CLAUDE.md
|
||||||
# #10 — même classe d'ANCRAGE que la CITATION #10 des fiches ci-dessus, mais sur
|
# #10 — même classe d'ANCRAGE que la CITATION #10 des fiches ci-dessus, mais sur
|
||||||
|
|||||||
@@ -73,6 +73,13 @@ cd "$ROOT" || exit 3
|
|||||||
CI_YML=".gitea/workflows/ci.yml"
|
CI_YML=".gitea/workflows/ci.yml"
|
||||||
[ -f "$CI_YML" ] || { echo "run_ci.sh : $CI_YML introuvable." >&2; exit 3; }
|
[ -f "$CI_YML" ] || { echo "run_ci.sh : $CI_YML introuvable." >&2; exit 3; }
|
||||||
|
|
||||||
|
# Miroir de l'`env:` workflow de ci.yml — force le mode UTF-8 de Python pour
|
||||||
|
# tous les jobs rejoués ici, de sorte que ce miroir local reproduise fidèlement
|
||||||
|
# le gate même si le poste tourne sous une locale ASCII (LC_ALL=C). Sans cela,
|
||||||
|
# les générateurs (print ✅/accents en fin de `build`) et les suites planteraient
|
||||||
|
# ici alors qu'ils passent en CI (cf. ci.yml env PYTHONUTF8 · check_ci_integrity INV-C).
|
||||||
|
export PYTHONUTF8=1
|
||||||
|
|
||||||
MODE="all"
|
MODE="all"
|
||||||
case "${1:-}" in
|
case "${1:-}" in
|
||||||
--static) MODE="static" ;;
|
--static) MODE="static" ;;
|
||||||
|
|||||||
Reference in New Issue
Block a user