Wiki/.claude/workflows/sync-bidirec.md
Corentin JOGUET 460f7effe0
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
feat(workflows): create 5 BYAN workflows for agent collaboration
Workflows (playbooks markdown) pour orchestrer les 4 agents specialises :

- README.md : index + conventions communes + integration BYAN web futur
- build-story.md : cycle complet livrer 1 story Phase 2 (bridge-dev → bridge-tester → review → CI → deploy staging → validation metier)
- sync-bidirec.md : sync Docmost ↔ Baserow event-driven (idempotence + anti-loop X-Bridge-Origin)
- release.md : process release semver (E2E staging → tag → approval → deploy prod → watch 30min)
- incident.md : SEV1/2/3 response + post-mortem blameless + runbooks
- bump-deps.md : Dependabot PRs + major bumps + Docmost/Baserow upstream

Chaque workflow specifie : trigger, acteurs (agents + humains), sequence
ordonnee avec outputs, gates humains bloquants, rollback, comm templates.

Workflows = playbooks declaratifs pour Claude main qui orchestre les agents
via Agent tool sequentiel. A migrer plus tard vers BYAN web workflow runs
quand le runtime BYAN sera fix.

Equipe complete pour formation-hub :
- 4 agents specialises (bridge-dev, bridge-tester, acadenice-devops, docmost-fork-dev)
- 5 workflows orchestrant leur collaboration
2026-05-07 19:30:48 +02:00

6.3 KiB

Workflow : SYNC BIDIREC Docmost ↔ Baserow

Orchestration de la synchronisation bidirectionnelle entre Docmost (wiki) et Baserow (DBs). Phase 2 — necessite que le bridge service soit deploye et operationnel.

Equivalent BYAN-natif : event-driven workflow avec idempotence.

Trigger

L'un des suivants :

  • Webhook Baserow row.created / row.updated / row.deleted sur table donnee
  • Webhook Docmost page.created (si configure cote Docmost custom)
  • Action explicite admin : "Sync forcee projet 42 → Docmost"
  • Cron periodique de reconciliation (Phase 3+)

Acteurs

  • bridge-dev (handler webhook + sync logic)
  • acadenice-devops (config webhooks + monitoring)
  • bridge-tester (validation idempotence + anti-loop)
  • Corentin (alerte si depassement capacite)

Sequence — type webhook Baserow row.created sur table 'projet'

[1] Webhook recu (bridge endpoint POST /api/webhooks/baserow/projet-changed)
    - Verifier signature HMAC X-Baserow-Signature (anti-spoofing)
    - Si invalide : log + 401, ABORT
    - Output : event valide

[2] Idempotence check (bridge + Redis)
    - Lire payload event_id
    - Redis : SET bridge:webhook:event:<event_id> "1" EX 86400 NX
    - Si SET retourne null (key existait) : event deja traite, ABORT 200
    - Sinon : continue
    - Output : event nouveau, marque traite

[3] Anti-loop check
    - Verifier header X-Bridge-Origin sur la row Baserow
    - Si X-Bridge-Origin == "bridge" : c'est nous qui avons cree la row, ABORT
    - Sinon : c'est un user qui a cree, continue
    - Output : event source legitime

[4] Logique metier (bridge service)
    - Pour 'row.created' sur 'projet' :
      * Fetch projet detail depuis Baserow (BaserowClient.getRow)
      * Fetch client lie (BaserowClient.getRow)
      * Calcul nom de page Docmost : "Projet [nom] - [client]"
      * Determiner space cible : "Agence" → fetch space ID
    - Output : payload pour creation Docmost

[5] Action Docmost (bridge service via DocmostClient)
    - DocmostClient.createPage({ spaceId, title, content: template_projet(projet) })
    - Header : X-Bridge-Origin: bridge (eviter loop futur)
    - Output : pageId Docmost cree

[6] Update Baserow row (bridge service)
    - BaserowClient.updateRow(projet_id, { docmost_page_id: pageId })
    - Header : X-Bridge-Origin: bridge
    - Output : projet Baserow enrichi avec docmost_page_id

