Files
festipod/AGENTS.md
T
Sylvain Duchesne 53c0e095cf Code against the polyfill's published contract, and nothing else
The data layer is now reached through one pulled, version-pinned engagement
(`.project/concepts/data-layer/contract_polyfill-surface.md`, @1ecf511e9d).
That copy is the only reference: the provider's sources are never opened, and
what the contract does not answer is a gap raised with it, never worked around
here.

Surface
- `@ng-eventually/sdk` -> `@ng-eventually/polyfill`, one entry point.
- `configure` loses `getSession`, `normalizeId`, `currentUser`; the session
  belongs to the package and its own `init` captures it.
- Placement is named by scope alone -- a session is one user, so the app no
  longer passes an identity it had no way to obtain. This removes a constant
  that made every user collide on one owner's document.
- `init(...)` then `await ensureIdentity()`, in that order, as one sequence:
  React runs child effects first, so the two calls sat in the wrong order and
  the contract now makes that throw.
- `sessionId` relayed as `string | number`, `materialize` -> `read`.

A rejection means "unknown", never "absent"
Four places treated a caught error as an empty result. The worst wrote a
duplicate participation: an unknown count read as zero defeated the idempotence
guard of `joinEvent`. Also fixed: a per-document count, a silently dropped
notification shown optimistically anyway, and a failed listing that left the
owned-event set empty and disabled the materializer for the whole session.

Shared identity is not a Festipod notion
A browser context is one user. The per-scenario identity plant is deleted at
its source and its five sites; what stays is the deployment's wallet file,
which the contract requires an application to serve.

Documentation
The doctrine no longer describes how the data layer works underneath: five
leaves whose subject was internals are gone, a dozen more are re-founded on the
contract's own words, and two frozen arbitrations about a deleted screen were
removed rather than left to mislead a future session.

Test harness
It can sign in at last: cucumber runs under node, which does not load `.env`,
so the harness never received the wallet material and every scenario silently
fell back to an empty local mode. A failed sign-in is now loud on both sides.
The suite also releases what it opens and exits on its own -- runs were still
resident hours after reporting, holding a browser and two servers.

Known red: `@data` cannot be measured. The served wallet accumulates and
nothing resets it; moving the browser profile aside does not, since the data
lives in the wallet file, not the profile.
2026-08-16 12:33:14 +02:00

3.3 KiB

Festipod

Web app mobile-first où les utilisateurs créent des points de rencontre qui se greffent sur des événements publics existants, pour favoriser les rencontres. L'événement n'est qu'un prétexte/ancrage ; la valeur, c'est le point de rencontre — on s'inscrit à un point de rencontre, pas à un événement. Stack : Bun + React + NextGraph (P2P, local-first, chiffré).

Invariants à toujours garder

  • Architecture feature-based : le code est organisé par domaine métier, pas par couche technique.
    src/modules/{event,user,home,auth,workshop,meeting,notification}/
    src/shared/          # Composants, context, data — importable par tous les modules
    src/app/             # App shell (router, providers, entrée)
    src/screens/index.ts # Registre d'écrans (utilisé par Storybook)
    
  • Un module n'importe QUE depuis shared/ — jamais d'un autre module. C'est l'invariant qui rend l'archi réelle.
  • Bun-first : bun / bun install / bun test / bun build, jamais node/npm/vite/jest. bun run dev (port 3000).

Frontière SDK

Le SDK de données de Festipod est @ng-eventually/polyfill, injecté une seule fois via ngSession.configure(...).

L'engagement que le fournisseur publie est tiré dans ce repo et épinglé : data-layer, fiche contract_polyfill-surface. C'est la seule référence. On n'ouvre jamais les sources du fournisseur ni sa copie dans node_modules, pas même pour vérifier une signature. Ce que le contrat ne dit pas, ce repo ne le sait pas : un manque est remonté au fournisseur, jamais contourné ici ni documenté ici. Vaut aussi pour les tests, qui valident Festipod et jamais le SDK (bdd-testing, rule_tests-validate-festipod-not-the-sdk).

Le contrat se re-tire à chaque montée de version : python3 ~/projects/skills/concept/contracts.py pull (dérive : … check). Sa surface rétrécit — un symbole retiré est du code que l'app supprime, pas une régression à amortir.

Ne jamais décrire dans ce repo comment la couche de données est implémentée. La doctrine Festipod décrit uniquement le contrat + comment Festipod l'utilise + le domaine + l'architecture + le contrat BDD.

Doctrine du projet — concepts (livrée automatiquement)

La connaissance détaillée vit dans .project/concepts/ (système concept) : fiches courtes, typées, livrées par un hook quand tu touches leur territoire — tu n'as pas à les charger d'avance. Les 6 concepts :

Concept Couvre
functional-domain Modèle produit : point de rencontre, acteurs, concepts métier, périmètres public/protected/private par entité, découverte, défi déduplication
app-architecture Modules, invariant d'imports, app shell, routing path-based, écrans
tech-stack Bun-first, APIs Bun, build pipeline, commandes
data-layer Persistance via le SDK @ng-eventually/polyfill : entités-documents par scope, shapes SHEX/ORM, modes connected/demo, pièges
bdd-testing Cucumber multi-couches FR, contrat @ui/@data/@e2e, harness broker, cookbook
app-security Isolation déléguée au SDK (pas de contrôle d'accès dans les écrans), auth wallet, matrice d'autorisations cible

Pour documenter un fait projet : /concept document <sujet> (ne pas écrire en libre dans .project/).