diff --git a/docs/SESSION_RESUME.md b/docs/SESSION_RESUME.md new file mode 100755 index 0000000..053586d --- /dev/null +++ b/docs/SESSION_RESUME.md @@ -0,0 +1,1623 @@ +# 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_`) + 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 (`Wakdo - Bienvenue`), 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 ``) gardait `aria-disabled="true"` car + `.disabled` est un no-op sur un `` -> 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 ` 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 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\Repository` (DatabaseInterface) : requetes preparees, unicite. +2. `Controller extends AdminController` : par action -> `guard()` -> (mutation : + `Csrf::validate`) -> validation serveur + allowlist -> repo -> redirect + `setFlash`, ou re-rendu 422. +3. Vues `admin//{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/` 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 `` +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 -m "abandoned: " + 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).* diff --git a/docs/journal/2026-06-04--p1-merise-v0.2-rewrite-and-forgejo-migration.md b/docs/journal/2026-06-04--p1-merise-v0.2-rewrite-and-forgejo-migration.md new file mode 100644 index 0000000..cf6fb5d --- /dev/null +++ b/docs/journal/2026-06-04--p1-merise-v0.2-rewrite-and-forgejo-migration.md @@ -0,0 +1,105 @@ +# P1 Merise v0.2 (prod-like) reecrit + migration vers Forgejo auto-heberge + +**Date** : 2026-06-04 (seconde session du jour, suite de `conception-prodlike-revision`) +**Branche** : `feat/p1-conception` +**PR** : commits directs sur la branche ; PR `feat/p1-conception -> dev` a ouvrir apres l'UML +**Duree estimee** : session longue (decisions + execution + infra) + +--- + +## Ce qui a ete fait + +Trois chantiers enchaines. + +### 1. Decisions de conception restantes tranchees (D4-D8 + stock + Maxi) + +- **D4 - Roles / RBAC / workflow** : seed de 5 roles `admin / manager / kitchen / counter / drive`. RBAC dynamique (roles + matrice role-permission editables en UI). Attributs sur `role` pour rendre les comportements dynamiques sans hardcode : `default_route`, `order_source`, et table `role_visible_source` (filtre du dashboard par canal). Machine a etats reduite de 6 a **4 etats** : `pending_payment -> paid -> delivered` (+ `cancelled`). La cuisine est en lecture seule ; counter/drive font la livraison en un geste. +- **D5 - Permissions** : catalogue fige de 23 codes `resource.action` + matrice par defaut. Correction appliquee : `admin` recoit `order.create`/`order.deliver` ; `manager` ne recoit pas `order.cancel`. +- **D6 - service_day** : coupure a 10h00, calcul applicatif (`CASE WHEN HOUR(created_at) < 10 ...`), la colonne generee buguee a 4h30 du MLD v0.1 est abandonnee. +- **D7 - Subnet Docker** : retour a l'auto-allocation (IP liberees sur le serveur), le subnet explicite n'est plus necessaire. +- **D8 - Numero de commande** : prefixe par canal `K`/`C`/`D`. +- **Stock numerique** par ingredient : colonnes `unit`, `stock_quantity`, `pack_size`, `pack_label`, `low_stock_threshold` sur `ingredient` ; decrement automatique a la transition `paid`, re-credit a l'annulation, reconciliation manuelle (inventaire), journal `stock_movement` append-only, permissions `stock.read`/`stock.count`/`stock.manage`. +- **Format Maxi** : multiplicateur de recette. `product_ingredient` porte `quantity_normal` et `quantity_maxi` ; le decrement suit `order_item.format`. Le Maxi agrandit l'accompagnement et la boisson uniquement (burger et sauce invariants). + +Decisions consignees dans `docs/notes/revue-alignement-p1.md` section 7 (non versionne). + +### 2. Reecriture des 5 docs Merise en prod-like v0.2 (19 entites, anglais) + +Delegation a l'agent `expert-merise-agile`, en deux lots (donnees : dictionary/MCD/MLD ; traitements : MCT/MLT), chaque sortie verifiee contre le tableau de decision puis recalee a la main (matrice de permissions, multiplicateur Maxi). Resultat : `dictionary.md`, `mcd.md`, `mld.md`, `mct.md`, `mlt.md` reecrits, 19 entites, vocabulaire anglais `snake_case`, `commande_event` et `menu_produit` supprimes au profit du configurateur d'ingredients, des allergenes, des slots de menu et du journal de stock. Cinq commits (un par doc) sur `feat/p1-conception`. + +### 3. Migration du depot vers Forgejo auto-heberge + gouvernance PR + +- Nouveau depot `https://git.acadenice.com/AcadeNice/corentin_wakdo` (Forgejo sur le serveur). +- **Dual-push** configure : `git push origin` pousse desormais sur GitHub ET Forgejo, les deux en HTTPS via des tokens lus dans `.env` (gitignore). Aucun token persiste dans `.git/config` (credential helper qui lit `.env` au moment du push). +- **Reconciliation** d'un desalignement decouvert au passage : `dev` sur GitHub (`a3eae01`) etait le vrai tronc integre (PR #4 front, #5 conception, #6 admin shell mergees), tandis que `dev` local et Forgejo etaient restes a `68db2ee`. Forgejo a ete resynchronise sur l'etat GitHub complet, sans perte. +- **Template de PR** (`.gitea/PULL_REQUEST_TEMPLATE.md`, conventions BYAN) ajoute sur `dev`. +- **Protections de branche** sur `main` et `dev` (API Forgejo) : push direct interdit (PR obligatoire), force-push bloque, 0 approbation requise (adapte au travail solo). + +--- + +## Pourquoi - decisions et alternatives + +- **Machine a 4 etats au lieu de 6** : en fast-food, le KDS cuisine est un affichage visuel ; `preparing` et `ready` ajoutaient des transitions sans valeur metier proportionnelle. La livraison fusionne `ready`+`delivered` en un clic. Le KPI reste le temps total `delivered_at - paid_at`. +- **Stock Maxi via multiplicateur de recette** (alternative ecartee : produits de taille distincte Moyenne/Grande) : l'auteur a prefere un seul produit par choix avec `quantity_normal`/`quantity_maxi`, plus simple a saisir au seed tout en gardant un stock realiste. +- **Forgejo auto-heberge** (alternative : rester sur GitHub) : GitHub facture les depots prives, et l'auto-hebergement apporte Forgejo Actions et un controle fin des regles de PR. Cela ajoute un argument infra (Bloc 5) puisque la chaine forge + CI est maitrisee de bout en bout. +- **Tout en HTTPS + token plutot que SSH** : la cle SSH GitHub (`~/.ssh/git_wakdo`) est protegee par passphrase et inutilisable depuis le shell non-interactif de l'assistant. Les tokens HTTPS stockes dans `.env` permettent un push automatise des deux cotes. +- **0 approbation sur les protections** : en solo, exiger une approbation bloquerait le merge (on ne peut pas approuver sa propre PR). La protection conserve l'essentiel : PR obligatoire et pas de force-push. + +--- + +## Comment - points techniques cles + +- **Credential helper lisant `.env`** : `git config credential.https://.helper` pointe vers un script qui extrait le token de `.env` au moment du push. Avantage : roter un token = editer `.env`, sans reconfiguration git, et aucun secret dans `.git/config`. +- **Reconciliation `dev`** : `git branch -f dev origin/dev` pour reprendre l'etat GitHub reel, template ajoute par-dessus dans un worktree dedie (working tree principal preserve), push fast-forward sur GitHub et push force sur Forgejo (lignee perimee remplacee, depot neuf donc sans risque). +- **Protections via API** : `POST /api/v1/repos/.../branch_protections` avec `enable_push=false` et `required_approvals=0` pour `main` et `dev`. + +--- + +## Criteres RNCP couverts + +- **Bloc 2 - Cr 3.a / 3.b** : modelisation des donnees prod-like (dictionnaire, MCD, MLD), polymorphisme, snapshots, configurateur d'ingredients, journal de stock. +- **Bloc 2 - Cr 3.d** : TVA portee par le produit et calculee ligne par ligne. +- **Bloc 5** : forge auto-hebergee, gouvernance de branches (protections + template PR), socle pour la CI Forgejo Actions a venir. + +--- + +## Questions anticipees du jury + +- **Q** : "Pourquoi heberger votre propre forge plutot qu'un service gere ?" + **R** : Maitrise de la chaine (depot, CI via Forgejo Actions, regles de PR), depots prives sans cout, et coherence avec l'infra deja en place sur le serveur. GitHub est conserve comme copie synchronisee. + +- **Q** : "Comment gerez-vous le stock d'un menu Maxi ?" + **R** : Un multiplicateur de recette : `product_ingredient` porte une quantite Normale et une quantite Maxi, le decrement suit le format de la ligne. Le Maxi n'agrandit que l'accompagnement et la boisson. + +- **Q** : "Vos secrets sont-ils exposes dans le depot ?" + **R** : Non. Les tokens vivent dans `.env` (gitignore) ; `.git/config` ne contient que des scripts qui les lisent. Le credential helper evite de stocker un token en clair dans la config git. + +--- + +## Points d'amelioration conscients + +- **UML pas encore reecrit** : `state-commande.md`, `use-cases.md`, `sequence-passer-commande.md` sont encore en v0.1 (6 etats, ancien vocabulaire). A reecrire pour 4 etats / 5 roles / slots+modifiers avant d'ouvrir la PR conception. +- **PROJECT_CONTEXT sections 7 et 11** : a mettre a jour (retirer "MVP", passer a ~19 entites, rechiffrer le planning). +- **Diagrammes drawio** (MCD/MLD) : a regenerer pour 19 entites ; les `.md` utilisent du Mermaid inline en attendant. +- **SESSION_RESUME.md** : perime (Session 7), a rafraichir avec le nouveau remote dual-push et l'etat v0.2. +- **Synchronisation des merges Forgejo vers GitHub** : un merge fait dans l'UI Forgejo ne remonte pas tout seul sur GitHub ; un push-mirror Forgejo -> GitHub serait la reponse propre, a decider. +- **Token Forgejo expose dans le chat** : il a transite en clair pendant la mise en place ; le roter quand pratique (il est dedie au push, donc impact limite). + +--- + +## Etat a la reprise + +- Conception v0.2 **committee** (dictionnaire + MCD/MLD/MCT/MLT) et **deja presente dans `dev`** (`971ce0c`) via la PR #5 mergee puis resynchronisee. +- `feat/p1-conception` est a `392ba9a` ; une PR vers `dev` serait vide tant que l'UML + PROJECT_CONTEXT ne sont pas produits. +- **Prochaine action** : reecrire l'UML + PROJECT_CONTEXT sur `feat/p1-conception`, committer (un commit par doc), pousser (dual), puis ouvrir la PR `feat/p1-conception -> dev` avec le template. + +--- + +## Liens vers artefacts + +- Commits Merise v0.2 : `6ceebf7` (dictionary), `6c1cede` (MCD), `36332b4` (MLD), `6057ef9` (MCT), `392ba9a` (MLT) +- Template PR + sync dev : `971ce0c` +- Depots : `https://git.acadenice.com/AcadeNice/corentin_wakdo` (Forgejo) + `https://github.com/AcadeNice/wakdo_corentin` (GitHub, miroir synchronise) +- Docs reecrits : `docs/merise/{dictionary,mcd,mld,mct,mlt}.md` +- Tableau de decision : `docs/notes/revue-alignement-p1.md` section 7 (non versionne) +- Journal precedent du jour : `docs/journal/2026-06-04--conception-prodlike-revision.md` diff --git a/src/app/Controllers/PrivacyController.php b/src/app/Controllers/PrivacyController.php new file mode 100644 index 0000000..7c066de --- /dev/null +++ b/src/app/Controllers/PrivacyController.php @@ -0,0 +1,41 @@ + $params + */ + public function index(array $params = []): Response + { + $guard = $this->guard(); + if ($guard instanceof Response) { + return $guard; + } + + return $this->adminView('admin/privacy', [ + 'title' => 'Traitement des donnees personnelles - Wakdo Admin', + 'activeNav' => '', + ], $guard); + } +} diff --git a/src/app/Views/admin/layout.php b/src/app/Views/admin/layout.php index 8a5e77c..1db63c7 100644 --- a/src/app/Views/admin/layout.php +++ b/src/app/Views/admin/layout.php @@ -72,6 +72,7 @@ $navClass = static function (string $code, string $current): string {