corentin_wakdo/docs/SESSION_RESUME.md
Imugiii 6243304a29
Some checks failed
CI / secret-scan (push) Successful in 10s
CI / php-lint (push) Successful in 20s
CI / static-tests (push) Has been cancelled
CI / js-tests (push) Has been cancelled
feat(admin): page d'information RGPD sur le traitement des donnees (Cr 3.d.2)
2026-06-22 06:20:27 +00:00

1623 lines
94 KiB
Markdown
Executable file
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Session Resume — Wakdo
**Document de reprise entre sessions de travail.**
A consulter en premier quand tu reprends le projet.
---
## Derniere session : 2026-06-19 — Fix borne same-origin + GOAL re-alignement maquette (P5 front) + reste P4
**Auteur : BYAN.** Session marathon. Depart : le user a constate que la borne « n'etait
pas cablee » (clic produit sans effet). Diagnostic prouve sur la vraie stack, puis un
cycle FD complet (`borne-realign-maquette`, COMPLETED) + un /goal de 8 items livres en
autonomie, chacun en PR auto-merge sur CI verte.
### 1. Fix borne same-origin (PR #62)
`data.js` faisait des `fetch('/api/*')` RELATIFS -> resolus sur l'origine borne
(corentin-wakdo.stark.a3n.fr) ou il n'y a pas d'API -> fallback SPA index.html (HTML,
pas JSON) -> catalogue vide. Le middleware CORS de #61 n'etait donc pas sollicite.
Decision (secu + conventions) : SAME-ORIGIN. Le vhost kiosk relaie `/api/*` vers PHP-FPM
(`ProxyPassMatch` + `ProxyFCGISetEnvIf` qui force SCRIPT_FILENAME sur admin/index.php,
sinon FPM rejette via security.limit_extensions). CORS conserve en defense. conventions.md §10 reecrit.
### 2. Decomposition maquette (PR #63)
maquette-borne.pdf decompose : 10 ecrans en PNG (docs/design/screens/) + docs/design/
maquette-vs-build.md (mapping maquette<->build : la maquette est mono-ecran avec panneau
persistant, le build etait multi-pages).
### 3. GOAL — 8 items livres (PR #64-#71)
- #64 L1 : panneau commande persistant + bandeau categories + charte.
- #65 L2 : composeur menu SLOT-DRIVEN /api/menus (remplace la compo libre ; ferme dette P4 #3).
- #66 L3 : modale options produit (remplace la page product.html ; grille -> modales).
- #67 seed : recettes des 53 produits (db/seeds/0003_ingredients_recipes.sql, 50 ingredients) -> stock vivant.
- #68 L4 : SOUMISSION REELLE de commande (checkout.js panier->/api/orders create+pay) + ecran chevalet. Avant : checkout 100% simule.
- #69 cleanup : memoisation des loaders data.js (promesse) + contraste a11y selection.
- #70 orders admin : /admin/orders (order.read) + KPIs vente dans /admin/stats (OrderQueryRepository).
- #71 /api/allergens : endpoint public (id/code/name) ; la borne garde son JSON statique (descriptions). Item 0 = gate CI js-tests requis (dev+main).
### Decouvertes corrigees
- La borne ne soumettait aucune commande (payment.html simulait) -> soumission reelle cablee.
- Migration 0003_order_service_tag.sql non appliquee sur la stack dev (3 j d'uptime)
-> la creation de commande echouait (Unknown column service_tag). Reconciliee dans
schema_migrations ; doublon de migration ecarte (catch d'une revue adversariale).
- Incident compose (reseau recree) : `wakdo-web` recree via
`docker compose -f docker-compose.prod.yml up -d --no-deps --force-recreate`
(la stack tourne via prod.yml + Traefik reseau `admin_proxy`).
### Verification
351 tests PHP (unit + integration DB) + 64 JS, PHPStan L6 propre. e2e reel : commande K1
creee + payee, stock decremente exactement (-5 Steak hache). Chaque lot a eu une revue
adversariale par workflow (must-fix integres).
### Nettoyage git (fin de session)
Branches purgees : local + forge = `dev` + `main` seulement (17 branches feature mergees
supprimees cote forge, 6 locales). `dev` = `7ab9a5a` (#71).
### EN COURS — accompagnement menu Frite/Potatoes + Maxi (approuve, NON code)
Le user a constate que le composeur menu propose les 5 produits frites (Petite/Moyenne/
Grande Frite + Potatoes/Grande Potatoes) comme accompagnement. Regle voulue (et conforme
maquette ecran 4 : 2 choix Frites/Potatoes) :
- Menu = 2 choix d'accompagnement : Frites (Moyenne Frite) + Potatoes.
- Maxi -> version Grande associee automatiquement (Grande Frite / Grande Potatoes).
- Petite Frite + tailles individuelles = a la carte seulement (section frites), hors menu.
**Approche choisie par le user : variante en base** (data-driven, le stock decremente le
bon produit). Plan a executer (TDD + revue + commit/PR/auto-merge) :
1. Migration `db/migrations/0004_product_maxi_variant.sql` : `ALTER TABLE product ADD
COLUMN maxi_variant_product_id INT UNSIGNED NULL` + FK -> product(id). A appliquer sur dev.
2. Seed `db/seeds/0004_menu_side_maxi.sql` (idempotent) :
- `UPDATE product SET maxi_variant_product_id=24 WHERE name='Moyenne Frite'`
(24 = Grande Frite) ; `=26 WHERE name='Potatoes'` (26 = Grande Potatoes).
- `DELETE` des options de slot `side` pour ne garder que Moyenne Frite (23) + Potatoes
(25) ; retirer Petite Frite (22), Grande Frite (24), Grande Potatoes (26) des options menu.
3. `ProductRepository::find` (src/app/Catalogue/ProductRepository.php:50) : ajouter
`maxi_variant_product_id` aux colonnes du SELECT (absent aujourd'hui).
4. `OrderRepository::resolveSelections` (src/app/Order/OrderRepository.php:395) : passer le
`format` ; valider la selection BASE contre les options du slot, puis si format='maxi' ET
le produit a un maxi_variant non nul -> STOCKER l'id de la variante (label de la variante).
`consumption()` decremente alors la Grande variante (stock correct). `resolveLine` (qui
connait le format) passe `$format` a `resolveSelections`.
5. Tests : OrderRepository DbTest (menu Maxi + accompagnement -> selection stockee = Grande
variante + stock decremente Grande Frite, pas Moyenne) ; unit du swap dans resolveSelections.
6. Borne : le composeur affiche 2 options automatiquement (slots /api/menus restreints par le
seed) -> pas de code borne. Afficher le libelle Maxi au recap = optionnel, differable.
Ids frites (dev) : Petite 22, Moyenne 23, Grande 24, Potatoes 25, Grande Potatoes 26.
### Reste / a faire
- VALIDATION VISUELLE utilisateur sur ecran reel (gros changement front, pas encore vu en vrai).
- Differes (LOW, notes) : format menu deduit de supplement_cents (theorique, seed +150) ;
multi-slots de meme slot_type (menus famille) ; descriptions allergenes cote schema si
on veut basculer la borne sur /api/allergens.
- Donnees demo : commande de test K1 en base dev (stock -5, sans impact).
### Reprendre
```bash
cd /home/acadenice/corentin_wakdo
git checkout dev && git fetch forgejo && git merge --ff-only forgejo/dev
git log --oneline -12 dev # #62 fix borne ... #71 allergens
docker compose ps # stack dev (prod.yml + Traefik)
```
---
## Session : 2026-06-18 (suite 2) — P4 read API borne (#60) + cablage CORS/data.js (#61)
**Auteur : BYAN.** Deux cycles FD menes en entier (BRAINSTORM -> PRUNE -> DISPATCH ->
BUILD -> VALIDATE), chacun livre en PR auto-merge sur CI verte. But : brancher la borne
kiosk sur le catalogue en base, a la place des fichiers JSON statiques.
### Livre
- **PR #60 — read API catalogue borne** (mergee dans dev, squash `a35db88`) : 5 endpoints
publics anonymes en lecture seule (`docs/api/conventions.md` 5.2) : `GET /api/categories`,
`/api/products`, `/api/products/{id}`, `/api/menus`, `/api/menus/{id}` (detail avec slots).
`CatalogueController` + methodes de lecture filtrees sur Category/Product/MenuRepository
(`is_available=1` ET categorie active : la borne ne voit que le commandable, en liste
comme en detail). Enveloppe `{data,total}`, champs canoniques snake_case ; `vat_rate`
exclu, `is_available` retire des payloads, `is_required` de slot en booleen, sans
timestamp. Dispo calculee RG-T21 differee (recettes non seedees).
- **PR #61 — cablage borne (CORS + data.js)** (auto-merge arme a la redaction) :
- `App\Core\Cors` : middleware CORS scope `/api/`, origine unique et exacte
(`CORS_ALLOWED_ORIGIN`, sans joker), `GET/POST/OPTIONS`, header `Content-Type`, sans
credentials, fail-closed ; preflight `OPTIONS -> 204` avant le routeur ; decoration de
la reponse y compris le 500 du `catch`. `CORS_ALLOWED_ORIGIN` deja cable
(`.env.example` + docker-compose).
- `data.js` reecrit : consomme `/api/categories|products|menus`, deballe `{data}`,
traduit la forme canonique vers la forme borne (`nom/prix/image/type`), regroupe par
slug de categorie, glisse les menus sous la cle `menus`. Signatures publiques
inchangees -> pages intactes. `loadAllergens` reste statique.
### Revue adversariale (workflow multi-agents, une par cycle)
- Read API : 1 finding LOW (lacune de test : slot sans option) -> test ajoute.
- Cablage : 2 findings, corriges.
- **CRITIQUE** : `findProduct(id)` supposait des ids uniques sur l'ensemble du catalogue
(vrai pour l'ancien JSON statique). En base, `product` et `menu` sont deux tables
auto-increment distinctes demarrant a 1 -> collision (id 4 = burger Big Mac ET Menu Big
Mac), cliquer un burger ouvrait le composeur de menu. Corrige :
`findProduct(id, categorySlug)` desambigue par categorie (id unique dans une
categorie) ; `page-product.js` passe le slug deja present dans l'URL ; 2 tests de
regression.
- **LOW** : la reponse 500 du `catch` n'etait pas decoree CORS -> `$request`/`$cors`
construits hors du `try` + decoration de la 500.
### Etat tests / CI
PHP unit 305 / 859 assertions, integration 40 / 253 (`WAKDO_DB_TESTS=1`, vraie base dev),
PHPStan L6 propre ; JS `data.js` 10 tests (node:test, `fetch` mocke). CI Forgejo : merge
natif squash sur checks requis (secret-scan, php-lint, static-tests).
### Decisions actees
- L'API de lecture parle le dictionnaire (snake_case canonique) ; le rapprochement vers la
forme heritee de la borne se fait en un point unique, `data.js` (conventions.md 8.3).
- Le composeur de menu reste libre (il ne consomme pas les slots `/api/menus`) pour ce
chunk ; sa refonte sur les slots est differee.
- Regle de livraison (user) : livrer via commit + PR + auto-merge natif sur CI verte, sans
gate manuel a chaque commit (memoire `feedback_commit_discipline`).
### File d'attente (suite)
1. **Verif cross-origin reelle** : le `.env` pointe `CORS_ALLOWED_ORIGIN` sur l'origine
serveur. Pour valider borne -> API en local, mettre `CORS_ALLOWED_ORIGIN` sur l'origine
du kiosk (`APP_URL_KIOSK`) + recreer `wakdo-app`, puis E2E #45.
2. **Seed des recettes** (`product_ingredient`) -> active le decrement de stock de `pay()`
(chunk 1b) ET la dispo calculee RG-T21 dans les endpoints read.
3. **Refonte du composeur de menu** borne pour consommer les slots `/api/menus` (B1 burger
impose, B2 Normal/Maxi) au lieu de la composition libre.
4. **Gate CI** : ajouter `CI / js-tests` aux checks requis (sinon un JS rouge pourrait
merger).
5. Optionnel : `/api/allergens` (la borne garde son JSON), prix en euros vs centimes.
### Reprendre
```bash
cd /home/acadenice/corentin_wakdo
git checkout dev && git fetch forgejo && git merge --ff-only forgejo/dev
git log --oneline -6 dev # #60 read API + #61 cablage borne
docker compose up -d
```
---
## Session : 2026-06-17 (suite) — P3 Users/RBAC/Stats + Makefile -> docker compose
**Auteur : BYAN.** Deuxieme cycle FD du jour (`20260617-102548-p3-users-rbac-stats`,
COMPLETED) puis un chore d'infra. P3 back-office desormais complet.
### Livre (dev = 9d75fab)
- **Lot S — Stats (#37)** : `StatsController` + `StatsRepository` -> `/admin/stats`
(permission `stats.read`). KPIs disponibles : compteurs catalogue + sante stock
(bandes RG-T21). **Ferme le 404 du landing manager**. KPIs de vente differes P4.
- **Lot U — Users (#38)** : `UserController` + `UserRepository` (mlt domaine 10).
CRUD comptes, desactivation, **anonymisation RGPD** (mlt 10.5, tombstone). Chaque
mutation = PIN equipier + audit (RG-T13/14), throttle (RG-T22). Garde-fous :
pas d'auto-desactivation (403), dernier admin actif protege. Unicite email -> 409.
- **Lot R — RBAC (#39)** : `RoleController` + `RoleRepository` (mlt 10.4). Matrice
roles x permissions (cases scalaires `perm_<id>`) + roles custom (RG-4) +
`role_visible_source`. PIN + audit avec DIFF des permissions (RG-6). Garde admin
(garde `role.manage` + reste actif), code immuable, 409 code dupli.
- **Chore (#40) — Makefile -> `docker compose up`** : service one-shot `wakdo-migrate`
(image mariadb) applique migrations (suivi `schema_migrations`) + seed (suivi
`seeds_applied`) idempotents via `db/migrate-container.sh` (connexion reseau) ;
`wakdo-app`/`web` attendent sa completion. Makefile supprime. Le « une commande »
(Cr 7.c.4) passe de `make init` a `docker compose up`. Cr 7.b porte par les scripts
Bash. Docs realignees + correction CI = **Forgejo Actions** (pas GitHub). Detail :
`docs/journal/2026-06-17--makefile-to-compose-migrate.md`.
- **Chore (#41) — `docker-compose.yml` standalone portable** : le repo ship un compose
qui tourne EN LOCAL sans config (`docker compose up -d` -> http://kiosk.localhost:8080
+ http://admin.localhost:8080), facon app open-source. Reseau interne seul,
`wakdo-web` publie `${HTTP_PORT:-8080}:80`, plus de Traefik dans le fichier versionne,
zero commentaire. Renommage `TRAEFIK_DOMAIN_*` -> **`APP_HOST_KIOSK/ADMIN`** (vhosts
Apache `ServerName`, *.localhost en local). `.env.example` local-first (valeurs dev
qui marchent sans edition). **Prod = `docker-compose.prod.yml` GITIGNORE**, propre a
chaque hote (meme stack + Traefik, sans port hote) : `docker compose -f docker-compose.prod.yml up -d`.
Valide via `docker compose config` (les deux fichiers) ; smoke-test runtime a faire
sur machine propre (container_name fixes -> pas de up parallele a la stack en cours).
- **Doc set socle (#42/#43/#44)** : `docs/ARCHITECTURE.md` (10 sections) + `docs/DEVELOPER.md`
(#42) ; `docs/adr/` (index + 9 ADR : from-scratch, MVC serveur, stock %, PIN+audit,
throttle PIN, 409/422, RGPD anonymise, Makefile->compose, standalone+prod) (#43) ;
`docs/domaines/` (index + 7 fiches : auth, catalogue, stock-recettes, users, rbac,
stats, borne) (#44). Decoupage en 3 PR par cohesion (squash). A pousser/lire sur la Forge.
### Etat tests / CI
unit 263 + integration 301/916 assertions (`WAKDO_DB_TESTS=1`) + 7 tests JS + 2 parcours
E2E (borne + admin, Playwright, lance a la main), PHPStan L6, CI Forgejo verte. Branches
feature nettoyees (local + remote).
### Fait depuis
- **Smoke-test standalone : PASSE** (2026-06-17). Lance dans un projet jetable isole
(`-p wakdotest`, override container_name, port 8099) pour ne pas toucher la stack dev
26h : migrate exit 0 (2 mig + 2 seeds, 5 roles/23 perms/admin), borne `kiosk.localhost`
HTTP 200 (`<title>Wakdo - Bienvenue</title>`), admin `admin.localhost` HTTP 200
(`Wakdo back-office`), healthz 200. Le renommage `APP_HOST_*` marche au runtime. Projet
jetable + volumes/images supprimes ensuite.
- **`.env` LOCAL migre** : `TRAEFIK_DOMAIN_*` -> `APP_HOST_*` (memes valeurs FQDN). Le
`.env` SERVEUR reste a migrer (idem) avant son prochain deploiement.
- **Playwright E2E etape 1 : PASSE** (#45, dev). Parcours borne complet (welcome ->
boissons -> produit -> ajout panier -> panier -> paiement -> confirmation), headless en
conteneur officiel, contre une stack JETABLE isolee (`tests/e2e/run.sh`). A trouve +
corrige un **bug a11y** : `#pay-btn` (un `<a>`) gardait `aria-disabled="true"` car
`.disabled` est un no-op sur un `<a>` -> pilote via `aria-disabled` (commit 2b51be3).
Detail conteneur : hostnames de test en `.test` (Chromium force `*.localhost`->127.0.0.1).
- **Playwright E2E etape 2 : PASSE** (#46, dev). Parcours admin : garde -> login
(admin@wakdo.local / mdp dev seede + CSRF) -> /admin/dashboard -> logout. A revele un
**probleme secu/usage** : cookie de session `secure=true` en dur -> session intenable
en HTTP local -> login admin KO. Corrige en `secure` **conditionnel au HTTPS**
(`SessionManager::cookieSecure`, X-Forwarded-Proto ; prod inchange) -> [[ADR-0010]].
Valide : PHPStan L6, 263 unit, 2 E2E verts.
### File d'attente (decidee avec le user)
1. **Deploiement serveur (Thanos)** : migrer le `.env` serveur (`TRAEFIK_DOMAIN_*` ->
`APP_HOST_*`, memes valeurs ; `HTTP_PORT` inutile en prod), placer son
`docker-compose.prod.yml` (gitignore), back-fill `seeds_applied` avant le 1er up
avec `wakdo-migrate` (sinon re-seed -> conflits). Le `.env` local est deja migre.
2. **Playwright E2E — etape 3 (CI)** : etapes 1 (borne) + 2 (admin) FAITES. Reste le
job CI Forgejo qui monte la stack jetable + lance Playwright en conteneur
(image `mcr.microsoft.com/playwright:v1.49.1-jammy`, doit matcher la devDep). NB : le
runner CI doit pouvoir faire du docker-in-docker / acceder au socket (a verifier).
3. **Front : page de LOGIN a retravailler** (demande user : "la page login est moche").
Initiative UI dediee ; ne PAS confondre avec le dashboard admin (qui, lui, va).
4. **Doc set — suite possible** : diagrammes/captures, ou doc commande quand P4 sort.
Footgun : branches `docs/*` heurtent le dossier `docs/` dans `forgejo-pr-automerge.sh`
(`git log "$HEAD"` ambigu) -> passer le titre en 3e arg.
5. **Reste P4** : domaine commande (KPIs vente, liens nav orders, swap borne->API).
### Reprendre
```bash
cd /home/acadenice/corentin_wakdo
git checkout dev && git fetch forgejo && git merge --ff-only forgejo/dev
docker compose up -d # stack complete en une commande (migrate+seed via wakdo-migrate)
```
---
## Session : 2026-06-17 — P3 Stock + Recettes + Borne allergenes (cycle FD complet, 4 PR mergees)
**Auteur : BYAN.** Cycle FD `20260616-134152-p3-ingredients-stock` mene a terme (reprise a DISPATCH -> BUILD -> VALIDATE). Scope Stock + recettes, etendu en cours a la borne allergenes, livre en 4 PR sequentielles toutes mergees sur `dev` en auto-merge sur CI verte.
### Livre (dev = 1ecd783)
- **PR-0 #33 — Harmonisation HTTP 409** : les reponses de CONFLIT (SQLSTATE 23000 : unicite + hard-delete bloque par FK RESTRICT) passent de 422 a 409 sur Category/Product/Menu (aligne le code sur le contrat `byan-api.md`) ; la validation simple reste 422. IngredientController nait directement en 409.
- **PR-A #34 — Stock ingredients** : `IngredientRepository` (CRUD, stock %/bande calcules, `restock` tx, `inventoryCount` tx ecrivant une ligne meme a delta=0 RG-3, `movements` borne, `isReferenced`) ; `IngredientController` (CRUD `ingredient.manage` sans PIN ; RESTOCK `stock.manage` sans PIN ; INVENTORY_COUNT `stock.count` + PIN equipier, `pin.failed`+throttle en 1 tx, sans `audit_log` au succes RG-T14 ; movements `stock.read`, user_id visible manager/admin RG-4) ; vues `admin/ingredients/*` + lien nav Stock ; hard-delete bloque -> 409.
- **PR-B #35 — Recettes + dispo calculee** : `ProductRepository` (`composition`, `setComposition` delete-and-reinsert tx, `ingredientExists`, `compositionCount`, `autoUnavailableIds`, `isOrderable` RG-T21) ; editeur recette `ProductController::recipeForm/saveRecipe` (`ingredient.manage`, sans PIN) + vue `admin/products/recipe.php` + `product-recipe.js` (vanilla CSP-safe) ; badge "Rupture auto" (RG-T21) dans la liste produits ; **dette #27 fermee** (nb de lignes `product_ingredient` cascade-supprimees trace dans le resume d'audit a la suppression produit).
- **PR-C #36 — Borne allergenes** : icone "i" sur carte ET fiche produit ouvrant une modale GENERALE des 14 allergenes INCO (`allergens.js` CSP-safe, `data/allergens.json`, `loadAllergens()` dans `data.js` avec point de swap P4 -> `/api/allergens`). Premier harnais de tests front du depot : node:test + jsdom (`tests/js/`, 7 tests), job CI `js-tests` (Node 20 epingle via NodeSource).
### Decisions actees (2026-06-17)
- Inventaire = seule action sensible du domaine stock (RG-T13 liste 9.2, hors 8.8 CRUD et 9.1 restock). Succes -> `stock_movement.user_id`, sans `audit_log` (RG-T14). Echec PIN -> `pin.failed` + throttle dans UNE transaction (RG-T22).
- "i" allergenes = info GENERALE (14 INCO), pas un calcul par produit. Mapping `ingredient_allergen` et allergenes calcules par produit restent differes.
- Harnais JS : ESM declare LOCALEMENT (`src/public/borne/assets/js/package.json` + `tests/js/package.json`), racine en CommonJS -> hooks `.claude` / `_byan` / `bin` intacts. (Une 1ere tentative `"type":"module"` a la racine cassait les hooks CommonJS -> corrige avant commit.)
### Etat tests / CI
259 tests PHP / 777 assertions (`WAKDO_DB_TESTS=1`), PHPStan L6 propre ; 7 tests JS (node:test + jsdom). CI `dev` verte : secret-scan, php-lint, static-tests, js-tests.
### A faire (suivi)
- **Gate CI** : ajouter `CI / js-tests (pull_request)` aux checks REQUIS de la branch protection (`scripts/forgejo-branch-protection.sh`). Au merge de #36 l'auto-merge natif (`merge_when_checks_succeed`) n'a pas attendu `js-tests` (pas encore requis) ; il a valide a posteriori sur `dev` (vert). Sans ce gate, une PR au JS rouge pourrait merger.
- **P3 restant** : Users + matrice RBAC ; Stats (ferme le finding `manager.default_route = /admin/stats` 404).
- **Differe** : mapping `ingredient_allergen` + allergenes calcules par produit ; decrement atomique RG-T20 + swap borne JSON->API = domaine commande **P4** (debloque liens nav `orders` #23, purge `ORDER_RETENTION` #25).
- **Hygiene user** : vars `.env` auth/throttle absentes (l'app tourne sur ses defauts code) ; revoquer anciens PAT GitHub.
### Reprendre
```bash
cd /home/acadenice/corentin_wakdo
git checkout dev && git fetch forgejo && git merge --ff-only forgejo/dev
git log --oneline -6 dev
# FD p3-ingredients-stock en phase VALIDATE (a clore en COMPLETED apres validation finale).
```
---
## Session : 2026-06-16 — Audit "du reel" + 13 PR de remediation + Menus (#32) + demarrage Ingredients/stock
### Vue d'ensemble
Session longue, quatre temps :
1. Diagnostic branche `dev` (CI "rouges" + graphe "bizarre").
2. Audit sur pieces du travail du 2026-06-15 (demande : "du reel, pas le journal") via un sweep multi-agents + verification adversariale -> 13 PR de remediation, toutes mergees.
3. Feature **Menus** construite en cycle FD complet (PR #32, mergee).
4. Demarrage du cycle FD **Ingredients/stock** -> EN PAUSE a la phase DISPATCH.
### 1. Diagnostic dev (resolu, rien de casse)
- **"CI pas vertes"** = 8 echecs `auto-merge` cosmetiques (double-fire : un 2e run tente de merger une PR deja mergee -> non-200 ; job non-bloquant) + 3 `static-tests` historiques (PR #9, php-xml/mbstring manquants, corriges le jour meme). `dev` HEAD etait vert.
- **"Graphe bizarre"** = refs de suivi locales non prunees (branches squash-mergees, supprimees cote serveur). `git fetch --all --prune` nettoie. Le squash ne cree pas de 2e parent -> moignons rouges (cosmetique).
### 2. Audit + remediation (PR #19-#31, toutes mergees)
- **CRITIQUE** : durcissement `php.ini` absent du conteneur en service (image perimee) -> **rebuild `wakdo-app`** (runtime, fait + verifie).
- **HIGH** : la CI ne lancait **aucun** test d'integration DB (auto-skip) -> **#21** (service MariaDB + `WAKDO_DB_TESTS=1` + `--fail-on-skipped`).
- **MEDIUM** : XSS kiosk innerHTML #20 ; liens nav morts #23 ; user DB `GRANT ALL` -> moindre privilege #24 ; purge RGPD/throttle cron #25.
- **LOW** : enum email sur reset #26 ; contrat FK suppression produit #27 ; lien page PIN #28 ; bouton mort `PASSWORD_ALGO` #29 ; atomicite chemin echec PIN #30 ; drift JSON borne #31.
- **+** maquettes `.html` non authentifiees retirees #19 ; note d'audit jury `docs/journal/2026-06-16--audit-reel-livrables-p2-p3.md` #22.
- **Incident** : `docker compose up -d <service>` nu a tue le reseau (vars `${...}` absentes du `.env` -> drift) -> recup `down`+`up` sans perte. Memoire `ops-compose-single-service-network` (utiliser `--no-deps --force-recreate`).
### 3. Menus (PR #32, mergee)
CRUD menus composes (mlt 8.4-8.6) : burger de base + slots (`menu_slot`/`menu_slot_option`), prix Normal/Maxi. Slots en **vanilla JS** (champ cache `slots_json`, CSP-safe). `delete` = PIN equipier + audit ; `create`/`update` **sans** PIN ; `update` = delete-and-reinsert des slots. Lien nav Menus reactive. Acces : `menu.create/update` admin+manager, `menu.delete` admin seul, `menu.read` tous.
### Etat Git
`dev = c2a4854` (toutes les PR #19-#32 mergees ; local == forgejo == github). Working tree propre sur `dev`. Branches feature supprimees au merge. **201 tests verts** (597 assertions avec `WAKDO_DB_TESTS=1`), PHPStan L6 propre, CI verte (avec tests d'integration DB).
### 4. EN COURS : FD Ingredients/stock (PAUSE a DISPATCH)
- `fd_id = 20260616-134152-p3-ingredients-stock`, phase **DISPATCH** (voir `byan_fd_status`).
- **Scope decide : Stock + recettes**, livre en 2 PR sequentielles :
- **PR-A Stock** : `IngredientRepository` (CRUD + calcul stock %/bande + `restock` tx + `inventoryCount` tx + registre `stock_movement` + `isReferenced`) ; `IngredientController` (CRUD `ingredient.manage` ; RESTOCK `stock.manage` ; **INVENTORY_COUNT `stock.count` + PIN** ; mouvements `stock.read`) ; vues `admin/ingredients/{index,form,restock,inventory,movements}` + lien nav Stock ; tests (unit + integration + FakeDatabase).
- **PR-B Recettes** : `product_ingredient` (quantite normal/maxi, `is_removable`/`is_addable`, `extra_price_cents`), editeur imbrique facon slots (vanilla JS). **Debloque RG-T21** (dispo produit calculee) + ferme la dette #27 (trace cascade).
- **Decisions actees** : inventaire = PIN + trace `stock_movement.user_id` **SANS** `audit_log` (RG-T14 exclut le stock du double-journal) ; soft-delete `is_active`, hard-delete bloque (FK `product_ingredient`/`stock_movement` RESTRICT -> 422).
- **Modele** : `ingredient` (`stock_quantity` signe ; `stock_capacity>0` = 100% ; `pack_size` ; `low_stock_pct=10`/`critical_stock_pct=5` avec `critical<low`) ; `stock_movement` append-only (`sale`/`cancellation`/`restock`/`inventory_correction`).
- **Permissions** : `ingredient.manage` + `stock.manage` = admin+manager ; `stock.count` + `stock.read` = tous les roles.
- **A LA REPRISE** : dire **"go PR-A"** -> chunk 1 = `IngredientRepository` + `IngredientRepositoryDbTest` (meme decoupe que Menus, chaque chunk verifie).
### A faire (apres le stock)
- **P3 restant** : Users + matrice RBAC ; Stats (ferme le finding `manager.default_route = /admin/stats` 404).
- **Differe** : allergenes `ingredient_allergen` ; affichage dispo calculee RG-T21 (vient avec PR-B) ; domaine commande **P4** (debloque les liens nav `orders` #23, la purge `ORDER_RETENTION` #25).
- **Hygiene cote user** : les vars auth/throttle (`ARGON2_*`, `ACCOUNT_LOCKOUT_*`, `PIN_THROTTLE_*`, ...) sont absentes du `.env` reel (l'app tourne sur ses defauts code) ; revoquer les anciens PAT GitHub.
### Reprendre
```bash
cd /home/acadenice/corentin_wakdo
git checkout dev && git fetch forgejo && git merge --ff-only forgejo/dev
git log --oneline -6 dev
# FD en cours : phase DISPATCH. Dire "go PR-A" pour reprendre le build (IngredientRepository).
```
---
## Session : 2026-06-15 (suite 5) — P3 : throttle PIN (RG-T22, #18) merge
### Vue d'ensemble
Fermeture du finding HIGH de la revue Produits (PIN d'action sensible sans limitation de tentatives).
Conception via un panel multi-agents (3 lentilles Ockham / efficacite-menace / anti-DoS -> synthese ->
passe adversariale, `holds = true`), construction en TDD avec les correctifs de l'adversaire integres,
revue adversariale de l'implementation (`holds = true`), puis merge sur `dev` en auto-merge (PR #18).
**Resume de session detaille (format jury RNCP) : `docs/journal/2026-06-15--p3-throttle-pin-rg-t22.md`.**
### Decision actee
Compter les echecs de PIN **par utilisateur AGISSANT** (`$guard->userId`), pas par email cible
(contournable par rotation) ni par IP (collateral sur poste a session partagee). Table dediee
`pin_throttle` (entite 22) cle sur `actor_user_id` (FK ON DELETE CASCADE), separee des compteurs de
connexion : un echec de PIN n'incremente aucun compteur de login (pas d'escalade DoS). Bornes propres
(PIN_THROTTLE_*, base 30s / plafond 300s, plus permissives que le login : controle de dissuasion).
### Ce qui a ete livre (merge ; PR #18 ; dev = ad5203d)
- `App\Auth\PinThrottle` (isLocked / recordFailure upsert atomique + backoff / reset), dimension `'pin'`
de `ThrottlePolicy`, `PinVerifier::payTimingDecoy` (parite de timing), cablage `ProductController`
update/destroy (gate avant verification, pas de `pin.failed` sous verrou actif = anti-flood, reset sur
l'acteur de session).
- Migration `0002_pin_throttle.sql` (appliquee a la base dev) ; docs Merise 21 -> 22 entites (RG-T22) ;
`.env.example` + `docker-compose.yml` (PIN_THROTTLE_*).
- 188 tests verts (525 assertions), PHPStan L6 propre. Branche supprimee (local + remote).
### Etat Git
```
dev ad5203d (auth #11 ... produits #17 + throttle PIN #18 ; local == forgejo == github)
Hors-scope untracked : package*.json, SESSION_RESUME.md, journal P1 2026-06-04.
```
### A faire a la reprise
- Suite P3 : Menus (+ slots), Ingredients/stock, Users + matrice RBAC, Stats.
- Differe : etendre le cron de purge a `pin_throttle` (predicat dans `mlt.md` 13.5) ; alerte de volume
`pin.failed` cote supervision ; seed ingredients/recettes ; revoquer anciens PAT GitHub.
### Reprendre
```bash
cd /home/acadenice/corentin_wakdo
git checkout dev && git fetch forgejo dev && git merge --ff-only forgejo/dev
git log --oneline -8 dev
```
---
## Session : 2026-06-15 (suite 4) — P3 : CRUD Produits (#17)
### Vue d'ensemble
Suite directe de la session P3. Le CRUD Produits est livre et merge (PR #17, squash sur `dev`),
memes patterns que Categories : `App\Catalogue\ProductRepository` sur `DatabaseInterface`,
`ProductController` non-`final`, double `FakeDatabase`, tests unit + integration auto-skippee,
revue adversariale par chunk. C'est le **premier usage reel du PIN d'action sensible** (RG-T13)
sur des mutations metier, avec ecriture `audit_log` dans la meme transaction (RG-T08 / RG-T14).
### CRUD Produits (merge ; PR #17 ; dev = 2756fb4)
- `App\Catalogue\ProductRepository` (DatabaseInterface) : all/find/create/update/delete +
`categoryExists`, allowlist de colonnes a l'ecriture (RG-T16).
- `ProductController` (index/create/store/edit/update/confirmDelete/destroy) : chaque action
`guard('product.manage')` (RG-T03), mutations CSRF (RG-T01), validation serveur bornee
(RG-T18 : categorie existante, nom <=120, prix > 0 et <= UINT32, TVA dans {55,100}, image <=255,
display_order 0..65535).
- **PIN conditionnel (8.2 RG-4)** : a l'update, le PIN equipier est exige uniquement si `price_cents`
OU `vat_rate` change (les deux sont fiscalement sensibles : sur_place 10% vs a_emporter 5.5%) ;
sinon write simple sans PIN. A la suppression, le PIN est exige dans tous les cas.
- **Modele PIN = identifiant equipier + PIN** : `PinVerifier::resolveActingUser(email, pin)` (ajoute
ce chunk) resout l'identite de l'equipier qui valide (SELECT filtre `is_active = 1`), et c'est
cet `acting_user_id` (pas l'utilisateur de session) qui est ecrit dans `audit_log` (RG-T14), dans
la MEME transaction que la mutation (RG-T08).
- **Suppression FK-safe** : hard delete seulement si le produit n'est pas reference (order_item /
menu.burger_product_id, FK RESTRICT) ; sinon `PDOException` SQLSTATE 23000 traduit en 422.
- Vues `admin/products/{index,form,delete}`, ajout des routes dans `src/public/admin/index.php`.
- 172 tests verts (452 assertions), PHPStan L6 propre.
### Revue : 6 findings confirmes (0 refute)
- **HIGH — PIN d'action sensible sans limitation de tentatives.** `PinVerifier` verifie avec parite
de timing (leurre argon2id) mais sans compteur ni backoff : un insider tenant deja `product.manage`
peut iterer les PIN (4 chiffres = 10^4) contre l'email d'un collegue et forger l'`acting_user_id`
de l'audit. Le login a son `login_throttle` (RG-8) ; le PIN n'a pas d'equivalent. **Mitigation
shippee dans ce chunk** : chaque PIN refuse ecrit un `audit_log` `pin.failed` (acteur NULL, cible,
email tente) -> la tentative devient detectable et alertable. Le throttle PIN degressif complet
reste a faire (chunk dedie, cf. plus bas).
- **LOW — case `is_available`** : sur re-rendu d'erreur, une case decochee revenait cochee (fallback
`?? 1` applique aussi au re-rendu). Corrige : la valeur ne retombe sur le defaut que pour le
formulaire de creation, le re-rendu reflete l'etat soumis.
- **4 MEDIUM = lacunes de couverture / resistance aux mutations** (le code de prod etait correct, les
correctifs durcissent les tests) :
- chemin TVA-only (vat 55) non teste -> ajout `testUpdateVatChangeRequiresPin` +
`...WithValidPinAudits`.
- route `resolveActingUser` du `FakeDatabase` ne forcait pas `is_active = 1` (un mutant retirant le
predicat passait) -> route durcie + assertion SQL.
- test de suppression n'assertait pas que l'acteur audit = l'equipier resolu -> assertions actor
(uid === 9, rid === 4) ajoutees sur update et destroy.
- `FakeDatabase` ne prouvait pas que l'INSERT `audit_log` tombe ENTRE begin et commit (deux listes
disjointes) -> ajout d'un `eventLog` entrelace + assertions d'ordre (begin < audit < commit).
### Etat Git (post-chunk)
```
dev 2756fb4 (auth #11 + RBAC #12 + PIN #13 + shell #14 + categories #15 + set-PIN #16 + produits #17 ; == forgejo/dev)
Branche feat/p3-products-crud supprimee (remote auto-merge + local). 172 tests, PHPStan L6 propre.
Hors-scope untracked : package*.json, SESSION_RESUME.md, journal P1.
```
### A faire a la reprise — d'abord le throttle PIN (chunk dedie), AVANT Menus / Stock
- **Throttle PIN d'action sensible (chunk dedie, prioritaire)** : fermer le finding HIGH. Decision de
schema a trancher d'abord : ne PAS reutiliser les colonnes `user.failed_login_attempts` /
`lockout_until` du login (un attaquant qui spamme le PIN d'une victime verrouillerait son LOGIN =
DoS sur la victime). Pistes : compteur PIN dedie (colonnes `pin_failed_attempts` /
`pin_lockout_until` sur `user`, OU table `pin_throttle`), dimension per-user ET per-IP (comme RG-8),
reponse generique (ne pas reveler quels emails existent), reset sur succes. Reutilise
`ThrottlePolicy`. Ajouter une RG Merise dediee + migration + tests. La mitigation `pin.failed` reste
en place entre-temps.
- Ensuite : Menus (+ slots), Ingredients/stock, Users + matrice RBAC, Stats.
- Differe : seed ingredients/recettes ; revoquer anciens PAT GitHub.
### Reprendre
```bash
cd /home/acadenice/corentin_wakdo
git checkout dev && git fetch forgejo dev && git merge --ff-only forgejo/dev
git log --oneline -7 dev
```
---
## Session : 2026-06-15 (suite 3) — P3 : shell (#14) + CRUD Categories (#15) + set-PIN (#16)
### Vue d'ensemble
P3 (CRUD admin) demarre. Architecture tranchee : **pages rendues serveur (PHP MVC)**, pas d'API
JSON pour le back-office (coherent avec l'auth, demontre le MVC). Routes back-office sous `/admin/...`
(le seed fixe `role.default_route` a `/admin/dashboard`, `/admin/stats`, etc.). Deux chunks merges,
memes patterns que P2 (services/repos sur `DatabaseInterface`, controleurs non-`final`, `FakeDatabase`,
tests unit + integration auto-skippee, revue adversariale par chunk).
### Shell admin (merge ; PR #14)
- `Controller` gagne un hook `layoutName()` (defaut inchange) ; le back-office rend dans `admin/layout`.
- `AdminController` (base) : `guard(permission?)` = RG-6/RG-T02 (302 `/login` si session KO) puis RG-T03
(403 si permission manquante), sinon `GuardResult` ; `adminView()` injecte identite + permissions +
CSRF + flash ; helpers `setFlash`/`takeFlash`.
- `DashboardController` -> `GET /admin/dashboard` ; `UserDirectory` (nom + libelle role topbar).
- Vues `admin/{layout,dashboard,forbidden}` : nav conditionnee aux permissions, logout POST CSRF,
sorties echappees (RG-T15). Premier cablage reel du `SessionGuard` sur une page.
- Revue : 5 findings corriges (initiales multibyte, couverture 403, test d'echappement, marqueur de
fragment, commentaire d'heritage).
### CRUD Categories (merge ; PR #15) — pattern de reference
- `App\Catalogue\CategoryRepository` : couche d'acces des entites CRUD (all/find/create/update/
setActive/nameExists/slugExists), testable via `DatabaseInterface`.
- `CategoryController` (index/create/store/edit/update/toggle) : chaque action `guard('category.manage')`
(RG-T03), mutations CSRF (RG-T01) + validation serveur (RG-T18 : requis/format/bornes/unicite,
display_order 0..65535) + allowlist de colonnes (RG-T16). Pas de suppression dure (FK RESTRICT) :
bascule `is_active`. Violation d'unicite concurrente traduite en 422. Flash apres redirection.
- Vues `admin/categories/{index,form}` + `admin/not_found`. Revue : 6 findings corriges (borne
display_order, violation unique -> 422, +4 tests de regression).
### Set-PIN self-service (merge ; PR #16)
- `ProfileController` -> `GET/POST /admin/profile/pin` : l'utilisateur connecte definit/change SON
propre PIN (cible = `guard.userId` de session, pas un champ de form -> pas d'IDOR). CSRF +
validation serveur (numerique + bornes via `meetsLengthPolicy`, confirmation). Hash argon2id.
Ecriture gardee sur 1 ligne affectee (pas de faux succes). `UserRepository` : `setPinHash`/`pinIsSet`.
- **Decision PIN actee** : modele **identifiant equipier + PIN** pour les actions sensibles (attribution
individuelle). `PinVerifier::resolveActingUser(email, pin)` reste a ajouter en chunk Produits.
- E2E : l'**admin a un PIN dev = `4729`** en base (pose via la page), ce qui debloque le gate Produits.
- Revue : 3 findings corriges (fallback `?? 0` retire, gate ligne affectee, assertion cible = session).
### Etat Git (post-chunks)
```
dev f63ac98 (auth #11 + RBAC #12 + PIN #13 + shell #14 + categories #15 + set-PIN #16 ; == forgejo/dev)
152 tests (unit + integration DB auto-skippee), PHPStan L6 propre. Branches feature supprimees.
Hors-scope untracked : package*.json, SESSION_RESUME.md, journal P1.
```
### Pattern CRUD admin (a reutiliser pour produits/menus/users)
1. `App\Catalogue\<Entite>Repository` (DatabaseInterface) : requetes preparees, unicite.
2. `<Entite>Controller extends AdminController` : par action -> `guard(<permission>)` -> (mutation :
`Csrf::validate`) -> validation serveur + allowlist -> repo -> redirect + `setFlash`, ou re-rendu 422.
3. Vues `admin/<entite>/{index,form}` rendues dans le shell ; tout texte echappe.
4. Actions sensibles (RG-T13) -> `PinVerifier` + `audit_log` (RG-T14) dans la meme transaction.
5. Pas de suppression dure si FK RESTRICT -> desactivation.
6. Tests : controleur (double via hooks), repository (integration DB auto-skippee).
### A faire a la reprise
- **CRUD Produits (chunk en cours)** : cas riche (price_cents, vat_rate {55,100}, category_id FK,
is_available, image, display_order). **Premier usage reel de `PinVerifier`** : PIN exige uniquement
si prix/TVA change (8.2 RG-4) et a la suppression (8.3), modele **identifiant equipier + PIN** via
`PinVerifier::resolveActingUser(email, pin)` (a ajouter) -> `acting_user_id` ecrit dans `audit_log`
dans la meme transaction (RG-T14). Suppression dure seulement si non reference (FK RESTRICT depuis
order_item / menu.burger_product_id) sinon 422. Reutilise les bornes d'entiers durcies (revue categories).
- Ensuite : Menus (+ slots), Ingredients/stock, Users + matrice RBAC, Stats.
- Differe : seed ingredients/recettes ; revoquer anciens PAT GitHub.
---
## Session : 2026-06-15 (suite 2) — RBAC P2 (PR #12) + moteur PIN (PR #13), tous deux merges
### Vue d'ensemble
Poursuite directe de la session auth. RBAC livre et merge (PR #12), puis moteur PIN d'action
sensible construit (commit/PR en attente apres revue). Memes patterns que l'auth : services
dependant de `DatabaseInterface`, controleurs non-`final` pour le seam de test, double
`FakeDatabase`, tests unit + integration auto-skippee, revue adversariale par panel.
### RBAC (merge ; dev = f979a23 ; PR #12)
- `Authorizer` : `can(role_id, code)` / `permissionsFor(role_id)` / `roleCode(role_id)` ; verifie une
PERMISSION (RG-T03), rechargee depuis la base a chaque appel (10.4 RG-3) ; un role `is_active = 0`
ne confere rien.
- `AuthenticatedController` (dans `App\Controllers`, pas le Core, pour ne pas inverser la dependance) :
cable `SessionGuard` (RG-6/RG-T02) + `Authorizer`.
- `MeController` -> `GET /api/me` : `401 AUTH_REQUIRED` si session absente/expiree/inactive, sinon
identite + permissions. **Premier consommateur reel du `SessionGuard`** (enfin cable).
- Revue : 3 findings corriges (`roleCode` ne filtrait pas `is_active` ; couverture du predicat
`is_active` via `FakeDatabase.roleActive` + un test d'integration `AuthorizerDbTest` contre le vrai
schema ; assertions de liaison par code de permission). 1 refute (le `SessionGuard` ne relit pas
`role_id` en cours de session = by-design 10.4 RG-3 : la session stocke `role_id`, seules les
permissions se rechargent).
### PIN (merge ; PR #13)
- `PinVerifier` : `verify(user_id, pin)` contre `user.pin_hash` (argon2id, default-deny, filtre
`is_active = 1`) ; `meetsLengthPolicy` (chiffres ASCII, bornes min/max `STAFF_PIN_MIN/MAX_LENGTH`,
RG-T18). Primitif RG-T13 reutilise par chaque operation sensible en P3.
- 119 tests verts (unit + integration, dont la garde du filtre `is_active` contre le vrai schema),
PHPStan L6 propre.
- Revue : 3 findings corriges (decoy argon2id sur le chemin sans PIN pour egaliser le timing ;
politique de longueur durcie `ctype_digit` + max ; couverture du filtre `is_active`). 2 refutes
(throttle PIN = concern orchestrateur P3 ; cast `(string)` redondant inoffensif).
- Perimetre P2 = le primitif seul. Les operations sensibles gardees (annulation, prix/TVA,
suppressions, correction d'inventaire, gestion user/RBAC, effacement PII), la definition d'un PIN
(set/change) et le cablage PIN + `audit_log` dans la meme transaction sont P3 (flux deja specifie
dans `docs/uml/security-sequence.md` pour CANCEL_ORDER).
### Etat Git
```
dev 7c35f8e (auth PR #11 + RBAC PR #12 + PIN PR #13 ; == forgejo/dev ; push-mirror -> GitHub)
Branches feature supprimees (remote auto-merge + local). Working tree sur dev, propre.
Hors-scope untracked : package*.json, SESSION_RESUME.md, journal P1
```
### A faire a la reprise
- **P3 CRUD admin** : les pages admin deviennent des vues rendues serveur passant par
`AuthenticatedController`. Chaque operation cable alors `SessionGuard` + `Authorizer::can(permission)`
+ `PinVerifier` (actions sensibles) + ecriture `audit_log` dans la meme transaction (RG-T08/RG-T14).
C'est en P3 que `SessionGuard` / `Authorizer` / `PinVerifier` sont reellement appliques aux operations,
et que les pages statiques (`.html` servies par Apache) deviennent protegeables.
- **Modele d'attribution PIN sur poste partage** a trancher en P3 : PIN seul (identifie l'individu)
vs identifiant equipier + PIN. Le primitif `verify(user_id, pin)` supporte les deux.
- Differe : seed ingredients/recettes ; revoquer les anciens PAT GitHub.
---
## Session : 2026-06-15 (suite) — P2 Auth back-office mergee (PR #11) + doc API + convention snake_case
### Vue d'ensemble
Livraison de la couche **authentification back-office** (P2), conçue via un panel de design
(3 architectes : testabilite / securite / simplicite) puis durcie par une revue adversariale
(6 findings confirmes corriges). Mergee sur `dev` en auto-merge sur CI verte (PR #11, squash =
`1b0b20c`). En bonus : doc API et arbitrage de la convention de nommage.
### Ce qui a ete fait
- **Prerequis compose** : `ARGON2_*`, `ACCOUNT_LOCKOUT_*`, `IP_THROTTLE_*`, `STAFF_PIN_MIN_LENGTH`,
`PASSWORD_RESET_TTL` passes au conteneur `wakdo-app` (deja dans `.env.example`).
- **Core etendu (additif)** : `Request::formBody()` (POST urlencode) + `Request::clientIp()`
(IP reelle = dernier hop `X-Forwarded-For` derriere Traefik, validee, repli `REMOTE_ADDR`) ;
`Database::transaction(callable): void` (atomicite RG-T08) + `DatabaseInterface` (seam de test) ;
accesseurs lecture `Response` (`body`/`header`/`headers`).
- **Domaine `App\Auth`** : `AuthService` (12.1 login + 12.2 logout), `PasswordResetService` (12.3),
`SessionGuard` (RG-6 + RG-T02 — **ecrit et teste mais PAS encore cable** : c'est le travail RBAC),
`ThrottlePolicy` (backoff degressif), `PasswordHasher` (argon2id + leurre de timing en cache
statique process), `Csrf` (synchroniseur + rotation), `SessionManager` (seul a toucher
`$_SESSION`/cookie, mode test memoire), `Mailer`/`LogMailer` (lien reset journalise, pas de SMTP
en P2), `AuthResult`/`GuardResult`.
- **Controllers + vues + routes** : `AuthController` + `PasswordResetController` (non-`final` pour
le seam de test), vues serveur `auth/{login,forgot,reset}.php` (CSRF cache, `htmlspecialchars`),
7 routes sur le vhost admin (`/login`, `/logout`, `/forgot_password`, `/reset_password`, ...).
- **Tests** : 98 verts (unit sans DB + integration DB auto-skippee). PHPStan L6 propre.
- **Doc API** : `docs/api/conventions.md` (conventions + listing des endpoints, en service + projete).
### Decisions actees
- **Login = vue PHP rendue serveur** (form POST + 302 vers `role.default_route`), pas une API JSON :
le back-office est du MVC serveur.
- **Convention de nommage des URL = minuscule + snake_case** (apres challenge utilisateur + fact-check :
le minuscule est source RFC 3986, aucun standard n'impose `-` vs `_`, la coherence avec la DB et les
champs JSON tranche pour `_`). Routes : `/forgot_password`, `/reset_password` ; `/api/<ressource>` au
pluriel snake (`/api/order_items`). Les champs JSON suivent le dictionnaire (snake_case).
- **Bug attrape en E2E** : `PDO::ATTR_EMULATE_PREPARES = false` interdit de lier un meme placeholder
nomme plusieurs fois dans une requete -> placeholders distincts.
- **Corrections de revue** : parite du profil d'ecritures sur email inconnu (`UPDATE` no-op sur `id = 0`,
anti-enumeration) ; increment du compteur IP `login_throttle` rendu atomique cote SQL (anti
lost-update) ; leurre argon2id mis en cache statique (parite de timing sans surcout par requete).
### Boucle de verification locale (pas de PHP sur l'hote)
```bash
# unit + analyse (image PHP du projet, conteneur jetable) :
docker run --rm -v "$PWD":/app -w /app wakdo-wakdo-app php phpunit.phar -c phpunit.xml
docker run --rm -v "$PWD":/app -w /app wakdo-wakdo-app php -d memory_limit=-1 phpstan.phar analyse --no-progress --error-format=raw
# integration DB (auto-skip sans le flag) :
docker run --rm --network wakdo_wakdo_internal --env-file .env -e WAKDO_DB_TESTS=1 -v "$PWD":/app -w /app wakdo-wakdo-app php phpunit.phar -c phpunit.xml
```
Les `*.phar` (phpunit 11.5.2 / phpstan 1.12.27) sont gitignores ; les telecharger si absents.
### Etat Git (post-session)
```
dev 1b0b20c (P2 auth merge, == forgejo/dev ; push-mirror -> GitHub auto)
feat/p2-auth : supprimee cote remote par l'auto-merge ; branche locale stale (supprimable)
Working tree : hors-scope habituels untracked (package*.json, SESSION_RESUME, journal P1)
```
### A faire a la reprise — RBAC (demarre juste apres cette mise a jour)
- **Service d'autorisation** : `can(role_id, permission_code)` via `role_permission` JOIN `permission`,
recharge depuis la DB a chaque verification (mct/mlt 10.4 RG-3 ; la session ne stocke que `role_id`).
Verifie une **permission**, pas un nom de role (RG-T03).
- **Cabler `SessionGuard::check()`** en tete des routes protegees (idle/absolu/`is_active`), avec
redirection vers `/login` si invalide. En P2 il n'y a pas encore de route protegee : on en cree une
reelle pour brancher et tester la chaine (ex. `/` devient le landing authentifie, et/ou `/api/me`).
- **PIN actions sensibles** (RG-T13) sur le sous-ensemble sensible : annulation, prix/TVA, RBAC,
gestion utilisateur, correction d'inventaire (cf. `docs/uml/security-sequence.md`).
- S'appuie sur le seed (5 roles + 23 permissions + matrice deja en base).
---
## Session : 2026-06-15 (P1 conception finie + traduction FR + P2 demarre : DB + seed + Core)
### Vue d'ensemble
Session marathon, trois phases :
1. **Pivot security-by-design boucle (A->G)** : threat model, infra/config secu, CI/CD Forgejo Actions,
diagrammes MCD + MLD. **P1 conception terminee.**
2. **Traduction FR** de la prose des 5 docs Merise (identifiants/code inchanges), via 2 workflows.
Convention projet : **FR ASCII (sans accents)**.
3. **P2 demarre (le code !)** : DDL 21 tables + runners migrations/seed, seed data, Core PHP from-scratch
(squelette MVC, namespace `App\` -> `src/app/`). Tout merge sur `dev`, identique Forgejo == GitHub.
### P2 back squelette (en cours, deja merge sur dev)
- **DB foundation** (PR #6) : `db/migrations/0001_init_schema.sql` (21 tables, verifie executable sur
MariaDB reel), `db/migrate.sh` + `db/seed.sh` (idempotents), `make migrate`/`make seed` cables.
- **Seed** (PR #8) : 5 roles + 23 permissions + matrice (manager = `user.read` lecture seule) + 14
allergenes INCO + 1 admin (argon2id, `admin@wakdo.local` / `WakdoAdmin2026!` dev) + catalogue
(9 cat / 53 produits / 13 menus + composition). **Differe** : ingredients/recettes (couche stock, P3).
- **Core PHP** (PR #9 + #10) : `src/app/{Core,Controllers,Views}` (App\ -> src/app), front controller
`src/public/admin/index.php`. Autoloader PSR-4, Config getenv, Database PDO (prepared, lazy), Router,
HealthController (`GET /api/health` -> DB reelle, categories:9). PHPUnit (.phar) + PHPStan L6, sans Composer.
### Ce qui a ete fait (lots du pivot SbD)
- **A** threat model STRIDE + classification 4 niveaux (`PROJECT_CONTEXT SS19` + `security-sequence.md`).
- **B** `PROJECT_CONTEXT` : drift GitHub->Forgejo Actions corrige, couche secu integree, planning
rechiffre (P0 22 / P1 38 / P7 22 ; **total 272h**), decision #16 SbD.
- **C** infra/config : `.env.example` (argon2id/lockout/throttle IP+compte/retention RGPD),
`php.ini` durci (allow_url_*off, cgi.fix_pathinfo=0, IDs session, disable_functions RCE),
`.gitleaks.toml`, `scripts/forgejo-branch-protection.sh`, doc runner. **act_runner enregistre**.
- **D** `SECURITY.md`, checklist secu dans PR template, **`.forgejo/workflows/ci.yml`**
(secret-scan gitleaks + php-lint + static-tests PHPStan/PHPUnit *gardes* jusqu'a P2),
`scripts/forgejo-pr-automerge.sh`. Auto-merge **teste et valide** (PR #2, #3, #4).
- **E** diagrammes MCD : 4 sous-domaines en Mermaid + SVG (`mcd-{catalogue,ingredients-stock,order,rbac}`),
vieux `.drawio` v0.1 supprimes, `mcd.md` SS3/SS11 reecrites.
- **F** PR #3 `feat/p1-conception -> dev` **auto-mergee sur CI verte** (squash), branche supprimee.
- **G** **push-mirror Forgejo->GitHub** configure+teste (sync auto a chaque commit) ; historique
git verifie **sans secret** (gitleaks + scan manuel). Reste cote user : revoquer anciens PAT GitHub (Session 6).
- **+** diagrammes **MLD** : 4 sous-domaines relationnels (Mermaid+SVG, `mld-*`), PR #4 auto-mergee.
### Etat Git (post-session) — tout aligne local == Forgejo == GitHub
```
main 5104040 (derniere release)
dev c8f5370 (P1 conception FR + P2 : DB foundation + seed + Core skeleton src/app)
Code applicatif : src/app/{Core,Controllers,Views} (App\ -> src/app), front src/public/admin/index.php
Branches locales : dev, main (aucune feature en cours). Working tree : hors-scope BYAN + SESSION_RESUME (local)
```
### Infra / forge / CI (a retenir)
- **`git.acadenice.com` = Thanos, PROD** (serveur distinct 72.61.105.12). NE PAS confondre avec
le forgejo local de stark `git.stark.a3n.fr`. Voir memoire `project_forge_topology`.
- **CI Forgejo Actions** activee au niveau instance (`FORGEJO__actions__ENABLED=true`, fait par le user).
Runner `forgejo-runner-wakdo` tourne sur stark, pointe la prod. **Apres restart du forgejo prod
-> `docker restart forgejo-runner-wakdo`** (sinon boucle 404).
- **CI static-tests = SANS Composer** : phpstan.phar + phpunit.phar epingles. Le runner (node:20-bookworm,
PHP 8.2) installe `php-xml php-mbstring` (sinon PHPUnit manque dom/mbstring) — deja dans ci.yml.
- **Auto-merge OPT-IN par label `auto-merge`** : le gate verifie le label EN SHELL via l'API
(`GET issues/PR/labels` + grep), PAS via `if: contains(labels...)` de Forgejo (NON fiable : avait merge
PR #9 sans label -> corrige PR #10). PR verte + label -> fusionne ; PR verte sans label -> reste ouverte.
- **Required checks (dev+main)** : les 3 contextes precis `CI / {secret-scan,php-lint,static-tests}
(pull_request)` (pas le glob `CI / *` qui incluait le job auto-merge skippe). Push-mirror -> GitHub auto.
### En attente du user
- **Relecture des diagrammes** MCD + MLD (4+4 sous-domaines) — le user lisait les docs a la pause.
Si un layout ne plait pas, ajuster via une petite PR.
- Decision : auto-merger directement ou faire valider les diagrammes avant merge la prochaine fois.
### A faire a la reprise (P2 suite)
- **Auth** : sessions PHP + argon2id (login), regeneration d'ID a la connexion, idle 4h / absolute 10h.
**Prerequis** : passer les vars `ARGON2_*` / `SESSION_*` / lockout / throttle au conteneur wakdo-app
(bloc `environment:` du `docker-compose.yml`) — PAS encore fait (seules DB_*/APP_*/SESSION_*/CORS_* y sont).
- **RBAC** : middleware de permissions (teste une permission, pas un nom de role), protection routes admin
+ PIN pour actions sensibles. S'appuie sur le seed (roles/permissions/matrice deja en base).
- **Seed ingredients/recettes** (differe) : decisions de modelisation (quels ingredients, quantites) avec le user.
- **Hygiene** : revoquer les anciens PAT GitHub (Session 6).
### Reprendre
```bash
cd /home/acadenice/corentin_wakdo
git checkout dev && git fetch forgejo dev && git merge --ff-only forgejo/dev
git log --oneline -6 dev
```
---
## Session 2026-06-12 (relecture security-by-design + modele stock en % + lot A threat model)
> Note : ce doc avait derive (fige a Session 7, 2026-05-21). Entre-temps : v0.2 prod-like
> 19 entites + migration Forgejo (2026-06-04, voir `docs/journal/2026-06-04--*.md`) puis
> demarrage du pivot security-by-design (2026-06-11). Le bloc ci-dessous repart de l'etat reel.
### Vue d'ensemble
Consolidation du pivot security-by-design sur `feat/p1-conception`. Trois temps :
1. **Relecture des 3 decisions securite** (RGPD, oversell, brute-force) : les 3 directions
validees. Le challenge a fait emerger une simplification (oversell) et un upgrade (stock %).
2. **Change-set applique aux 5 docs Merise** via 1 workflow (5 editeurs paralleles + 4 critiques
adversariaux), puis correctifs manuels des residus rates par les critiques.
3. **Lot A ecrit** via 1 workflow (2 auteurs + 2 critiques) : threat model + classification dans
`PROJECT_CONTEXT §19`, et `docs/uml/security-sequence.md`.
### Decisions actees cette session
- **Stock en vrais %** : `ingredient` porte `stock_capacity` (INT, CHECK > 0 = reference 100% +
garde anti div/0), `low_stock_pct` (def 10), `critical_stock_pct` (def 5) ; `stock_quantity`
signe (peut etre negatif). `stock_pct = ROUND(stock_quantity/stock_capacity*100)` calcule.
3 bandes : normal / alerte (le manager retire via `is_available` OU re-stocke) / auto-OOS
sous le critique.
- **Disponibilite produit CALCULEE** (RG-T21) : commandable ssi `is_available=1 AND chaque
ingredient non-retirable > critical`. Pas de cascade ni flag ; retrait manuel = override dur.
- **`login_throttle` = table** (entite 21) : throttle brute-force per-IP, en plus du compteur
per-compte sur `user`.
- **Drop `SELECT ... FOR UPDATE`** : decrement stock = `UPDATE ... stock = stock - :units`
atomique (plus de read-gate, plus de risque deadlock). `idempotency_key` conserve.
- **Drift MCD §5.1 corrige** : `product_ingredient.quantity` -> `quantity_normal`/`quantity_maxi`.
### Etat Git (post-session)
```
Branche : feat/p1-conception
5 commits POSES cette session (non pushes - le push partira avec la PR) :
fadf0bd DICT - SbD data layer, 21 entities
a1692b6 MCD - SbD entities + % stock model, drift fix
14348ba MLD - audit_log + login_throttle tables, % stock columns
0f57a44 MCT - SbD operations, PIN gating, computed availability
5c34f6b MLT - RG-T13-T21, atomic decrement, throttle, RGPD
Working tree UNCOMMITTED (tenu volontairement) :
docs/PROJECT_CONTEXT.md (§19 threat model + classification ECRIT ; tenu car lot B
va aussi toucher §7/§11)
docs/uml/security-sequence.md (NOUVEAU, lot A, fini, verifie)
docs/uml/{sequence-passer-commande,state-commande,use-cases}.md (drift-fix v0.2, pret,
a grouper avec security-sequence.md)
+ journal 2026-06-04 untracked ; hors-scope BYAN (.claude, Makefile, docker-compose, package*)
```
### A faire a la reprise (ordre)
- **B** : `PROJECT_CONTEXT §7/§11` (retirer « MVP », 21 entites, rechiffrer planning) ->
puis commit PROJECT_CONTEXT (§19 + §7/§11) + les 4 docs UML.
- **C** : infra/config secu (`.env.example` argon2/lockout/retention/seuils, `php.ini` durci,
secret-scan, ruling branch-protection).
- **D** : SDLC (`SECURITY.md`, checklist PR template, jobs CI Forgejo PHPStan/secret-scan).
- **E** : regenerer les drawio MCD/MLD pour 21 entites (les `.md` sont en Mermaid inline).
- **F** : clore -> PR `feat/p1-conception -> dev` (base = `dev` !), rafraichir ce doc.
- **G** : roter le token Forgejo expose ; decider push-mirror Forgejo->GitHub.
### Reprendre
```bash
cd /home/acadenice/corentin_wakdo
git branch --show-current # feat/p1-conception
git log --oneline -6 # voir les 5 commits SbD
git status --short # PROJECT_CONTEXT + UML uncommitted
```
---
## Session precedente : 2026-05-21 (Session 7 - P1 conception : 5 docs Merise/UML + machine a etats commande unifiee)
### Vue d'ensemble
Session centree sur la reprise de P1 conception. Trois resultats :
1. **Rapatriement P1 sur `feat/p1-conception`** : les fichiers P1 (mcd.md + diagrammes) flottaient dans le working tree de la branche demo `demo/p5-front-and-p3-admin`. Portes proprement sur `feat/p1-conception` (stash des fichiers BYAN hors-scope, switch, pop). Les 4 `.drawio` y etaient deja commites (`64f5a27`), identiques au working tree.
2. **5 docs P1 produits** via 2 agents (expert-merise-agile + architect), puis normalises : MCT, MLT, use-cases, state-commande, sequence-passer-commande.
3. **Machine a etats de commande unifiee** : resolution d'une contradiction qui preexistait entre `dictionary.md`, `PROJECT_CONTEXT.md` et le brief initial. Vocabulaire desormais coherent sur toute la doc.
### Etat Git actuel (post-Session 7)
```
Repo : /home/acadenice/corentin_wakdo/
Branche courante : feat/p1-conception (rien commite cette session)
Working tree P1 (untracked, a commiter apres validation drawio) :
docs/merise/mcd.md (MCD, refs encore vers SVG Mermaid - cf. pivot drawio non termine)
docs/merise/mct.md (NEW - 24 operations metier)
docs/merise/mlt.md (NEW - preconditions/regles/postconditions)
docs/uml/use-cases.md (NEW)
docs/uml/state-commande.md (NEW)
docs/uml/sequence-passer-commande.md (NEW)
docs/merise/_diagrams/*.mmd + *.svg (Mermaid leftover, a supprimer si on finit le pivot drawio)
Working tree modifie (tracked) :
docs/PROJECT_CONTEXT.md (ligne statut realignee sur machine canonique)
.drawio : commites sur feat/p1-conception (clean), en attente validation visuelle user sur le site drawio.
Hors-scope BYAN (persistants, jamais commites) :
.claude/CLAUDE.md, .claude/rules/byan-agents.md, Makefile, package.json, package-lock.json
```
### Decision majeure : machine a etats de commande (CANONIQUE)
Trois vocabulaires se contredisaient (ENUM dictionnaire avec paiement / PROJECT_CONTEXT sans paiement / noms FR introduits par erreur dans le brief Merise). Tranche par le user :
- **Deux phases** : le client compose (`pending_payment`) PUIS paie (`paid`), apres quoi la commande passe en preparation. Le paiement EXISTE dans le cycle.
- **Termes en anglais** (code-facing), valeurs ENUM du dictionnaire.
- **Machine retenue** : `pending_payment -> paid -> preparing -> ready -> delivered`
- **`cancelled` atteignable depuis tout etat non remis** (`pending_payment`, `paid`, `preparing`, `ready`) : annulable a tout moment tant que non livree (modification / annulation / remboursement client). `delivered` et `cancelled` finaux.
Applique partout : dictionary.md (deja correct), PROJECT_CONTEXT.md (corrige ligne 61), mct.md, mlt.md, state-commande.md, use-cases.md, sequence-passer-commande.md.
### Autres decisions actees Session 7
| Decision | Contexte |
|---|---|
| Outil diagramme MCD = drawio | User valide les 4 .drawio lui-meme sur le site (pas encore valides). Pivot Mermaid->drawio a finir cote pipeline + refs mcd.md |
| Pas de parcours employe dedie | Les employes sont couverts par le diagramme de cas d'utilisation (use-cases) + diagramme d'etats. Ockham, evite le doublon |
| Production parallele en respectant les dependances | On fait ce qui ne depend PAS du MCD (dependance nulle ou faible). On NE fait PAS ce qui en depend moyennement ou fortement (MLD, class-diagram) tant que les .drawio ne sont pas valides |
| MOT saute (rappel) | Coherent Session 4 (raccourci agile MCT -> MLT direct) |
### Decisions secondaires EN SUSPENS (remontees par l'agent Merise, a trancher avant MLD)
1. **`source` (canal : kiosk/comptoir/drive) vs `mode_consommation` (fiscal : sur_place/a_emporter/drive)** : deux dimensions distinctes. `source` est absent du dictionnaire et du MCD. A ajouter avant de generer le DDL.
2. **`user_id` sur `commande`** : aucune tracabilite de l'equipier qui saisit / prend en charge / livre. A amender dans le MCD si stats par equipier souhaitees.
(Points mineurs documentes dans mct.md section 14 et mlt.md section 13 : `service_day` non materialise, etc.)
### A faire lors de la reprise (ordre recommande)
1. **User valide les 4 `.drawio`** sur le site drawio (preview Markdown ne rend pas Mermaid sans extension ; les .drawio/.svg s'affichent en revanche).
2. **Trancher les 2 decisions secondaires** (`source` vs `mode_consommation`, `user_id` sur commande) - elles debloquent le MLD + class-diagram.
3. **Finir le pivot drawio** (si confirme) : cible Makefile `docs-render-drawio` (image `jgraph/drawio` dispo localement), regenerer les SVG depuis les .drawio, reecrire les refs dans mcd.md (actuellement vers SVG Mermaid), supprimer les `.mmd`/`.svg` Mermaid.
4. **Produire MLD + class-diagram** une fois le MCD valide et les decisions secondaires tranchees.
5. **Commiter le lot P1 conception** (option : 1 commit par doc) sur `feat/p1-conception`, puis PR vers `dev` (vigilance : base = `dev`, pas `main`).
### Commande exacte pour reprendre
```bash
cd /home/acadenice/corentin_wakdo
git status
git branch --show-current # attendu : feat/p1-conception
ls docs/merise/ # mcd.md, mct.md, mlt.md, dictionary.md
ls docs/uml/ # use-cases.md, state-commande.md, sequence-passer-commande.md
```
---
## Session precedente : 2026-05-09 (Session 6 - drawio + P5 front complet en remote control)
### Vue d'ensemble
Session ~3-4h en remote control mobile qui a couvert 2 gros chantiers et un long detour sur l'automation des PRs :
1. **Pivot Mermaid -> drawio sur le MCD** : 4 fichiers `.drawio` XML generes pour gain de controle layout (planarite du global non resoluble par mmdc)
2. **Front P5 complet anticipe** : 2 runs d'agent UX (Sally) ont produit 7 pages HTML kiosk + 7 modules JS vanilla + JSON fallback statique. Flux complet welcome -> confirmation, live sur le vhost.
3. **Setup automation PR via API** : detour sur fine-grained PATs (3 tentatives KO sur policy org AcadeNice "approval required"), resolu avec un classic PAT `ghp_` en attendant l'approbation admin lundi.
2 PR ouvertes sur GitHub :
- **#4** ready : front P5 complet (7 commits)
- **#5** draft : 4 .drawio sources, en attente cleanup et suite Merise/UML
### Etat Git actuel (post-Session 6)
```
Repo : /home/acadenice/corentin_wakdo/
Remote : git@github-wakdo:AcadeNice/wakdo_corentin.git
Branches :
main 00a3f82 (v0.1.0)
dev 68db2ee
feat/p1-conception 64f5a27 (PR #5 draft : 4 .drawio committed)
feat/p5-front-landing 6a7e772 (PR #4 ready : 7 commits P5 complet) <- HEAD post-session
feat/infra-docker b09c461 (mergee, conservee)
feat/p1-assets-import 24e733b (mergee, conservee)
feat/p1-stubs-and-dictionary d1a9876 (mergee, conservee)
Tags :
v0.1.0 00a3f82 Infrastructure foundation
Working tree out-of-scope (BYAN, persistent depuis sessions precedentes) :
.claude/CLAUDE.md, .claude/rules/byan-agents.md, Makefile
package.json, package-lock.json
docs/SESSION_RESUME.md (ce fichier)
Working tree P1 (uncommitted, voyage entre branches lors des switches) :
docs/merise/mcd.md
docs/merise/_diagrams/*.mmd (Mermaid leftover, a supprimer apres render drawio)
docs/merise/_diagrams/*.svg (Mermaid render, idem)
```
### PRs ouvertes
| # | Titre | Branche | Base | Statut |
|---|---|---|---|---|
| 4 | feat(front): P5 kiosk complete flow with vanilla JS and JSON fallback | `feat/p5-front-landing` | `dev` | Ready for review |
| 5 | docs(merise): MCD diagrams in drawio XML (4 files) | `feat/p1-conception` | `dev` | Draft |
### Decisions actees Session 6
| Decision | Contexte |
|---|---|
| Switch Mermaid -> drawio pour MCD | Planarite du global non resoluble par mmdc auto-layout, controle manuel requis |
| 4 .drawio separes (1 par diagramme) | Plus simple a editer et diff que multi-page |
| Front P5 anticipe pendant P1 | Data contract gele par brief ecole (JSON sources), front consomme JSON = consomme future API au mapping pres |
| Classic PAT (`ghp_`) au lieu de fine-grained AcadeNice | Org AcadeNice policy "approval required", admin pas dispo le weekend |
| Branche front renommee `p1` -> `p5` | Front borne = livrable P5 per plan SDLC (independant de la phase de realisation) |
| Auto-validation 7 checks par l'agent | Remote control mobile = pas de validation visuelle facile, agent doit s'auto-verifier |
| Mode JSON fallback statique dans `borne/data/` | Apache bind-mount ne sert pas `_sources/`, copie statique = solution simple. Swap point unique dans `data.js` pour P4 |
| TVA 10% (taux restauration FR 2024 simplifie) | TODO P3 : valider avec comptable les variations sur place vs a emporter, alcool, etc. |
| `gh` dans Docker = mauvaise idee | Stack Docker = runtime app, pas dev tooling. `gh` ou curl appartient a l'host. |
| Token GitHub stocke dans `.env` (gitignore) | Standard, pas de leak en commits, lu via `source .env` au moment du curl |
### Ce qui a ete fait chronologiquement
**Bloc 1 - Pivot Mermaid -> drawio + 4 sources XML**
1. Decision drawio uniquement, 4 fichiers separes, manual layout
2. Generation des 4 fichiers `.drawio` XML avec entites + cardinalites Merise `(min,max)` a partir des sources Mermaid
3. Commit `64f5a27` : `docs(merise): add drawio XML sources for MCD diagrams`
**Bloc 2 - 1er agent UX (welcome + categories scaffold)**
4. Spawn agent UX-designer (Sally) en background avec scope 2 ecrans (welcome + categories), flag `isolation: "worktree"`
5. L'agent a base sa branche sur `feat/p1-conception` au lieu de `dev` (le worktree n'a pas vraiment isole comme attendu)
6. Rebase `feat/p1-front-landing --onto dev 64f5a27` pour droper le commit drawio comme parent
7. Renommage `feat/p1-front-landing` -> `feat/p5-front-landing` (front borne = livrable P5)
**Bloc 3 - Setup automation PR (long detour sur les PATs)**
8. Push des 2 branches (SSH agent socket `/tmp/ssh-Evc7jT0fk2rs/agent.2611024`)
9. Tentative `gh` CLI dans Docker -> mauvaise idee, abandonne
10. Tentative fine-grained PAT #1 : Resource owner = Imugiii -> 404 sur wakdo_corentin (org repo invisible pour PAT a owner perso)
11. Fine-grained PAT #2 : meme probleme (Resource owner encore = Imugiii)
12. Fine-grained PAT #3 avec Resource owner = AcadeNice -> token en "Pending review" (org policy "approval required")
13. Fallback classic PAT (`ghp_`) -> fonctionne des generation, scope `repo`, admin sur le repo
14. PR #4 (front ready) + PR #5 (drawio draft) creees via `POST /repos/AcadeNice/wakdo_corentin/pulls`
**Bloc 4 - 2e agent UX (P5 complet)**
15. Spawn agent UX en background sur `feat/p5-front-landing` avec scope etendu : 5 nouvelles pages + JS state + JSON fallback
16. Auto-validation 7 checks dans le brief (assets exist, links resolve, JSON valid, HTML closed, JS syntax, http server e2e, JSON fetch)
17. Livrable : 5 pages HTML, 7 modules JS, 2 JSON normalises copies dans `borne/data/`, CSS etendu de 438 -> 1257 lignes
18. 6 commits thematiques (`6f5daca` -> `6a7e772`), 7/7 auto-checks PASS
19. Push, mise a jour PR #4 (titre + body) via `PATCH /repos/.../pulls/4`
20. Test live kiosk : les endpoints repondent 200 (welcome, categories, products, product, cart, payment, confirmation, JSON, CSS, JS, images)
### Commande exacte pour reprendre
```bash
cd /home/acadenice/corentin_wakdo
git status
git branch --show-current
# Si HEAD = feat/p5-front-landing : tu peux tester le front sur https://corentin-wakdo.stark.a3n.fr/
# Pour reprendre P1 :
git checkout feat/p1-conception
```
### A faire lors de la reprise (ordre recommande)
1. **Review visuelle PR #4** : tester le flux kiosk complet sur https://corentin-wakdo.stark.a3n.fr/, valider, merger dans `dev` (via web ou via API)
2. **Drawio render automatique** : ajouter cible Makefile `make docs-render-drawio` qui utilise le container `rlespinasse/drawio-export` pour generer les SVG depuis les `.drawio` XML. Evite l'export manuel sur drawio web (galere sur mobile).
3. **Continuer P1 conception** sur `feat/p1-conception` :
- `docs/merise/mct.md`
- `docs/merise/mlt.md`
- `docs/merise/mld.md`
- `docs/uml/class-diagram.md`
- `docs/uml/use-cases.md`
- `docs/uml/state-commande.md`
- `docs/uml/sequence-passer-commande.md`
4. **Commit final mcd.md** : une fois les SVG drawio generes, commit `mcd.md` + suppression des `.mmd`/anciens `.svg` Mermaid + Makefile mis a jour (drop `docs-render` mmdc, garder uniquement `docs-render-drawio`)
5. **Passage PR #5 draft -> ready** (via PATCH API) quand tout le P1 conception est dedans
6. **Hygiene secu PATs** : revoquer dans GitHub Settings :
- 3 fine-grained `github_pat_...` (suffixes BE4y, UiZc, ljeC)
- classic `ghp_Rr5EkM4...` (a rotater quand fine-grained AcadeNice approuve par admin lundi)
### Note technique - isolation worktree
Le flag `isolation: "worktree"` sur l'Agent tool n'a pas cree de vrai worktree isole - les agents ont travaille directement sur le main repo (`git worktree list` ne montre qu'une entree). Pas grave en pratique mais a savoir pour les prochains spawns : instruire explicitement l'agent de ne pas switch de branche dans le main repo, ou de bosser en branche dediee pre-checked-out.
---
## Session precedente : 2026-04-30 soir (Session 5 - notes perso + demarrage P1 conception + setup pipeline diagrammes)
### Vue d'ensemble
Session moyenne (~3h) qui a couvert :
1. **Cloture P1 dictionnaire** : commit + push + PR #3 mergee dans `dev`
2. **4 notes perso ecrites** (gitignore) : apache-fastcgi-pitfalls, docker-network-pools-rfc1918, enum-vs-table-reference, merise-yagni-quantite + index README mis a jour
3. **Demarrage P1 conception** sur nouvelle branche `feat/p1-conception` (renommee depuis `feat/p1-merise-conception` pour inclure UML)
4. **MCD redige** (`docs/merise/mcd.md`) avec 3 sous-domaines (Catalogue/Commande/RBAC) en Mermaid + tableau recap cardinalites Merise
5. **Pipeline mmdc setup** : .mmd sources + SVG generes + `make docs-render` target
6. **Blocage layout MCD global** : 10 entites + 10 associations = probleme planarite intrinseque, decision laissee en suspens
Aucun commit fait sur la branche `feat/p1-conception` — tout reste dans le working tree pour reprise propre.
### Etat Git actuel (post-Session 5)
```
Repo : /home/acadenice/corentin_wakdo/
Remote : git@github-wakdo:AcadeNice/wakdo_corentin.git
Branches :
main 00a3f82 (v0.1.0)
dev 68db2ee (= main + PR#1 + PR#2 + PR#3 mergees)
feat/infra-docker b09c461 (mergee, conservee)
feat/p1-assets-import 24e733b (mergee, conservee)
feat/p1-stubs-and-dictionary d1a9876 (mergee via PR#3, conservee)
feat/p1-conception 68db2ee (NOUVELLE, ZERO COMMIT, working tree only)
Tags :
v0.1.0 00a3f82 Infrastructure foundation
Working tree sur feat/p1-conception (a commit a la prochaine session) :
docs/merise/mcd.md (NEW - 411 lignes)
docs/merise/_diagrams/mcd-global.mmd (NEW)
docs/merise/_diagrams/mcd-global.svg (NEW)
docs/merise/_diagrams/mcd-catalogue.mmd (NEW)
docs/merise/_diagrams/mcd-catalogue.svg (NEW)
docs/merise/_diagrams/mcd-commande.mmd (NEW)
docs/merise/_diagrams/mcd-commande.svg (NEW)
docs/merise/_diagrams/mcd-rbac.mmd (NEW)
docs/merise/_diagrams/mcd-rbac.svg (NEW)
Makefile (modified - target docs-render ajoute)
docs/notes/README.md (modified, gitignore - 7 entries ajoutees)
docs/notes/apache-fastcgi-pitfalls.md (NEW, gitignore)
docs/notes/docker-network-pools-rfc1918.md (NEW, gitignore)
docs/notes/enum-vs-table-reference.md (NEW, gitignore)
docs/notes/merise-yagni-quantite.md (NEW, gitignore)
Working tree out-of-scope (BYAN, deja modifies en sessions precedentes) :
.claude/CLAUDE.md
.claude/rules/byan-agents.md
package.json, package-lock.json
docs/SESSION_RESUME.md
```
### Decisions actees Session 5
| Decision | Contexte |
|---|---|
| Branche renommee `feat/p1-conception` (au lieu de `feat/p1-merise-conception`) | Pour inclure UML dans le scope de la PR |
| UML ajoute au scope P1 conception | RNCP 37805 attend Merise + UML, pas que Merise |
| Granularite commits PR : option α (1 commit par doc) | Coherent avec l'historique, squashable en fin de PR si besoin |
| Pipeline diagrammes : option B (mmdc local + SVG embed) | Rendu portable Cursor/GitHub/PDF/Notion, regenerable via `make docs-render` |
| Ecriture des 4 notes perso AVANT le travail Merise | Sujets frais dans le contexte (Session 4 + dictionnaire) |
| Auteur des notes = "BYAN" | Coherent avec section 17 PROJECT_CONTEXT (transparence methodologie) |
| 4 hedgings appliques (`toujours`/`forcement` -> `tend a`/`implique`) | Hook PreToolUse fact-check intransigeant |
### Decision EN SUSPENS (a trancher en premier la prochaine session)
**Layout du diagramme global MCD** : avec 10 entites et 10 associations (dont
2 polymorphiques sur `LIGNE_COMMANDE` -> PRODUIT et MENU), il y a un probleme
de planarite : impossible de placer toutes les entites sans croiser au moins
2 lignes. Mermaid auto-layout galere, Excalidraw manuel n'aide pas vraiment.
3 options en attente d'arbitrage :
- **A. Drop le global** — supprimer section 3 du MCD, garder section 6
(tableau recap des cardinalites) + section 4 (3 sous-domaines Mermaid
propres). Coherent avec l'approche Merise (decomposer si > 5 entites).
Defendable au jury : "j'ai decompose, tableau recap = vue integree".
- **B. Excalidraw manuel** — refaire le global a la main dans Excalidraw,
export `.excalidraw.svg` (avec scene embedee), embed dans le MCD. Coute
20-30 min de layout manuel, perd la regen automatique.
- **C. Garder Mermaid en l'etat** — assumer que le layout est croise mais
jouer le compromis "decomposition prime sur visuel global".
Le user a dit "le choix n'est pas encore fait" (Session 5 fin de soiree,
fatigue sur le sujet). A reprendre tete reposee.
### Ce qui a ete fait chronologiquement
**Bloc 1 - Cloture dictionnaire (commit + PR)**
1. Verification etat initial : branche `feat/p1-stubs-and-dictionary` avec
`b8f7d35` (stubs+fixes) commit, `docs/merise/dictionary.md` uncommitted
2. Commit `d1a9876` : `docs(merise): data dictionary v0.1 - 10 entities + Mermaid ER diagram + 7 modeling notes`
3. Push branche, PR #3 ouverte manuellement (gh CLI absent), mergee sur `dev`
4. `git fetch + checkout dev + merge --ff-only origin/dev` : sync OK sur `68db2ee`
**Bloc 2 - 4 notes perso (gitignore)**
5. Bascule sur `Read` au lieu de `grep`/`sed` apres blocage du hook
`tool-failure-guard` sur "internal error" string presente dans
`vhost.conf` (pattern legitime du commentaire, faux positif Bash output)
6. `apache-fastcgi-pitfalls.md` (405 lignes) - 3 pieges Session 4
(DirectoryIndex, allowed_clients, R=200) + Q&A jury
7. `docker-network-pools-rfc1918.md` (376 lignes) - saturation pools Docker
sur stark, choix `192.168.148.0/24`, RFC 1918, Q&A jury
8. `enum-vs-table-reference.md` (381 lignes) - matrice de decision ENUM vs
table, application aux 4 ENUMs Wakdo, Q&A jury
9. `merise-yagni-quantite.md` (337 lignes) - YAGNI applique a `menu_produit`,
audit source ecole 13 menus mono-portion, Q&A jury
10. `docs/notes/README.md` index mis a jour : 7 nouvelles entrees (3 Session
4 deja redigees + 4 nouvelles)
**Bloc 3 - Setup branche conception + cadrage**
11. Branche `feat/p1-merise-conception` creee depuis `dev`, puis renommee en
`feat/p1-conception` apres discussion sur l'ajout UML au scope
12. Decision : 8 documents prevus pour la PR :
- Merise : MCD, MCT, MLT, MLD (4 docs dans `docs/merise/`)
- UML : classes, use cases, etats, sequence (4 docs dans `docs/uml/`)
13. `docs/uml/` cree
**Bloc 4 - MCD redige + setup pipeline diagrammes**
14. Premier draft MCD avec 4 diagrammes ASCII art (global + 3 sous-domaines)
+ tableau recap cardinalites Merise `(0,N)/(1,1)`
15. User trouve l'ASCII moche -> bascule sur Mermaid `erDiagram` inline
16. User ne voit pas le rendu Mermaid dans Cursor (pas d'extension installee)
-> bascule sur SVG via mmdc
17. Setup `docs/merise/_diagrams/` : 4 fichiers `.mmd` extraits + 4 SVG
generes via `npx mmdc`
18. MCD edite : 4 blocs Mermaid remplaces par `<img src="_diagrams/*.svg">`
19. Makefile : target `docs-render` ajoute, scanne tous les
`docs/**/_diagrams/*.mmd` du projet, regen SVG (testee, OK 4/4)
20. User constate que le global a des lignes croisees, tente Excalidraw,
n'arrive pas a un layout logique -> decision laissee en suspens
### Commande exacte pour reprendre
```bash
cd /home/acadenice/corentin_wakdo
git status # confirmer working tree intact
git branch --show-current # doit etre feat/p1-conception
git log --oneline dev..HEAD # doit etre vide (zero commit sur la branche)
ls docs/merise/ # mcd.md + _diagrams/ presents
ls docs/notes/ # 4 notes Session 5 + README maj
```
### A faire lors de la reprise (ordre recommande)
1. **Trancher le layout MCD global** (option A / B / C ci-dessus). Si A,
editer mcd.md pour supprimer section 3, decrementer les numeros, et
supprimer `_diagrams/mcd-global.{mmd,svg}`. Si B, faire le travail
Excalidraw manuel. Si C, ne rien faire et continuer.
2. **Commit le MCD** (option α : 1 commit par doc) :
```bash
git add docs/merise/mcd.md docs/merise/_diagrams/ Makefile
git commit -m "docs(merise): MCD v0.1 + pipeline mmdc -> SVG via make docs-render"
```
3. **Continuer P1 conception**, dans l'ordre :
- `docs/merise/mct.md` - operations metier + acteurs + flux
- `docs/merise/mlt.md` - workflow logique (preconditions / regles /
postconditions)
- `docs/merise/mld.md` - schema relationnel formel
- `docs/uml/class-diagram.md` - vue OOP (prepare PHP P2)
- `docs/uml/use-cases.md` - acteurs + goals (kiosk client, manager, ...)
- `docs/uml/state-commande.md` - machine a etats `commande.statut`
- `docs/uml/sequence-passer-commande.md` - flux passer commande
4. **Push + PR vers `dev`** quand les 7 docs restants sont commit.
Vigilance : verifier `base = dev` (pas `main` !).
5. **Apres merge** : continuer P1 implementation (DDL + seed + fallback)
sur une branche `feat/p1-implementation` separee.
---
## Session precedente : 2026-04-30 jour (Session 4 etendue - smoke test infra + import assets + P1 demarre)
### Vue d'ensemble
Session tres longue (~6h cumulees) qui a couvert 3 grandes phases en une seule journee :
1. **Smoke test infra Docker** sur le serveur stark : `make init` valide bout en bout, 4 services healthy, certs Let's Encrypt provisionnes
2. **Import assets ecole** : 71 images normalisees + 2 JSON sources + maquette Figma PDF
3. **Demarrage P1 Merise** : stubs unblock-403 + dictionnaire de donnees v0.1 (10 entites)
3 PR ont ete fusionnees (1 incident main->dev recupere) et 1 PR reste ouverte (la branche
de la 3e phase n'est pas encore push).
### Etat Git actuel (post-Session 4)
```
Repo : /home/acadenice/corentin_wakdo/
Remote : git@github-wakdo:AcadeNice/wakdo_corentin.git
Branches :
main 00a3f82 (v0.1.0 - infra foundation)
dev 84d2559 (= main + PR#2 assets)
feat/infra-docker b09c461 (mergee dans main via PR#1, conservee)
feat/p1-assets-import 24e733b (mergee dans dev via PR#2, conservee)
feat/p1-stubs-and-dictionary b8f7d35 (LOCAL UNIQUEMENT - NON PUSHEE)
+ dictionnaire UNCOMMITTED dans le working tree
Tags :
v0.1.0 00a3f82 Infrastructure foundation (annotated, pushe)
Working tree out-of-scope (volontairement non commit) :
.claude/CLAUDE.md, .claude/rules/* (modifies hors-projet, BYAN)
package.json, package-lock.json (BYAN)
docs/SESSION_RESUME.md (ce fichier - local perso)
```
### Vue chronologique des PR
```
00a3f82 (main, dev, v0.1.0) Merge PR #1 from feat/infra-docker
- INCIDENT : PR ouverte par defaut sur 'main' au lieu de 'dev'
- REMEDIATION : git checkout dev && git merge --ff-only origin/main && git push origin dev
- LECON : verifier le selecteur 'base branch' sur GitHub avant 'Create pull request'
- 9 commits infra : compose, dockerfiles, smoke test fixes, FQDN switch, journal session 4
84d2559 (dev) Merge PR #2 from feat/p1-assets-import
- PR sur la bonne base 'dev' cette fois
- 1 commit : import des 2 JSON sources + 71 images normalisees + maquette PDF
```
### Ce qui a ete fait (chronologique session)
**Phase 1 - smoke test infra (avant pause)**
1. Conflit `.env` BYAN/Wakdo traite par fusion en 1 fichier (gitignore)
2. Switch FQDN `acadenice.fr` -> `stark.a3n.fr` (zone wildcard existante, evite la
provisioning DNS prerequise par le challenge HTTP-01 de Traefik)
3. Smoke `make init` casse 3 fois et corrige :
- Subnet explicite `192.168.148.0/24` (pools Docker satures)
- `init: true` sur cron (dcron PID 1 setpgid)
- Healthz statique dans `/usr/local/apache2/htdocs/` (RewriteRule R=200 declenchait
ErrorDocument template)
4. Validation HTTPS externe : 4 services healthy, certs OK, `/healthz` isole
5. Journal session ecrit (`docs/journal/2026-04-30--smoke-test-infra.md`)
6. PR #1 -> incident main au lieu de dev -> remediation FF dev=main
7. Tag `v0.1.0` annote sur le merge commit
8. Memoire `feedback_no_absolutes.md` enregistree (hook fact-check sensible)
**Phase 2 - import assets ecole**
9. Drop zone `docs/_inbox/` recue (9 Mo, 71 images + 2 JSON + 1 PDF + 1 readme)
10. Diagnostic : naming "wacdo" vs notre "Wakdo", casse incoherente, 7 typos dans JSON
11. Rangement final :
- `docs/merise/_sources/` (2 JSON + source-school.md provenance)
- `docs/design/` (maquette PDF + README pointant Figma)
- `src/public/borne/assets/images/` (71 images normalisees kebab-case lowercase)
12. Workaround `chown` via container ephemere (src/ etait owned root par Docker bind-mount)
13. PR #2 -> dev (workflow correct cette fois)
**Phase 3 - demarrage P1 Merise**
14. Decisions cadrage P1 actees :
- MOT skippe (raccourci agile MCT -> MLT direct)
- Composition flexible (`menu_produit` avec `role` et `position`)
- Pas de `quantite` sur menu_produit (YAGNI - 13 menus mono-portion)
- 9 entites + 1 jointure RBAC : Categorie, Produit, Menu, MenuProduit, Commande,
LigneCommande, User, Role, Permission, RolePermission
- Pas de stock numerique (juste `est_disponible` boolean)
- Pas de stats agregees (live queries)
- Pas d'allergenes/nutrition pour MVP
15. Stubs minimaux pour debloquer le 403 :
- `src/public/borne/index.html` (HTML statique avec logo, vhost kiosk)
- `src/public/admin/index.php` (PHP qui prouve la chaine FastCGI E2E)
16. 2 bugs infra exposes par les stubs et fixes :
- Apache `DirectoryIndex` ne contenait pas `index.php` (admin renvoyait 403)
- PHP-FPM `listen.allowed_clients = any` rejete par PHP 8.3 (toutes les connexions
Apache->PHP-FPM rejetees, FastCGI cassait silencieusement)
17. Validation HTTPS externe : kiosk -> 200 HTML, admin -> 200 PHP rendu (PHP_VERSION
et timestamp dynamique substitues)
18. Commit `b8f7d35` : `feat(stubs): unblock 403 with kiosk and admin index pages, plus FastCGI fixes`
19. Dictionnaire de donnees v0.1 redige (`docs/merise/dictionary.md`) :
- ~340 lignes, 10 entites detaillees
- Diagramme entites-relations en Mermaid (rendu GitHub natif)
- 7 notes de modelisation (FLOAT vs cents, ENUM vs table, STI, polymorphisme,
audit fields, RFC 5321 emails, etc.)
- Cross-validation avec `PROJECT_CONTEXT.md` et source ecole
20. 3 notes techniques perso (`docs/notes/`, gitignore) :
- `rbac-roles-permissions.md` : pattern RBAC dynamique vs statique, UI matrice, Q&A jury
- `monetary-int-cents.md` : INT centimes + TVA pour mille, Goldberg ACM 1991, Stripe
convention, 5 pieges
- `polymorphic-fk-snapshots.md` : 2 colonnes nullables + discriminator + snapshots,
Karwin SQL Antipatterns, integrite historique commandes
21. Memoire `feedback_notes_author.md` mise a jour : auteur des notes = "BYAN" (pas un
nom de LLM specifique), coherent avec la transparence projet section 17 PC
---
## En cours (a reprendre)
**Branche locale `feat/p1-stubs-and-dictionary` avec 1 commit fait + dictionnaire UNCOMMITTED.**
### Commande exacte pour reprendre
```bash
cd /home/acadenice/corentin_wakdo
git status # confirmer ?? docs/merise/dictionary.md
git branch --show-current # doit etre feat/p1-stubs-and-dictionary
git log --oneline dev..HEAD # voir b8f7d35 (stubs+fixes)
```
### A faire lors de la reprise
1. **Commit le dictionnaire** :
```bash
git add docs/merise/dictionary.md
git commit -m "docs(merise): data dictionary v0.1 - 10 entities + Mermaid ER diagram + 7 modeling notes
Bottom-up derivation from school JSON sources + PROJECT_CONTEXT business rules.
Covers : Categorie, Produit, Menu, MenuProduit, Commande, LigneCommande,
User, Role, Permission, RolePermission. Decisions documented :
prices in INT cents, VAT in per-mille, polymorphic FK with snapshots
on ligne_commande, dynamic roles vs static permissions for RBAC."
```
2. **Push + PR** :
```bash
SSH_AUTH_SOCK=/tmp/ssh-Evc7jT0fk2rs/agent.2611024 git push -u origin feat/p1-stubs-and-dictionary
```
Puis ouvrir PR via :
`https://github.com/AcadeNice/wakdo_corentin/compare/dev...feat/p1-stubs-and-dictionary?expand=1`
**VIGILANCE** : verifier base = `dev` (pas `main` !) avant de cliquer Create.
3. **Continuer P1** sur une nouvelle branche `feat/p1-merise-models` (apres merge de la
precedente) ou sur la meme branche etendue. Suite des etapes :
- **MCD** : `docs/merise/mcd.md` avec un diagramme Mermaid plus formel (entites +
associations + cardinalites min/max), distinct du diagramme preview du dictionnaire
- **MCT** : `docs/merise/mct.md` - les operations metier (valider commande, encaisser
paiement, marquer pret, ...)
- **MLT** : `docs/merise/mlt.md` - workflow logique de chaque traitement (preconditions,
regles, postconditions, sorties)
- **MLD** : `docs/merise/mld.md` - schema relationnel formel
- **DDL** : `db/migrations/0001_init_schema.sql` - SQL CREATE TABLE concret
- **Seed** : `db/seeds/0001_demo_data.sql` - INSERT des 9 categories + 53 produits +
13 menus a partir des JSON sources, avec normalisation des prix en centimes
- **Export fallback JSON** : `scripts/export-fallback.{sh|php}` qui genere
`src/public/borne/data/*.json` depuis le seed (pour mode "Bloc 1 isole")
4. **Notes perso restantes a rediger** (basse priorite, peut s'etaler sur plusieurs sessions) :
- `enum-vs-table-reference.md` : quand utiliser ENUM vs table referentielle
- `docker-network-pools-rfc1918.md` : pool saturation Docker, choix subnet explicite
- `apache-fastcgi-pitfalls.md` : les 3 bugs infra de cette session (DirectoryIndex,
listen.allowed_clients, RewriteRule R=200)
- `merise-yagni-quantite.md` : application concrete de YAGNI sur menu_produit
5. **Optionnels post-merge** (peuvent aller sur une branche `feat/infra-polish` separee
apres P1) :
- Investiguer redirect HTTP->HTTPS qui retourne 404 (interaction avec middleware
global du Traefik d'hote)
- Unifier le style init Docker (wakdo-app a tini explicite, wakdo-cron a `init: true` -
incoherence stylistique a accepter ou unifier)
---
## Workflow git en vigueur
- **Convention** : `feat/* -> dev` (squash merge), `dev -> main` (avec tag `vX.Y.Z` annote)
- **Vigilance PR** : verifier explicitement `base branch = dev` (le defaut GitHub est `main`)
- **Branches mergees** : conservees comme trace historique
- **Procedure d'archive** si une branche est abandonnee non-mergee :
```bash
git tag -a archive/feat-xxx <branch-tip-sha> -m "abandoned: <reason>"
git push origin archive/feat-xxx
git push origin --delete feat-xxx
git branch -D feat-xxx
```
- **Tags previsionnels** : v0.1.0 (infra), v0.2.0 (P2 stubs+auth+CRUD basic), v0.3.0 (CRUD
admin complet), ..., v1.0.0 (livrable jury)
---
## Decisions structurelles actees Session 4
| Decision | Pourquoi |
|---|---|
| Fusion `.env` BYAN+Wakdo | Outil tiers lit `.env` du cwd, separation `.env.wakdo` plus risquee |
| FQDN sur `*.stark.a3n.fr` | Wildcard DNS existant, evite provisioning prerequis HTTP-01 |
| Subnet explicite `192.168.148.0/24` (RFC 1918) | Pools Docker satures sur hote partage |
| `init: true` sur cron | dcron en PID 1 sans init reaper boucle sur setpgid Operation not permitted |
| Healthz statique htdocs | RewriteRule R=200 declenche template ErrorDocument |
| `APP_ENV=dev` + `APP_DEBUG=true` | Phase de construction, flip vers prod avant demo jury |
| Naming "wacdo" (source) vs "Wakdo" (projet) | wacdo preserve dans `_sources/` pour tracabilite ecole |
| Casse images normalisee kebab-case lowercase | Anti-piege case-sensitive Linux dans Docker |
| MOT (modele organisationnel des traitements) skippe | Raccourci agile MCT -> MLT direct |
| Composition flexible `menu_produit` (role+position) | Defendable evolution future sans refacto |
| Pas de `quantite` sur `menu_produit` | YAGNI - 13 menus mono-portion |
| Prix en INT centimes + TVA en pour mille | Anti-FLOAT IEEE 754 (Goldberg ACM 1991) |
| Snapshots libelle+prix sur `ligne_commande` | Integrite historique audit |
| Polymorphisme `ligne_commande` -> `produit` OR `menu` | 2 cols nullable + discriminator + 2 FKs reelles |
| RBAC : roles+role-permission dynamiques, permissions statiques | Permissions liees au code (declarees en migration) |
| Pas de stock numerique en MVP | Juste `est_disponible` boolean |
| Pas de stats agregees | Live queries SUM/GROUP BY suffisent |
| Pas d'allergenes/nutrition | Hors source ecole, hors scope MVP |
| ENUM pour valeurs metier stables | mode_consommation, statut, role -> 3-7 valeurs, ALTER si extension |
| Auteur notes perso = "BYAN" | Coherent avec section 17 PC (transparence methodologie) |
---
## Prochaines etapes (apres merge `feat/p1-stubs-and-dictionary` -> `dev`)
```
P1 Conception Merise (semaines 2-3) - EN COURS
1. Dictionnaire de donnees v0.1 ECRIT (a commit)
2. MCD a faire
3. MCT a faire
4. (MOT skippe)
5. MLD a faire
6. MLT a faire
7. DDL a faire
8. Seed data a faire
9. JSON fallback a faire
P2 Back squelette (semaines 4-6)
- Core (Router, Autoloader, DB)
- Auth (sessions PHP fichier + argon2id)
- RBAC (Authorization service)
- Front controller `index.php`
P3 CRUD admin (semaines 7-10)
- CRUD produits, menus, categories
- UI roles + assignment matrice permissions
- CRUD users
P4 API REST (semaines 11-12)
- Endpoints /api/categories, /api/menus, /api/produits, /api/orders
- CORS + tests
P5 Front borne (semaines 13-16)
- Integration maquette + Ajax + a11y RGAA
- Mode JSON-seuls (fallback) + mode API-connecte
P6 Tests + finition (semaines 17-18)
P7 DevOps final (semaine 19) - GitHub Actions, crons, docs
P8 Prep soutenance (semaine 20)
```
---
## Fichiers cles a consulter a la reprise
| Fichier | Contenu |
|---|---|
| `docs/PROJECT_CONTEXT.md` | Source de verite projet (FQDN sur stark.a3n.fr, section 17 transparence IA) |
| `docs/_ref/rncp-37805-referentiel.pdf` | Referentiel RNCP officiel |
| `docs/_ref/rncp-37805-index.md` | Index texte compact des criteres (cherchable) |
| `docs/journal/README.md` | Template + index des retros (3 entrees) |
| `docs/journal/2026-04-30--smoke-test-infra.md` | Retro Session 4 phase 1 (smoke test) |
| `docs/merise/dictionary.md` | **Dictionnaire P1 v0.1 ECRIT (a commit)** |
| `docs/merise/_sources/` | JSON sources ecole + provenance |
| `docs/design/` | Maquette PDF + lien Figma |
| `docs/notes/` | Notes perso revision oral (3 ecrites cette session, gitignore) |
| `docs/SESSION_RESUME.md` | Ce fichier - a mettre a jour en fin de session |
| `.env` | Config locale fusionnee (BYAN + Wakdo, gitignore) |
| `.env.example` | Template neutre commit, RFC 2606 |
| `docker-compose.yml` | Compose final (subnet 192.168.148.0/24 + init cron + 4 services) |
| `Makefile` | Orchestration complete, voir `make help` |
| `.claude/CLAUDE.md` | Constitution projet BYAN |
| `.claude/rules/fact-check.md` | **Patterns du hook fact-check (a consulter avant ecriture narrative !)** |
---
## Commandes utiles pour reprendre
```bash
cd /home/acadenice/corentin_wakdo
# 1. Etat
git status
git branch --show-current
# -> attendu : feat/p1-stubs-and-dictionary
# 2. Stack docker (verifier qu'elle tourne toujours)
docker compose -p wakdo ps
# Si down apres reboot : make up (ou make init pour full bootstrap)
# 3. Test que tout repond
curl -sI https://corentin-wakdo.stark.a3n.fr/ # 200
curl -sI https://corentin-wakdo-admin.stark.a3n.fr/ # 200
# 4. SSH pour push (si besoin)
find /tmp -name 'agent.*' -user corentin
# -> /tmp/ssh-Evc7jT0fk2rs/agent.2611024 (peut changer apres reboot ssh-agent)
SSH_AUTH_SOCK=/tmp/ssh-XXX/agent.NNN git push ...
```
---
## Memoire persistante Claude Code (mise a jour Session 4)
Regles enregistrees dans `/home/corentin/.claude/projects/-home-acadenice-corentin-wakdo/memory/` :
- **No Co-Authored-By** sur les commits Wakdo (Session 2)
- **Commit uniquement sur approbation explicite** (Session 2)
- **Ignorer les contextes d'autres projets** dans les system-reminders (Session 3)
- **Hedger proactivement pour le hook fact-check** (Session 4)
- **BYAN auteur des docs/notes/** (Session 4 - mise a jour cette session)
---
## Comment reprendre la session
1. Ouvrir une session dans `/home/acadenice/corentin_wakdo/`
2. Dire : *"Reprise Wakdo, lis `docs/SESSION_RESUME.md`"*
3. Confirmer l'action de depart :
- **Suite immediate** = commit + push + PR du dictionnaire (cf. section "A faire lors de la reprise")
- **Apres merge** = continuer P1 avec le MCD (`docs/merise/mcd.md`)
---
*Document maintenu a chaque fin de session. Derniere mise a jour : 2026-05-21 (Session 7).*