[7] Cache invalidation (bridge + Redis)
    - RedisCache.invalidatePattern("bridge:projet:*")
    - RedisCache.invalidatePattern("bridge:client:<id>:projets")
    - Output : caches invalides

[8] Notif si capacite formateur depassee (cas attribution)
    - Si event = creation 'attribution' :
      * Recalculer Personne.heures_restantes_total
      * Si < 0 : notifier admin via SMTP/Slack
    - Output : notification envoyee si depassement

[9] Audit log
    - Log structurel : { event_id, source: 'baserow', target: 'docmost', action: 'createPage', success: true, duration_ms, ... }
    - Output : trace persistee

[10] Reponse webhook
     - Return 200 OK { processed: true, page_id: <docmost_page_id> }

Patterns specifiques par event

Trigger Action sync
Baserow row.created sur projet Auto-create page Docmost dans space Agence
Baserow row.created sur formation Auto-create collection Docmost (sub-pages par bloc)
Baserow row.updated sur projet/formation (titre, statut) Update title/icon page Docmost liee
Baserow row.created sur intervention Check capacite → notify admin si depassement
Baserow row.created sur attribution Notify formateur (email) + check capacite
Docmost page.created (template specifique 'compte-rendu') Auto-create row dans table comptes_rendus Baserow (Phase 3+)
Docmost share.created Log audit + notify admin (alerte data leak risk)

Gates humains

Aucun gate bloquant — c'est event-driven temps reel. Mais :

  • Notif Corentin sur depassement capacite (asynchrone)
  • Notif Corentin sur erreurs critiques (sync echec apres 3 retry)

Rollback / gestion d'erreurs

Echec Strategy
Docmost API down Retry 3x exponential backoff. Si tjrs KO : queue Redis pour retry batch
Baserow row introuvable (race condition) Fetch retry x2 avec 200ms delay. Sinon : log + skip event
Cache invalidation echec Log warning, continuer (TTL fallback 5 min)
Notification SMTP fail Log warning, alerte degraded
Loop detecte (X-Bridge-Origin manquant cote bridge writes) URGENT : alerte Corentin, audit code bridge

Anti-loop strategy (CRITICAL)

Pour eviter Docmost → bridge → Baserow → bridge → Docmost → ... boucle infinie :

  1. Header X-Bridge-Origin : tous les writes du bridge vers Baserow et Docmost ajoutent ce header
  2. Detection cote handler : si l'event provient d'une row/page avec ce flag, ABORT
  3. Idempotence event_id : meme si une boucle se forme, max 1 cycle (TTL 24h en Redis)
  4. Rate limit : max 1 sync identique / 5 min sur entite (cle: bridge:sync:<entity>:<id>)
  5. Monitoring : alerter si > 10 events identiques en 1 min (signe de boucle)

Outputs

  • Pages Docmost crees automatiquement
  • Rows Baserow enrichies avec ids Docmost (lien bidirec)
  • Caches invalides
  • Audit log evenement traite
  • Notifications metier si necessaire

Tests obligatoires

  • Test idempotence : envoyer le meme event 5 fois → un seul effet (bridge-tester)
  • Test anti-loop : simuler bridge-write → verifier que webhook ignore (bridge-tester)
  • Test rate limit : 100 events identiques en 1 min → verifier que rate limit kick in
  • Test recovery : Docmost down 5 min → events queues et processed apres recovery
  • Test webhook signature invalid : event avec mauvais HMAC → 401 (bridge-tester)

Exemple invocation

Trigger non-manuel — se declenche automatiquement quand Baserow envoie un webhook au bridge. Mais peut etre invoque manuellement pour :

  • Reconciliation : "WF SYNC : force re-sync de tous les projets non-mappes vers Docmost"
  • Debug : "WF SYNC : trace l'event ID xyz pour comprendre pourquoi il a abort"

Notes

  • Webhooks Baserow : a configurer cote Baserow UI ou API (apres deploy bridge)
  • Endpoint signature secret : BASEROW_WEBHOOK_SECRET dans .env bridge
  • Logs : toutes les operations sync sont loguees structurellement (Pino) avec event_id pour traceability