Wiki/_byan-output/fast-app/formation-hub/SESSION-RESUME.md
Corentin JOGUET 9c910995f2
Some checks are pending
CI / Lint bridge (Biome) (push) Waiting to run
CI / Type-check bridge (push) Blocked by required conditions
CI / Tests unit bridge (push) Blocked by required conditions
CI / Tests integration bridge (push) Blocked by required conditions
CI / Security scan (push) Waiting to run
CI / Docker build + healthcheck (push) Blocked by required conditions
docs(session): mark R3.1.c as delivered — 71c2aba, 41 tests, database-view Tiptap extension
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-08 00:08:20 +02:00

40 KiB

SESSION RESUME — formation-hub Acadenice (last update 2026-05-08 post R3.1.c)

RECAP SESSION 2026-05-07 (lecture obligatoire post-/compact)

Pivot strategique majeur acte

DocAdenice n'est plus un outil metier formation-hub mais un produit Notion-like generique. Le bridge a ete refactor (R1) pour supprimer l'ontologie metier (Personne/Formation/Bloc/Module/Attribution/Client/Projet/Tache/Intervention) au profit de routes generiques /api/v1/tables/*. Le metier formation-hub vit dans examples/acadenice-formation-hub/.

Memoire perso a jour

  • feedback_no_mvp.md : Corentin refuse les MVP / shortcuts. Production-like des le jour 1.
  • user_role.md : ancien conseil "MVP first" marque OBSOLETE.
  • MEMORY.md index cree.

Etat des chantiers (commits, ordres chronologique de la session)

Bridge formation-hub (bridge/, push origin+selfhost) :

c998c0d R3.1.b SSE realtime stream (Redis Streams pub/sub, Last-Event-ID, heartbeat) — 380 tests
95089c4 R3.1.a views endpoints (GET /views/table/:id + GET /views/:id/data) — 336 tests
a79c51e R2.3b bridge accepte JWT HMAC DocAdenice via DOCMOST_APP_SECRET
2ed73fa R1   refactor proxy generique style Notion
0cf6533 Bloc 5 rate limit + cache invalidation cote writes
571f5c3 Bloc 4 OIDC-ready (Authentik JWKS + service tokens)
8b42cbc chore docmost upstream clone + rename setup
022b1ee Bloc 7 webhooks Baserow + Docmost stub (HMAC + idempotence)
c4f087b Bloc 6 tests integration adapters via testcontainers

Bridge state : 380/380 tests verts (+44 R3.1.a +6 retry/fake-redis, +11 SSE route, +8 baserow-handler SSE), coverage globale ~90% lines, 3 sources d'auth Bearer (brg_*, RS256 Authentik, HS256 DocAdenice). event.ts 100%, event-bus.ts 100% lines/94.44% branches, events.ts 78.23% lines (onError Hono safety net + heartbeat-closed guard non atteignables sans serveur HTTP reel). Thresholds vitest.config.ts mis a jour pour les 3 nouveaux fichiers.

Fork DocAdenice (docmost/, gitignored, branche acadenice/main, local-only) :

71c2aba R3.1.c database-view Tiptap extension + renderer table + slash /database + SSE hook (41 tests)
4d8bd25 R2.3a /api/acadenice/permissions/me + frontend hook React Query propre
022add9 R2.2 frontend pages settings RBAC (PermissionMatrix, sidebar, i18n FR+EN)
bcd8611 R2.1 backend RBAC dynamique (catalogue 22 perms, 5 roles seed, JWT enrichi)
06c46f7 fix scopes Authentik (groups dans profile, pas un scope standard)
07d0b66 Bloc 4b OIDC client Authentik via openid-client v6.8.2
efa2644 rebrand DocAdenice (titres + emails, identifiants techniques KEEP)

Ce qui marche end-to-end (en local)

  • Bridge expose /api/v1/tables/* (CRUD generique Baserow)
  • Frontend DocAdenice /settings/roles + matrix permissions + assignation users
  • JWT DocAdenice enrichi avec acadenice_permissions[] au sign
  • Bridge consume le claim direct (pas de mapping)
  • 3 modes auth Bearer cohabitent

Catalogue 22 permissions atomiques (en code TS, fork)

pages:read|write|delete|share, space:read|create|write|delete|invite,
tables:list|create|write|delete, rows:read|write|delete,
attachments:upload|delete, users:invite|write|delete, roles:manage, admin:*

5 roles classiques pre-seed (is_system_role=true)

Owner=admin:*, Admin=tout sauf *:delete et roles:manage, Editor, Member, Guest.

Suite immediate : R3 — Tiptap node-views Notion-like (5 sous-blocs)

  • R3.1 database-view inline (embed une table/kanban/calendar Baserow dans une page) — decoupe :
    • R3.1.a bridge endpoints views (LIVRE 95089c4, 336 tests)
    • R3.1.b bridge SSE realtime (events Baserow -> stream DocAdenice, invalidation cache) — LIVRE c998c0d, 380 tests
    • R3.1.c frontend node-view Tiptap + renderer table (TanStack Table v8) + slash /database — LIVRE 71c2aba (41 tests)
    • R3.1.d frontend renderers kanban (@dnd-kit) + calendar (lib a trancher) + edit inline
    • R3.1.e Playwright e2e cross-stack (compose dev Postgres+Redis+Baserow+bridge+DocAdenice, scenarios login -> page -> insert database-view -> CRUD inline -> verify SSE update -> RBAC user-side)
  • R3.2 backlinks bidirec (page A reference B → B liste les references entrantes)
  • R3.3 slash commands custom (declarer ses propres /foo extensibles)
  • R3.4 dual editor (code raw markdown + WYSIWYG)
  • R3.5 graph view (style Obsidian / AFFiNE) — visualise les liens entre pages :
    • R3.5.1 backend : GET /api/acadenice/graph qui parse les contenus Tiptap pour extraire wikilinks/mentions, retourne { nodes, edges } avec filtres (space, tag, depth)
    • R3.5.2 frontend : page /graph avec lib graph interactif (zoom, pan, drag, click), candidates : react-force-graph-2d, cytoscape.js, sigma.js
    • Depend de R3.2 (backlinks fournit la data)

TODO connus non bloquants

  • Hook WorkspaceService.create pour seed live RBAC (actuellement seed au prochain boot)
  • Audit log mutations role/assignation
  • Mapping group sync OIDC -> acadenice_role (sync user.groups Authentik vers acadenice_user_role)
  • Pagination liste roles (assume < 100 / workspace)
  • Section "Members" dans page detail role
  • Endpoint admin debug GET /permissions/me/effective?for=<userId>

Push pending au fork

Quand un fork remote acadenice sera cree (Forgejo ou GitHub fork), push toute la branche acadenice/main du repo docmost/ sur ce remote. Aujourd'hui les commits sont local-only.


CHANGELOG R2.3b — Bridge accepte JWT HMAC DocAdenice (mode local sans Authentik)

Date : 2026-05-07 Commit local (a pusher manuellement) : voir git log -1 dans bridge/.

Pourquoi : DocAdenice signe ses JWT en HS256 avec appSecret. En local sans Authentik branche, le frontend DocAdenice qui call le bridge directement aurait echoue (le bridge ne savait valider que brg_* et JWT RS256 Authentik). R2.3b ajoute un troisieme mode au middleware : valider les JWT HS256/384/512 signes par DocAdenice via DOCMOST_APP_SECRET.

Fichiers crees :

  • bridge/src/middleware/docmost-jwt-verifier.ts (verifier HMAC + helpers decodeJwtAlg + extractDocmostPermissions)
  • bridge/tests/middleware/docmost-jwt-verifier.test.ts (28 tests unitaires)

Fichiers modifies :

  • bridge/src/lib/config.ts : 3 nouvelles vars (docmostAppSecret, docmostJwtIssuer default "Docmost", docmostJwtAudience) + helper isDocmostJwtEnabled()
  • bridge/src/lib/container.ts : champ docmostJwt: DocmostJwtVerifier | null, init si secret >= 32 chars
  • bridge/src/middleware/auth.ts : routing par algo JWT (decode header non verifie -> RSA -> OIDC, HMAC -> DocAdenice). Sources d'auth ajoutees : docmost-jwt, docmost-cookie. Refactor en helpers internes pour separer la logique attach par mode.
  • bridge/src/index.ts : injecte ctn.docmostJwt dans l'app
  • bridge/.env.example : section commentee DOCMOST_APP_SECRET / DOCMOST_JWT_ISSUER / DOCMOST_JWT_AUDIENCE
  • bridge/vitest.config.ts : threshold >= 85% sur docmost-jwt-verifier.ts
  • bridge/tests/middleware/auth.test.ts : +14 tests (DocAdenice mode + coexistence Authentik/DocAdenice + algo none rejected)

Quality gates :

  • typecheck : OK
  • lint : OK
  • tests : 292/292 verts (was 250/250 — +42 tests)
  • coverage : docmost-jwt-verifier.ts 100% lines/funcs/97.87% branches, auth.ts 96.35% lines/93.61% branches

Choix techniques :

  • decodeJwtAlg : decode header non verifie pour router vers le bon mode. Si JWT non decodable -> AUTH_INVALID immediat. Si algo n'a pas de mode actif -> AUTH_INVALID (pas de fallback silencieux).
  • DocAdenice JWT : pas de mapping groupsScopesMap — le claim acadenice_permissions[] (R2.1) est la source de verite directe (DocAdenice resout deja tout via son RBAC). scopes = permissions = acadenice_permissions[].
  • Constant-time : jose.jwtVerify utilise node:crypto.timingSafeEqual pour comparaison HMAC.
  • Algo none / ES* / EdDSA explicitement rejetes (AUTH_INVALID) — seuls RS* (mode 2) et HS* (mode 3) routent quelque part.
  • Validation claims requis dans le verifier : sub, workspaceId, type doivent etre presents et non vides.

SESSION RESUME — formation-hub Acadenice (last update 2026-05-07 R1 refactor)

Vision — DocAdenice = Notion-like generique

Pivot strategique 2026-05-07 : DocAdenice n'est plus un outil metier formation-hub mais un produit Notion-like generique. Le bridge est livre vide a un user (admin), qui cree ses tables Baserow comme il veut via UI ou API. Le code n'a aucune ontologie metier.

Composants cibles :

  • Pages / Spaces : Docmost reskin (DocAdenice fork) + spaces hierarchiques
  • Databases custom : tables Baserow exposees via le bridge /api/v1/tables/*
  • RBAC dynamique : roles custom declares cote DocAdenice qui projettent dans le JWT le claim acadenice_permissions[]
  • Bidirec backlinks : pages <-> rows + mentions cross-page (R3)
  • Slash commands custom : Tiptap node-views (R3)
  • Dual editor : edition wiki Docmost + edition table Baserow inline (R3)

Le metier formation-hub (CFA + Agence Acadenice) devient un exemple parmi d'autres : examples/acadenice-formation-hub/.

CHANGELOG depuis derniere update (R1 — refactor proxy generique style Notion)

  • R1 livre (suppression domain metier + bridge generique) :
    • Supprime tout le metier formation-hub du bridge :
      • 9 entites domain (Personne, Formation, Bloc, Module, Attribution, Client, Projet, Tache, Intervention)
      • types.ts (Role, StatutPersonne, etc.)
      • baserow-repo.ts (le mega-fichier 554 LOC avec 9 repos heritant de BaseRepo)
      • 6 routes metier (personnes, formations, projets, modules, interventions, attributions)
      • Tous les tests metier correspondants (10 tests domain + 6 tests routes + 1 test repo + 1 test rate-limit-app metier)
    • Cree le domain generique :
      • domain/table.ts (Table : id, name, databaseId, fields[], orderIndex)
      • domain/row.ts (Row : id, tableId, fields opaque, createdOn, updatedOn, order)
      • domain/field.ts (Field : id, name, type libre, primary, options nullable)
      • domain/view.ts (View : id, name, type, tableId)
      • domain/schemas.ts refonte zod (TableSchema, RowSchema, FieldSchema, ViewSchema, RowFieldsSchema permissif)
    • Cree les repos generiques :
      • repos/baserow-tables-repo.ts (list/get tables — JWT requis upstream)
      • repos/baserow-rows-repo.ts (CRUD rows par tableId — DB token OK)
      • repos/baserow-fields-repo.ts (list fields par tableId — DB token OK)
      • repos/baserow-views-repo.ts (list views + runGrid — DB token OK)
    • Cree la route generique unique :
      • routes/tables.ts avec 9 endpoints REST : GET /tables, GET /tables/:id (+ fields embarques), GET /tables/:id/fields, GET /tables/:id/views, GET /tables/:id/views/:viewId/rows, GET /tables/:id/rows, GET /tables/:id/rows/:rowId, POST /tables/:id/rows, PATCH /tables/:id/rows/:rowId, DELETE /tables/:id/rows/:rowId
      • Scopes generiques : read:tables, write:tables, admin:*
      • 501 NOT_IMPLEMENTED si DB token sur endpoint qui exige JWT (list/get tables metadata)
    • Etendu BaserowClient : listTables, getTable, listFields, listViews, getGridViewRows
    • Refactor middleware/auth.ts :
      • Supprime entierement le lookup personneRepo.findByEmail + cache Personne par email
      • Supprime strictMapping (plus de notion d'email orphelin)
      • Lit le claim JWT acadenice_permissions[] directement dans extractPermissions(payload)
      • AuthenticatedUser.scopes = union (groups -> scopes) + (permissions claim)
      • Plus de roles[] dans AuthenticatedUser — remplace par permissions[]
    • Refactor middleware/scopes.ts :
      • Supprime DEFAULT_ROLE_SCOPES (plus de mapping role formation-hub)
      • computeOidcScopes(groups, permissions, groupsMap) — la signature change
    • Refactor webhooks/baserow-handler.ts :
      • Plus de cascade rollup metier (attribution -> module + personne, etc.)
      • Pour chaque event Baserow sur tableX : invalide uniquement bridge:tables:<tableX>:list:*, bridge:tables:<tableX>:views:*, bridge:tables:<tableX>:row:<id> (si update/delete)
      • Si l'utilisateur veut des cascades cross-table, il les pose en formules/lookups Baserow qui emettent leurs propres webhooks naturellement
    • Refactor lib/cache.ts :
      • invalidateEntity(redis, entity, id?) -> invalidateTable(redis, tableId, rowId?)
      • Patterns : bridge:tables:<tableId>:* (plus de pattern par entite metier)
    • Refactor container :
      • Supprime tableIds field (plus de mapping name->id metier)
      • RepoSet = { tables, rows, fields, views } (4 repos generiques)
      • Supprime pickTableIds + resolveTableIds au boot (plus necessaire)
    • Refactor config :
      • Supprime authStrictMapping (plus de Personne lookup)
      • BASEROW_TABLE_IDS env retire (plus de mapping metier)
    • .env.example reecrit : scopes generiques, plus de mention formation-hub
    • Sortie metier vers exemples : cree examples/acadenice-formation-hub/ avec README.md, seed-baserow.md (schema 9 tables markdown), example-roles.md (Formateur, Developpeur, Direction, Support, Admin avec permissions generiques)
    • Tests : 250/250 verts (depuis 319/319). 33 tests metier supprimes ; 33 tests generiques ajoutes (4 domain : table/row/field/view, 4 repos generiques, 19 routes /tables, edge cases, errors helpers, http helpers, isOidcEnabled).
    • Coverage globale : 89.54% lines / 92.42% branches.
      • domain/** : 98.9% lines / 93.75% branches (>= 80% ✓)
      • adapters/** : 89.04% lines / 95.04% branches (>= 70% ✓)
      • middleware/auth.ts : 97.08% lines / 92% branches (>= 85% ✓)
      • middleware/rate-limit.ts : 100% (>= 85% ✓)
      • lib/cache.ts : 100% (>= 85% ✓)
      • webhooks/** : 100% (>= 80% ✓)
    • Quality gates verts : typecheck, lint, test, test:coverage.

Status R1/R2/R3

Bloc Status Detail
R1 — Bridge refactor proxy generique style Notion DONE Suppression domain metier + nouvelles routes /api/v1/tables/*
R2 — RBAC dynamique cote DocAdenice (claim acadenice_permissions[]) TODO docmost-fork-dev
R3 — Bidirec backlinks + slash commands + dual editor TODO Phase 3

CHANGELOG anterieur (Bloc 5 — rate limit + cache invalidation cote writes)

  • Bloc 5 livre (rate limit defensif + invalidation cache writes) :
    • Nouveau module src/middleware/rate-limit.ts : middleware Hono autour de RedisCache.checkRateLimit (sliding window deja teste integration). Cle derivee de l'identite avec priorites : tokenId (service token) > email OIDC (lower-cased) > sub OIDC > IP via x-forwarded-for (avec WARN log car spoofable) > anonymous. Throw errors.rateLimited(windowSeconds) avec headers X-RateLimit-Limit/Remaining/Reset. Helper exporte defaultRateLimitKey pour composer (${default}:mut).
    • Nouveau module src/lib/cache.ts : invalidateEntity(redis, entity, id?) qui mirror la logique cascade de webhooks/baserow-handler.ts (attribution -> module + personne, intervention -> tache + personne, etc.). Volontairement duplique plutot qu'extrait commun car les contextes sont differents (event_type webhook vs intent route).
    • Wire src/index.ts :
      • rateLimit(redis, {global}) sur /api/v1/* apres l'auth middleware.
      • rateLimit(redis, {mutation, keyFrom: ...:mut}) ajoute conditionnellement sur POST/PATCH/PUT/DELETE — compteur Redis distinct, plus strict.
      • Pas de rate limit sur /api/health, /api/ready, /api/webhooks/* (ces dernieres ont HMAC + idempotence Redis qui couvrent).
    • Routes mutation appellent invalidateEntity() apres write reussi (3 routes : POST /modules/:id/attribuer, POST /interventions, PATCH /attributions/:id/heures-realisees). Ferme la fenetre stale entre l'ecriture Baserow et l'arrivee du webhook (idempotent avec l'invalidation webhook qui suivra).
    • Config zod : 4 vars ajoutees avec defauts 100/60s global et 30/60s mutation. Toutes coercees + optionnelles via env (RATE_LIMIT_GLOBAL_MAX, etc.).
    • .env.example : section rate limit reecrite (commentee, defauts documentes), ancienne triple-var Phase 1 supprimee.
    • Tests : 29 tests ajoutes (290 -> 319). 11 tests rate-limit middleware (cles, priorites, 429, headers, mutation independance, anonymous fallback), 11 tests cache helper (cascade par entite, idempotence, total returned), 7 tests integration /api/v1/* vs health/webhooks. Pas de fake timers : RedisCache.checkRateLimit est mocke, on simule la reset par mutation du compteur fake (equivalent fonctionnel).
    • Coverage : src/middleware/rate-limit.ts = 100% lines / 100% branches / 100% funcs. src/lib/cache.ts = 100% lines / 100% branches / 100% funcs. Coverage globale 87.7% (+0.3pt).
    • vitest.config.ts thresholds : ajout src/middleware/rate-limit.ts et src/lib/cache.ts a 85%.

SESSION RESUME — formation-hub Acadenice (Bloc 4 — auth OIDC-ready)

CHANGELOG (Bloc 4 — auth OIDC-ready)

  • Bloc 4 livre (middleware OIDC-ready, dual mode) :
    • Nouveau module src/middleware/oidc-verifier.ts : verification JWT via jose + JWKS remote (cache 10min). Algorithmes acceptes : RS256/RS384/RS512 (pas HS* puisque cle publique). Throw errors.authInvalid() sur tout echec (signature, expired, issuer mismatch, audience mismatch).
    • Nouveau module src/middleware/scopes.ts : parseGroupsScopesMap (parse JSON env var) + computeOidcScopes (union groups Authentik + roles formation-hub). Defaut role-scope conservateur (admin -> admin:*, formateur -> read:personnes,read:formations,write:attributions, etc.).
    • Refactor src/middleware/auth.ts (mais service tokens 100% retro-compat) :
      • Schemes acceptes : Authorization: ApiKey brg_*, Authorization: Bearer brg_* (service token) OU Authorization: Bearer <jwt> OU cookie authToken=<jwt> (OIDC).
      • Si OIDC desactive (vars Authentik manquantes) + JWT envoye -> 401 (pas de fallback silencieux).
      • Lookup PersonneRepo.findByEmail (nouvelle methode) + cache Redis 60s avec key bridge:auth:personne-by-email:<sha256(email)> (RGPD : pas d'email en clair dans Redis). Cache positif et negatif.
      • Type AuthenticatedUser injecte dans Hono context : { source, tokenId?, email?, sub?, personneId?, roles, groups, scopes }.
      • Mode strict (defaut) : email orphelin (JWT valide mais pas de Personne) -> 403 FORBIDDEN. Mode permissif : autorise avec scopes des groups uniquement.
      • requireScope etendu : wildcard prefix (read:* couvre read:personnes, etc.) + admin:*.
    • Config zod (src/lib/config.ts) : ajout authentikIssuer, authentikJwksUri, authentikAudience, authGroupsScopesMap, authStrictMapping (toutes optionnelles). Helper isOidcEnabled() retourne true ssi 3 vars Authentik set.
    • PersonneRepo.findByEmail(email) : recherche via search Baserow puis filtre exact post-fetch (case-insensitive + trim). Retourne null sur miss/row-malformee (vs throw).
    • Erreurs : ajout code FORBIDDEN (vs FORBIDDEN_SCOPE deja existant) pour les cas auth-mais-pas-de-droits-metier.
    • Container DI : ajout oidc: OidcVerifier | null + groupsScopesMap. Construit au boot si vars set.
    • Tests : 30 tests ajoutes (260 -> 290). 27 tests integration auth middleware (12 cas reglementaires + 15 cas annexes), 10 tests scopes mapping, 5 tests findByEmail. Mini serveur HTTP local genere une cle RSA via jose.generateKeyPair -> sert un JWKS reel -> verifier le tape via fetch (plus realiste que mocker createRemoteJWKSet).
    • Coverage src/middleware/auth.ts = 94.11% lines / 88.37% branches (seuil >= 85% applique dans vitest.config.ts). Coverage globale stable a 87.38%.
    • .env.example enrichi (commente, prefixe # AUTHENTIK_*). .env local inchange — Authentik n'est pas branche en local pour l'instant, le mode reste service-tokens-only par defaut.
    • Lib ajoutee : jose@^6.2.3 (standard moderne OIDC, ESM-first).

SESSION RESUME — formation-hub Acadenice (last update 2026-05-07 nuit Bloc 7)

CHANGELOG depuis derniere update (Bloc 7 — webhooks)

  • Bloc 7a livre (Baserow webhooks complet) + Bloc 7b stub (Docmost) :
    • Nouveau module src/webhooks/ : signature.ts (HMAC-SHA256 hex constant-time + format sha256= accepte), types.ts (zod payloads Baserow + Docmost), baserow-handler.ts (mapping table_id -> entite + invalidation cache + cascade rollups parents), docmost-handler.ts (stub log-only avec TODO Bloc 8).
    • Route POST /api/webhooks/baserow (HMAC X-Baserow-Signature, idempotence Redis 24h, dispatch invalidation par event_type, table inconnue -> 200 ignored).
    • Route POST /api/webhooks/docmost stub (HMAC X-Docmost-Signature, idempotence si event_id present, log + 200).
    • Body brut via c.req.text() puis JSON.parse manuel (stream consomme une seule fois).
    • Config zod : docmostWebhookSecret ajoute (optional, min 16 chars).
    • 40 tests Vitest ajoutes (220 -> 260) : signature 13 / handler baserow 10 / handler docmost 2 / routes 15.
    • Coverage src/webhooks/** = 100% lines/branches/funcs, src/routes/webhooks.ts = 97.77% lines / 96.42% branches.
    • vitest.config.ts thresholds : ajout src/webhooks/** a 80%.

SESSION RESUME — formation-hub Acadenice (Bloc 6, conserve pour reference)

Document de reference pour reprendre le travail apres restart Claude Code OU /compact. Lis-moi avant de commencer la prochaine session.

CHANGELOG depuis derniere update (session 2026-05-07 nuit — Bloc 6)

5 commits ajoutes (5b2abbc, 2c5665b, c8e9b4d, 7a3fbe4, 1528017) — bridge passe de "scaffold + 4 agents recrutes" a "service utilisable end-to-end + adapters couverts a 97-100%" :

  • Bloc 1 cloture (5b2abbc) : adapters propres (TS errors fixed, biome format), 679 LOC.
  • Bloc 2 livre (2c5665b) : domain models 12 fichiers (Personne, Module, Attribution, Tache, etc.) + 111 tests Vitest, coverage 97.86% lines sur src/domain/. Decimal.js partout pour heures, schemas zod, RG-01 implementee dans Module.creerAttribution.
  • Bloc 3 livre (c8e9b4d) : routes REST Tier 1 + auth middleware + repos Baserow + tests integration mockes. 10/10 endpoints livres : GET personnes/:id/dashboard, GET formations/:id, GET projets/:id, POST modules/:id/attribuer, POST interventions, PATCH attributions/:id/heures-realisees, etc. Tests : 163/163 verts, coverage globale src/ : 70.77%.
  • Smoke test fixes (7a3fbe4) : 2 bugs decouverts via test live contre Baserow + Docmost reels :
    • BaserowClient.resolveTableIds requiert un JWT user (Baserow API distingue DB tokens / JWT). Workaround : env var BASEROW_TABLE_IDS JSON override.
    • BaseRepo.list cassait sur row malformee (Personne avec splits null != 100 → throw). Fix : try/catch toDomain par row, skip + log warn + meta.skipped exposed.
  • Bloc 6 livre (1528017) : tests integration des 3 adapters via bridge-tester. 59 nouveaux tests (220/220 verts au total) :
    • redis-cache.test.ts : 16 tests via testcontainers redis:7-alpine, 100% lines / 95.2% branches.
    • baserow-client.test.ts : 18 tests via faux serveur node:http local, 99% lines / 96.9% branches.
    • docmost-client.test.ts : 25 tests via faux serveur node:http (login + cookie + envelope {data}), 97.7% lines / 93.7% branches.
    • Choix technique : faux serveur HTTP plutot que container Baserow/Docmost (boot 60-120s incompatible CI rapide). Le code adapter tape un vrai socket TCP via ofetch/fetch — boundary integration rigoureux. Helper reutilisable tests/helpers/http-server.ts.
    • vitest.config.ts : threshold 70% lines+branches ajoute sur src/adapters/**.
    • Note design : RedisCache.checkRateLimit utilise ${Date.now()} comme membre ZSET → collision si plusieurs appels dans la meme ms. Workaround dans tests (delay 2ms). Pas critique en prod (charge plus diffuse) mais a noter.

Smoke test live — etat actuel

Stack live + bridge testes :

  • Baserow : http://localhost:8080 (workspace 112 "Acadenice", database 133 "formation-hub", 9 tables au singulier 609-617)
  • Docmost : http://localhost:3000
  • Redis bridge dedie : container bridge-redis sur 127.0.0.1:6379 (separe du docmost-redis interne)
  • Bridge : http://localhost:4000 via npm run dev dans bridge/

.env du bridge cree (gitignore confirme). Token Baserow DB cree : vyabYuYW7E5BLTTV7RGbl2Y0Mkk4hvHP (workspace 112, CRUD complet). Token bridge admin de test : brg_smoketest_admin avec scope admin:*.

7/8 endpoints OK au smoke test (le 8e bug est fix dans 7a3fbe4). Tableau detaille :

Endpoint Resultat
GET /api/health 200
GET /api/ready 200 (baserow:true, redis:true)
GET /personnes (no auth) 401 AUTH_REQUIRED
GET /personnes (bad token) 401 AUTH_INVALID
GET /personnes (good, 2 rows malformees) 200 data:[] meta.skipped:2 (apres fix)
GET /personnes/9999 404 NOT_FOUND
GET /formations 200 (2 rows)
GET /projets 200 (2 rows)

Etat des blocs Phase 2 (a jour)

Bloc Status Detail
1 — Adapters DONE 5b2abbc, coverage adapters 97-100% via Bloc 6
2 — Domain models DONE 2c5665b, 97.86% coverage
3 — Routes Tier 1 + auth + repos DONE c8e9b4d, 10/10 endpoints, 86-96% coverage middleware/routes
3.2 — Refactor erreurs domain typees + routes /blocs /clients /taches TODO DomainError sub-classes (RGViolationError, ConflictError) pour remplacer mapping par texte
4 — Auth middleware OIDC-ready DONE dual mode service-token + Authentik JWT/cookie, mode OIDC desactive en local (vars env absentes), 27 tests coverage 94.11%
4b — Docmost OIDC fork (rebrand DocAdenice + login Authentik) TODO docmost-fork-dev — depend Bloc 4
5 — Rate limit + cache invalidation DONE middleware/rate-limit.ts (100%), lib/cache.ts (100%), wire global + mutation sur /api/v1/*, invalidation 3 routes write
6 — Tests integration adapters DONE 1528017, 59 tests, redis-cache 100% / baserow 99% / docmost 97.7% lines
7a — Webhook Baserow (HMAC + idempotence + invalidation cache) DONE webhooks/* + routes/webhooks.ts, 100% coverage
7b — Webhook Docmost (stub) STUB log-only, handlers metier en Bloc 8
7 — Sync bidirec (write-back Baserow apres event Docmost) TODO depend de Bloc 8 (parser node-views)
8 — Tiptap node-views Docmost TODO docmost-fork-dev, Phase 2.3+ — PROCHAIN
9 — Bidirec backlinks TODO docmost-fork-dev, Phase 3
10 — Doc utilisateur + release v0.1.0 TODO tech-writer + acadenice-devops

Coverage globale (post-Bloc 7)

  • All files : 87.38% lines / 86.31% branches (post-Bloc 4)
  • adapters/ : 98.73% lines / 95.04% branches
  • domain/ : 97.86% lines / 98.16% branches
  • routes/ : 96.58% lines / 76.19% branches (incluant webhooks.ts 97.77%)
  • webhooks/ : 100% lines / 100% branches (signature, baserow-handler, docmost-handler, types)
  • middleware/ : 92.12% lines / 85.36% branches (auth.ts 94.11/88.37, oidc-verifier.ts 88.88/58.33, scopes.ts 100/95.45)
  • lib/ : 49.18% lines (config.ts/container.ts non couverts — bootstrap)
  • repos/ : 59.53% lines (BaseRepo abstract — couvert via repos concrets)

Vote pour la prochaine session

Recommandation pour la reprise :

  • Option A : DocAdenice rebrand + Bloc 4b — fork Docmost pour ajouter login Authentik + theme Acadenice. Le bridge cote serveur est pret (Bloc 4 livre), reste a brancher Authentik live + faire en sorte que Docmost emette le cookie authToken ou un Bearer JWT vers le bridge.
  • Option B (recommandee si pas d'Authentik live) : Bloc 8 — Tiptap node-views Docmost (docmost-fork-dev). Forke Docmost AGPL, ajoute les nodes custom baserow-row / baserow-list qui font des reads via le bridge /api/v1/* et des writes via webhooks Docmost (handler stub deja en place — il restera a parser le payload reel et appeler les repos Baserow). Cest la piece UI manquante qui rend le bridge visible cote utilisateur.
  • Option B : Bloc 9 — tests E2E Playwright contre la stack live (bridge-tester) pour figer le comportement actuel des routes + webhooks avant que le fork Docmost ne bouge.
  • Option C : Bloc 5 — rate limit + cache invalidation middleware. Court (~1h). RedisCache.checkRateLimit existe deja, faut le wire dans Hono. Pas bloquant pour Bloc 8.
  • Option D : Bloc 3.2 — refactor erreurs domain typees + routes restantes (/blocs, /clients, /taches). Pas urgent.

Vision projet en 3 lignes

Notion-like self-host pour Acadenice (CFA + Agence dev) en Stack composite :

  • Docmost (wiki AGPL, illimite users) + Baserow (DBs MIT, illimite users) + bridge service custom Node TS (Phase 2)
  • Suivi heures formateurs/devs unifie via entite PERSONNE pivot multi-roles, scope etendu CFA + Agence approuve.
  • Cible 90-100 users total. Production-like des le jour 1.

User & equipe

  • Corentin JOGUET (corentin@acadenice.fr) — AdminSys/DevOps solo, bras droit de Yan (resp tech). Decisionnaire technique.
  • Yan (resp tech), Ludo (fondateur), Sophie (co-fondatrice) — validation business. Pas a confondre avec Corentin.

Localisation des artefacts

Resource Chemin / URL
Repo source of truth https://git.acadenice.com/AcadeNice/Wiki (Forgejo selfhost, public)
Repo mirror GitHub https://github.com/AcadeNice/wiki (private, plan free)
Local dev /home/imugiii/Documents/jsap/formation-hub/
Wiki conception (19 docs) https://wiki.acadenice.com/collection/agence-rd-notion-like-v9nvBLodst
BYAN web project id 4e72108b-dc05-4938-a1a9-530e1551ed52
Stack locale http://localhost:3000 (Docmost) + http://localhost:8080 (Baserow)

Phase 0 — Conception (DONE — 19 docs)

Localises dans docs/ du repo + miroir Outline collection R&D Notion-Like :

# Doc Status
01 Discovery Recap OK
02 Scope etendu CFA + Agence (APPROVED 2026-05-07) OK
03 Decision Records (5 ADR) OK
04 CDC Technique (stack + NFR + roadmap + couts) OK
05 Data Dictionary OK
06 Merise MCD (5 vues splittees) OK
07 Merise MLD (5 vues splittees) OK
08 Merise MCT OK
09 Merise MOT OK
10 State Diagrams OK
11 UML Use Cases (4 vues splittees) OK
12 UML Class Diagram (5 vues splittees) OK
13 UML Activity Diagrams OK
14 Repo Structure & GitOps OK
15 Baserow MPD OK
16 Plan de tests OK
17 Plan de deployment OK
18 Plan d'operations OK
19 Bridge API design (incl. MCP server Phase 3+) OK
99 DRAWIO Architecture infra (XML) OK

Phase 1 — Build local (en cours — local seul, prod-like)

OK et teste live

Iteration Detail
I1 — Baserow tables + liens 9 tables (PERSONNE + CFA + Agence) + 10 link FK avec related fields renommes
I2 — Baserow formulas 17 formulas (rollups + heures_restantes)
I3 — Docmost setup Workspace Acadenice + 3 spaces (CFA, Agence, Interne) + page Welcome + share link
I5a — Healthcheck etendu UI + API Docmost/Baserow + container status — 4/4 OK

Partiellement OK

Item Probleme Fix prevu
I4a — Forms publics Baserow Form cree mais endpoint /api/database/views/form/{id}/field-options/ retourne 404 sur Baserow 1.30 A investiguer (URL exacte selon version) — bridge-dev
I4b — Space etudiant Docmost Slug fix applique (re.sub), limit fix (200→100). Pas re-teste. Re-run pour confirmer — quick
I5b — Cron install Script ecrit non-execute (sudo requis) A run sur la prod quand VPS sera up — acadenice-devops
I5c — Backup test E2E Script scripts/backup.sh existant, pas teste end-to-end avec restore Test mensuel selon plan ops — acadenice-devops

TODO Phase 1 finale

Item Pour qui
Test rollups Baserow live (1 personne + 1 formation + 1 attribution) Corentin
Migration data initiale (formations/clients existants) Corentin + Yan + Sophie
Onboarding 5-10 testeurs (Yan, Ludo, Sophie + 2-3 formateurs + 2 devs) Corentin
Setup VPS staging (Hetzner CPX21) acadenice-devops
Configurer Forgejo Actions runner (infra/forgejo-runner/) acadenice-devops

Phase 2 — Bridge service (en cours, Blocs 1-3 + fix smoke test livres)

Code Phase 2 = MAIN focus de la session 2026-05-07 soir + suite. Brief complet dans docs/19-bridge-api-design.md. Architecture :

  • 5 missions : expose Baserow, webhooks Baserow, sert Tiptap nodes Docmost, orchestre workflows metier, sync bidirec Docmost ↔ Baserow
  • Stack fixee : Node 22 + Hono + zod + ofetch + ioredis + pino + decimal.js + Vitest + Biome
  • Endpoints REST /api/v1/* versionnes (10 livres Tier 1)
  • Webhooks anti-loop via header X-Bridge-Origin
  • MCP server (Phase 3+) co-located dans le meme service

Cf section "Etat des blocs Phase 2 (a jour)" en haut du document pour le status detaille de chaque bloc.

Agents BYAN crees pour le projet

Dans .claude/agents/ du repo :

Agent Mission Quand l'invoquer
bridge-dev Code TS du bridge service (adapters, domain, routes, webhooks) Toute tache code metier dans bridge/
bridge-tester Tests Vitest + testcontainers + E2E Playwright + coverage Toute tache test bridge ou validation AC
acadenice-devops Infra (Docker, Traefik, Forgejo, backups, monitoring, CI/CD) Toute tache ops/deploy/infra
docmost-fork-dev Fork Docmost + Tiptap node-views React + bidirec backlinks Phase 2.3+ et Phase 3 (UI custom)

Invocation : Agent tool avec subagent_type='bridge-dev' (ou autre nom) apres restart Claude Code.

Chaque agent a un brief detaille (~150-200 lignes) avec :

  • Mission + contexte projet
  • Stack technique fixee
  • Specialisations
  • Conventions code & commits
  • Limites (ce qu'il ne fait PAS)
  • Resources & references

Workflows BYAN proposes (a creer plus tard)

Pas encore crees. A faire via BYAN web ou skill byan-bmb-workflow-builder :

Workflow Phases
WF formation-hub BUILD story → bridge-dev code → bridge-tester tests → user review → push → deploy staging → smoke tests. Boucle si fail.
WF formation-hub SYNC webhook Baserow → bridge handler → cache invalidation → notif → log audit. Idempotence event_id.
WF formation-hub RELEASE tests E2E staging → CHANGELOG update → tag semver → approval review → deploy prod → 30 min watch period → rollback si fail.

Decisions structurelles (a respecter)

Decision Reference
Stack Docmost + Baserow + bridge custom ADR-001 doc 03
Path B : UX quasi-unified via Tiptap nodes ADR-002 doc 03
Monorepo trunk-based development ADR-003 doc 03
Postgres separe par service ADR-004 doc 03
Bridge stack Node 22 + Hono ADR-005 doc 03
Scope etendu CFA + Agence via PERSONNE pivot ADR-006 doc 02
Etudiants pas modelises en Baserow, juste users Docmost doc 02
API Docmost = Enterprise paye → on utilise endpoints internes (AGPL legal) doc 19
Repo source of truth = Forgejo selfhost (git.acadenice.com), GitHub mirror optionnel doc 14
Pas de mirror auto decide pour l'instant session 2026-05-07
Local seul pour le moment (pas de staging deploy) session 2026-05-07
Pas de modification des docs conception sans ADR session 2026-05-07

Credentials utilises (dans .env gitignore — a regenerer si compromis)

Racine .env :

DOCMOST_ADMIN_EMAIL=corentin@acadenice.fr
DOCMOST_ADMIN_PASSWORD=ton-pwd123456
BASEROW_EMAIL=admin@acadenice.fr
BASEROW_PASSWORD=ton-pwd123456
GITHUB_TOKEN=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  (a revoke apres session)
FORGEJO_TOKEN=cc21fee2913b6043fb68f93d8b6c184fac4671f4  (admin AcadeNice)
OUTLINE_TOKEN=ol_api_s2EqjDW5SPlXzM4vqiZaMd8UD00jsnespK4rRs

bridge/.env (cree session 2026-05-07 soir) :

NODE_ENV=development
PORT=4000
LOG_LEVEL=debug
BASEROW_API_URL=http://localhost:8080
BASEROW_API_TOKEN=vyabYuYW7E5BLTTV7RGbl2Y0Mkk4hvHP
DOCMOST_API_URL=http://localhost:3000
REDIS_URL=redis://127.0.0.1:6379
BASEROW_WEBHOOK_SECRET=smoke-test-webhook-secret-32chars-min
BRIDGE_API_TOKENS=[{"token":"brg_smoketest_admin","name":"smoketest","scopes":["admin:*"]}]
BASEROW_DATABASE_ID=133
BASEROW_TABLE_IDS={"personne":609,"formation":610,"bloc":611,"module":612,"attribution":613,"client":614,"projet":615,"tache":616,"intervention":617}

Container Redis dedie pour bridge (separe du docmost-redis interne) :

docker run -d --name bridge-redis -p 127.0.0.1:6379:6379 redis:7-alpine

Commits Forgejo selfhost (cumulés)

Session 2026-05-07 jour :

668576c chore: initial commit (55 files, 7986 insertions, conception complete)
d510bdd ops: fix CI run + bump testcontainers + doc 19 sync bidirec
991d172 ops(ci): trigger CI on main + disable auto deploy-staging Phase 0
66ff909 ops(ci): add vitest config + sanity tests
d8e8bde ops(ci): fix docker-build .env before compose
ecb7a44 ops(infra): add Forgejo Actions Runner skeleton
6724be6 feat(baserow): add seed script + Fast-App iteration 1 artifacts
a0266b8 feat(baserow): add formulas pass + related field naming
5d02977 feat(docmost): manual setup guide for iteration 3
8a676d2 feat(docmost): add seed.py via internal endpoints
d5558ca fix(docmost-seed): handle data envelope + add format field
1d71364 feat(seed): add I4 forms publics + space etudiant + I5 healthcheck
7d4d2cd feat(agents): create bridge-dev specialized agent (1st BYAN INT)
b37220d feat(agents): complete BYAN INT for 3 more agents + session resume MD
460f7ef feat(workflows): create 5 BYAN workflows for agent collaboration

Session 2026-05-07 soir :

5b2abbc feat(bridge/adapters): bloc 1 propre — BaserowClient + DocmostClient + RedisCache
2c5665b feat(bridge/domain): bloc 2 — domain models + tests Vitest (coverage 97.86%)
c8e9b4d feat(bridge): bloc 3 — routes REST Tier 1 + auth + repos Baserow (10 endpoints)
7a3fbe4 fix(bridge): smoke test fixes — skip rows malformees + BASEROW_TABLE_IDS override
[NEXT]  docs(session): update SESSION-RESUME apres Bloc 1+2+3 + smoke test

Memoire BYAN persistee

/home/imugiii/.claude/projects/-home-imugiii-Documents-jsap/memory/ :

  • user_role.md : Corentin JOGUET profil + role chez Acadenice
  • project_notion_like.md : projet detaille, scope, stack, decisions, IDs externes
  • reference_outline.md : Outline wiki Acadenice config + endpoints

Pour la prochaine session — checklist demarrage

[ ] Lire ce SESSION-RESUME.md (ce CHANGELOG en haut + section "Etat des blocs Phase 2")
[ ] Verifier stack locale up : docker compose ps + docker ps | grep bridge-redis
    (si bridge-redis absent : docker run -d --name bridge-redis -p 127.0.0.1:6379:6379 redis:7-alpine)
[ ] Verifier git pull a jour : cd formation-hub && git pull
[ ] Verifier bridge boot : cd bridge && npm run dev (logs dans /tmp/bridge-smoke.log si en background)
[ ] Smoke quick : curl http://localhost:4000/api/ready
[ ] Decider quoi attaquer en premier (cf section "Vote pour la prochaine session" en haut) :
    - Option A : Bloc 7 — webhooks Baserow + sync bidirec (gros, recommande)
    - Option B : Bloc 5 — rate limit + cache invalidation (court, prerequis)
    - Option C : Bloc 6 — tests integration adapters via bridge-tester
    - Option D : Bloc 3.2 — refactor erreurs domain typees + routes restantes

Fast-App workflow local (artefacts dans _byan-output/fast-app/formation-hub/)

Fichier Contenu
pitch.json Validated 2026-05-07
backlog.json 15 features MoSCoW + 5 WONT validated
cdcf-stories.json 10 stories Connextra+Gherkin pour Phase 1
plan.json 7 iterations BUILD
dispatch.json Repartition Claude/Corentin/equipe
build-state.json current_iteration: 1, completed phases 1-6 (workflow Fast-App)

Tao Acadenice respecte tout au long : direct, structures avec tirets, zero emoji, orientation solution.

Pret pour la suite. Bonne session.