Commit Graph

15 Commits

Author SHA1 Message Date
Sylvain Duchesne 4148df8fcb Record what running the flow proved, and fix the leaf that caused one defect
The create-and-participate flow was driven in a real browser for the first time.
It had been called correct by construction -- typecheck, build, reading -- and
the probe found three defects none of those could see.

Two open bugs, both major, both filed rather than worked around:
signing up to your own event makes the NEXT connection fail outright
(`ensureIdentity()` rejects inside the data layer's own inbox processing, 3/3,
reproduced on a fresh origin and identity), and the participant count does not
converge in the same session (2/2, 120 s and 75 s). Whether it converges at the
next connection is recorded as UNKNOWN and unmeasurable, because the first bug
stops the app from getting there.

The doctrine defect is the one worth the trouble. `knowledge_build-pipeline`
said production builds into `dist/`; `knowledge_deployment` said the container
runs `bun run start` from `src/`. Both were written down, they contradicted each
other, and the code followed the wrong one -- which is how a deployed app that
could sign nobody in was shipped. The three paths now live in one table whose
discriminating column is what is actually served: dev `src/`, production `src/`,
and `dist/` served by nothing at all. A build artefact nobody serves is a trap
for the next reader who assumes otherwise.

The probe method itself is written down: the suite cannot run, a targeted probe
can, and the difference is worth knowing before concluding that nothing is
measurable.

Stated once where a reader meets it: honest steps do not add up to an honest
flow. Every gesture in the sign-up reports correctly, and the user is still told
they participate while the count never moves and the next connection fails.
2026-08-16 16:46:10 +02:00
Sylvain Duchesne cebd54c978 The doctrine says what the code does again
Eighteen leaves had drifted behind today's changes, and several taught the exact
mistakes that were just removed.

Corrected, among others: the identity and the profile were conflated, and
`knowledge_context-internals` still described the impersonation fallback and the
principal-to-username join as current mechanisms. `caveat_identity-ids-in-screens`
and `knowledge_data-modes` still had `joinEvent` logging and returning where it
now throws. The shape listings still carried the event host. And
`knowledge_screen-pattern`'s canonical sample taught a toast written beside the
call rather than after the write -- the very bug fixed this afternoon, sitting in
the file a new screen is copied from.

New leaves for what had no home: write rights read from the owned-document
listing, with its three states and its deliberate residual; the owner's ruling
that no "may I write this?" call is coming, so the list is the answer for good;
and the `@data` suite losing its fixtures now that the seed writes nothing into a
connected wallet.

Four doc-debt files settled, including one the hook opened mid-pass.

Worth recording how one leaf died: a caveat was written for the unguarded edit
screen exactly as briefed, then deleted on finding the fix had landed while the
pass ran. Doctrine tracks the tree, not the instructions it was given.
2026-08-16 15:25:34 +02:00
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
Sylvain Duchesne b6a6b14fad docs(concept): passer les 59 fiches de doctrine en anglais
Convention du projet pour la documentation projet. Traduction fidèle, sans
changement de fond : mêmes fiches, mêmes sections, mêmes liens. Le lint est
identique à la baseline (59 leaves, 0 nouveau lien cassé, wikilinks bit-à-bit
inchangés) et aucun `.feature` n'a été touché.

Le `summary:` du frontmatter est traduit lui aussi — c'est ce que le hook affiche
dans l'index, il porte autant que le corps.

RESTENT EN FRANÇAIS, délibérément :
- les fichiers .feature (convention explicite du projet : Etant donné/Quand/Alors)
  et le bloc Gherkin cité dans brief_2026-07-06 ;
- les libellés d'interface cités en prose (« Entrer », « ✓ Je participe »,
  « Voir tous les participants », « participant inconnu »…) : ce sont des chaînes
  réelles de l'app, pas de la prose ;
- les noms de scénarios BDD ;
- les `triggers.keywords` des _overview : jetons de matching du hook, et la
  conversation reste en français — les traduire aurait cassé la livraison.

