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

144 lines
6.3 KiB
Markdown

# 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