EFFET SECONDAIRE UTILE : relire intégralement a fait remonter des contradictions
et des péremptions que personne ne voyait section par section. Notées, non
corrigées (hors périmètre de la traduction) :
- rule_document-per-entity décrit la lecture via readEntities/readUnion/
  registerDoc/bumpRead, que rule_app-uses-sdk-surface-only déclare SUPPRIMÉS au
  profit de watchShape/useShapeQuery. Une règle qui décrit des APIs retirées est
  activement trompeuse — à traiter en priorité.
- brief_2026-07-06 §P3 réaffirme « prouvé par l'e2e D.2, sans reload » juste
  après l'encadré qui déclare cette phrase fausse et sur-cadrée.
- knowledge_data-modes cite useShapeWithDefaults() là où useShapeQuery est
  documenté ailleurs.
- knowledge_stack-and-commands : build:orm pointe ./src/shapes/* alors que les
  shapes vivent sous src/shared/shapes/.
- knowledge_screens / knowledge_routing : ConnectScreen décrit comme absent du
  registre mais présent en route.
- brief_2026-05-18 : « identifié si connecté » était ambigu en français (session
  ouverte vs est une connexion) ; l'anglais a forcé à trancher — rendu par
  « if a connection », à confirmer côté produit.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014GbGgNEHRejVKoREvFuDFg
2026-07-28 16:27:24 +02:00
Sylvain Duchesne 96e28a702f docs(concept): solder la doc-debt des 6 concepts
Dette accumulée depuis le 13/07 (27 marqueurs). Au-delà du vidage, trois
corrections de doctrine réellement fausse — c'est ce que le reconcile devait
attraper :

- app-security : `sharedWallet.ts` capture le mot de passe à l'ÉVALUATION du
  module. Tant qu'un repli existait, un global posé trop tard ne faisait que
  dégrader ; depuis que le wallet partagé est l'unique mode, il rend la barrière
  INUTILISABLE (écran d'erreur, aucun champ). Conséquence non anticipée de la
  décision shared-wallet-only → nouveau caveat.
- bdd-testing : la doctrine rendait des tests faux-verts. `ctx.newPage()` sur le
  profil persistant relit l'IndexedDB local et ne prouve JAMAIS la durabilité
  broker ; seul un contexte partagé neuf tranche. Un agent suivant la doctrine
  écrivait un test qui passe sans rien vérifier → nouveau caveat.
- app-architecture : `knowledge_routing` décrivait encore une route `/login`
  disparue, et `knowledge_screen-pattern` citait `LoginScreen` qui n'existe
  plus. Nouveau caveat sur les deux espaces d'id vus depuis un écran.

Aussi : data-layer/knowledge_context-internals décrit la jointure
participation→profil et corrige un mécanisme de changement d'identité périmé ;
tech-stack raccroche la table des scripts au vrai point d'entrée cucumber ;
functional-domain note qu'« implémenté » ≠ « durable ».

Trois marqueurs soldés comme sans objet : ils visaient
`reconnexion-socket-mort.{feature,steps.ts}`, absents de l'arbre ET de tout
l'historique — expérience abandonnée avant tout commit. Ce qu'elle devait
établir est capturé ailleurs (caveat de durabilité, post-mortem polyfill, fiche
INBOX socket-death).

Liens morts vers une décision disparue avec le concept `nextgraph-platform`
réparés. Reste au lint : le brief 07-06 (superseded) porte des file:line et des
références aux internes NextGraph — laissé intact, il décrit l'Option-B encore
implémentée et se dissoudra à la graduation.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014GbGgNEHRejVKoREvFuDFg
2026-07-27 14:43:09 +02:00
Sylvain Duchesne a21d9b0735 docs(concept): durabilité écriture↔déconnexion, décision wallet-partagé-unique, rule_bun-first (install pnpm)
caveat_write-durability-across-disconnect + decision_2026-07-20 (wallet partagé = seul mode ; identifiant ≠ username profil) + amendement bun-first. Marqueurs _debt.md inclus (voyagent avec la branche, à réconcilier avant push).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014GbGgNEHRejVKoREvFuDFg
2026-07-20 13:16:04 +02:00
Sylvain Duchesne 65bd67cc20 Isolation deux-identités: test permanent + le créateur ne participe plus
Deux corrections produit/tests demandées, empiriquement validées au broker réel.

1. Créateur ≠ hôte (décision produit). Il n'y a PAS de notion d'hôte : un
   événement est public, simplement signalé par le créateur, qui n'est PAS
   obligé de participer. `createEvent` n'écrit plus de participation-hôte et
   `participantCount` démarre à 0 ; le matérialiseur du propriétaire dérive
   `participantCount = |inscriptions actives|` (plus de base « +1 hôte »).

2. Isolation deux-identités : le trou réel était l'ABSENCE d'un test de
   régression, pas un bug de code actif. Reproduction empirique (DIAG instrumenté,
   retiré) : la fuite n'apparaît QUE si le reset `useEffect([username])` est
   désactivé ET les caps vides (docs persistés d'une session antérieure sur wallet
   gonflé) — le reset en place la neutralise. La sighting live venait d'un état
   wallet pré-fix + identifiant réutilisé. Ajout du test permanent manquant :
   - isolation-deux-identites.feature (@data) : A crée+rejoint E, une identité
     fraîche B sur le même wallet ne voit E ni sur son accueil, ni via
     isParticipating(E,B), et ne lit aucune participation portant le principal de A.
   - us-13 : « Le créateur ne participe pas automatiquement » (count 0,
     isParticipating false autoritatif, puis join→1, leave→0).

Harness: 4 helpers permanents (switchIdentity, currentIdentifier, homeEventTitles,
currentParticipations) pour piloter/observer l'identité en test.
Scénarios @multibrowser/us-7 réalignés (compteur 0→1 au lieu de 1→2).
Doctrine mise à jour (context-internals, actors-and-concepts).

Gates: build OK, tsc propre, @data verts (inscription, désinscription,
idempotence, compteur dérivé, auth ×4), lib @ng-eventually/client non touchée.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 18:55:42 +02:00
Sylvain Duchesne e951eaaf96 feat(auth)+refactor(app): identifier at the access barrier; adopt the lib fidelity refactor
Consumer-side of the @ng-eventually/client fidelity pass, plus the identifier UX:

- Identity: the user types an IDENTIFIER at the access barrier (AccessGateScreen),
  in the same act that opens the shared wallet — the separate 'pick a username'
  screen (ConnexionScreen) is removed. The identifier is a technical id (a pseudo
  in practice, not a Festipod username), normalized (trim, @-stripped, lowercased)
  and persisted before the broker redirect, then handed to the SDK as the identity.
  AccountContext keeps its API but its stored value is now this normalized id.
- Relationship/connections are app-owned: new src/shared/utils/connections.ts holds
  the bilateral registry and maps each link to the SDK's directed grantRead(doc,
  grantee); the lib no longer carries a connection concept. Rewired FestipodData
  and the @data harness to it.
- Login removed: accounts use the SDK's IdentityStore (set/clear/get); no faux
  login/logout framing in the SDK boundary.

Doctrine reconciled: app-security (knowledge_authentication flow, knowledge_trust-model
directed grants, decision_2026-07-06_identifier-at-access-barrier), data-layer
(knowledge_context-internals: stable id principal + single-seed), app-architecture
(knowledge_screens auth inventory), bdd-testing (caveat_wallet-bloat-hang).

App gates: tsc no new errors, build OK. @data path unaffected (harness bypasses the
gate and sets identity directly; login() is not on that path).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-06 14:52:40 +02:00
Sylvain Duchesne 8bb19b687b feat(data): union read model — list via anchorless sparql_query, hang eliminated
Replace the reactive-ORM per-entity fan-out read (which HUNG 75s: orm_start_graph
opened every scope graph and RepoNotFound on any fresh/unsynced doc aborted the
subscription) with the read model:
- readEntities.ts → lib readUnion: resolve the by-need doc set (my own scope docs
  via listMyEntityDocs + public events via the discovery index — NOT all-accounts
  fan-out), then ONE anchorless union sparql_query (GRAPH ?g, VALUES-pinned). Map
  to app types. Re-query on a change signal (no reactive union query).
- countUserParticipations no longer fans out over all accounts (own docs only).
- await loadTestData in the seed step; deleted orphaned useShapeWithDefaults;
  removed the old multistore-stopgap fan-out scenarios; added the read-model-probe.
- Doctrine: rule_document-per-entity read half + _overview rewritten to the union
  model (write half unchanged).

Result: the 75s ORM hang is ELIMINATED (0 hangs; build/tsc/lib-93-tests green;
boundary clean). @data is NOT yet fully green: remaining failures are 90s step
timeouts in the test-harness broker data ops (clearWallet / runUnionProbe / seed)
this run — a harness/broker-op issue, not the read path. To finish separately.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-05 20:49:01 +02:00
Sylvain Duchesne bc3d270bd4 chore: scrub simulation vocabulary from app comments + settle doc-debt
Enforce the boundary in code-comments and doctrine (adversarial-review cleanup):
- App comments in the data plane no longer narrate the SDK's internals: "emulated
  curator"→"the inbox read", "fan-out"→"discovered", removed store-placement
  reasoning and "polyfill/shim/mono-store" wording (FestipodDataContext,
  registration, storeRegistry, ngSession, AccountContext, isolation, sharedWallet,
  AccessGateScreen). Executable logic unchanged.
- Removed dangling references to the dissolved `nextgraph-platform` concept and
  `brief_2026-06-15_shared-wallet-shim` from app code.
- knowledge_nextgraph-stack: dropped "mécanique d'émulation" from the boundary note.
- Settled and deleted all concept _debt.md (confirmatory; target leaves clean).

(Test-infra under workshop/ + generated features.ts still carry some simulation
vocabulary — parked as a separate below-SDK decision.)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-04 09:58:52 +02:00
Sylvain Duchesne a436c3bd79 feat(data): discover public events via the global index (not fan-out)
On creating a public event, Festipod submits it to the discovery index (an SDK
call); the discovery screen reads the index instead of enumerating accounts. The
app knows nothing of the index's owner, inbox, or materialization — it treats the
lib as a finished SDK whose discovery is a global index. No store ids.

Unit-validated in the lib (79 tests). @data broker validation deferred: the
NextGraph broker (nextgraph.net/eu) was unreachable at run time — to be re-run
in T03.d once the broker recovers.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-04 09:33:42 +02:00
Sylvain Duchesne 337a1e000d feat(data): activate isolation — declare identity + connections to the SDK
Festipod performs the domain acts that make isolation real: AccountContext
declares the current identity at login/change; FestipodDataContext declares its
connections (friendships) to the data SDK. Reads then discriminate by scope
through the SDK (private→owner, protected→owner+connections, public→all) — no
app-side filtering, no store ids, no awareness that isolation is emulated. New
@data scenario proves an unconnected account can't read another's protected
entity but can after connecting; public stays visible. @data 21/21.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-03 23:59:03 +02:00
Sylvain Duchesne db9eb1cf47 doctrine: Festipod treats @ng-eventually/client as a finished NextGraph SDK
Enforce the project boundary: Festipod is written as if NextGraph were a mature,
finished SDK; @ng-eventually/client IS that SDK. NO current-NextGraph-state,
simulation, polyfill, shim, mono-store, store-id or broker-internal knowledge
remains in this repo — it now lives in the @ng-eventually/client repo.

- Dissolved the `nextgraph-platform` concept entirely (12 leaves — all
  current-state/simulation, now in the lib's docs/). Rescued the genuine domain
  parts into functional-domain/knowledge_data-scopes-and-discovery.md (which
  entity → which scope; product-level discovery/notification intent), framed as
  SDK usage with no mechanism.
- data-layer re-anchored to "how Festipod persists via the SDK": stripped
  mono-store/private_store_id/RepoNotFound/DataCloneError/FESTIPOD_MULTISTORE.
  Deleted the current-SDK compensation leaves (private-store-scope, multistore,
  the 2026-03-17 ADRs, conditional-ng-init). Kept/reworded the domain + app
  leaves; caveat_participation-deletion reduced to the domain contract.
- app-security reworded (isolation delegated to the SDK; app trusts it).
- AGENTS.md: dropped the nextgraph-platform row, reworded data-layer/
  functional-domain/app-security, added the "Frontière SDK NextGraph" note.
- Fixed dangling [[links]]; concept lint clean (43 leaves).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-03 23:23:23 +02:00
Sylvain Duchesne aabb2b77f7 doctrine: reconcile store/document model + T02 features into concepts
- data-layer/caveat_multistore-is-multi-document (new): the recurring store vs
  document confusion. Two axes — (A) which native store, (B) documents within a
  store. FESTIPOD_MULTISTORE toggles axis B (multi-document), not multi-store.
  Isolation (ReadCap) is per-document. As of T02.h the default path writes
  shareable entities to the real protected store (axis A, step 1).
- rule_private-store-scope: rewritten — shareable entities now scope/@graph the
  protected store; private anchors the shim/inbox + settings; "never did:ng:i"
  kept. decision_2026-03-17 marked partially superseded.
- knowledge_stores-permissions: ⚠️ store↔document callout.
- knowledge_entities: MeetingPoint/Notification now persisted (not local-only).
- nextgraph-platform: decision_2026-06-17 records the emulated inbox; fork-inbox
  brief marked short-circuited; discovery-model divergence (shipped fan-out vs
  global-index target) flagged for confirmation.
- functional-domain/knowledge_roadmap, bdd-testing leaves updated. All doc-debt
  settled; lint clean (60 leaves).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-03 15:51:57 +02:00
Sylvain Duchesne 0294e3992f docs(concepts): migrate project docs into 7 concepts + code-grounded audit
Migrate .project/{knowledge,decisions,briefs} and the always-loaded
AGENTS.md/CLAUDE.md into the in-repo `concept` system (hook-delivered,
typed leaves). Then audit the actual code to verify the migrated doctrine
and capture knowledge that lived only in the source.

Concepts (53 leaves):
- functional-domain — produit : point de rencontre greffé, acteurs, déduplication
- app-architecture — modules, invariant d'imports, routing, écrans, styling-system,
  screen-pattern, cookbook d'ajout d'écran
- tech-stack — Bun-first, APIs, build pipeline, deployment (Dockerfile), commandes
- data-layer — NextGraph mono-store, shapes, modes, règles + caveats (suppression,
  champs non persistés, internals du contexte)
- bdd-testing — Cucumber multi-couches, contrat de couches, harness, cookbook
- app-security — posture actuelle (mono-store, confiance broker), auth wallet,
  brief matrice d'autorisations cible
- nextgraph-platform — NextGraph système externe + briefs (multi-store, shim, fork)

Audit corrections:
- décision SPARQL-delete annulée (superseded) → caveat (le code utilise ngSet.delete,
  persistance possiblement partielle)
- divergences relevées : routing path-based (pas hash), thème moderne sous components/sketchy,
  ConnectScreen hors registre, build:orm au chemin périmé, champs d'event perdus en connecté

Strip migrated sources; AGENTS.md/CLAUDE.md réduits au cœur (but, invariants,
carte des concepts) + pointeurs.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 14:58:44 +02:00