4148df8fcbe49b3360926ea4553a8d7f5b514082
19 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
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. |
||
|
|
fa934ccdc6 |
Production serves from source, so it must fetch its config like everything else
The deployed application could never sign anybody in. `loadRuntimeConfig` skipped fetching `/festipod-config.json` under `NODE_ENV=production`, reasoning that a production build has the value inlined by `build.ts`'s `define`. This project's production does not build: the container copies the sources and runs `bun run start` (= `NODE_ENV=production bun src/index.ts`), serving from `src/` exactly as dev does. Nothing serves `dist/` at all. So the one step that could supply a wallet was skipped, `ensureIdentity()` threw for want of one, and the endpoint sat there — served, and never asked. The condition tested the wrong thing. "Was the value inlined?" is a question the global itself answers; "am I in production?" only ever stood in for it, and the stand-in was false on the very path that matters. Verified on the served bundle rather than on the endpoint: under `NODE_ENV=production` it now carries the fetch, where it carried none. The same combination is what `hooks.ts` spawns for the @e2e app server, so that layer could not sign in either. |
||
|
|
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. |
||
|
|
13eb2c4a15 |
Waits that say ten seconds now wait ten seconds, and editing checks you may
Two things that announced what they had not verified. Seventeen `waitForFunction` calls passed their timeout in Playwright's ARGUMENT slot instead of its options slot, so every one of them silently used the 30 s default while the code read 5, 10, 15 or 60. The inventory said sixteen: one was a false positive and two more were found that it never listed. All seventeen are corrected, including the nine whose written value is SHORTER than the default. Honouring the author's number is the point: a wait that is too short fails loudly and names its step, where thirty seconds obtained by accident hides a real slowness and reads as a lie in the source. Which of them need raising is a question for the day the suite can run again -- it will be answered on an honest number. The event edit screen awaited nothing: the success toast fired and the screen navigated away whether or not the write resolved. It now confirms after the write, keeps the user on their edits when it fails, and says so. That route was also unguarded -- anyone reaching the URL got the form, for any event. It is now decided by ownership, read from the list of my own documents, with the same three-state answer the pencil icon uses. UNKNOWN renders neither the form nor a bounce: both would present a guess as a fact, and the guess that matters here is telling a genuine owner their event is not theirs. |
||
|
|
db3dbba294 |
No fixture seed, no event host, and the edit affordance stops lying
Three changes the product model asked for.
The fixture seed no longer writes anything into a connected wallet, by any
route. `bootstrapWallet` is the single enforcement point -- both call sites
funnel through it -- so the switch cannot be walked around by a screen or a
bridge. The fixtures, the seeding code, the demo path and the rendering tests
are untouched; a unit test now fails if a document is created after all.
An event has no host. The domain says so -- the meeting point has a host, the
event is only the anchor -- while the shape carried `hostName`/`hostInitials`
and every created event was written with the fabricated `'Moi'` / `'MD'`. Gone
from the shape, the ORM bindings, the type, the adapters, the writes and the
screens. `fp:MeetingPoint.host` stays: that one is real.
Regenerating the ORM revealed the committed bindings had drifted from what the
generator emits -- stylistic, verified predicate by predicate, plus the loss of
the `Fp` prefix. The prefix cannot be restored at the generator: the name comes
from the shape IRI, and those IRIs are the persisted RDF classes. Aliased at the
three import sites instead, so nothing downstream moved and the DOM `Event` and
`Notification` types are never shadowed.
Write rights are ownership, read from the list
The contract leaves no other reading -- only an owner writes, and no call adds a
writer -- so `listMyEntityDocs('public')` is what says which events are mine.
The hard-coded `isOwner = true` is replaced by a three-state answer, and the
UNKNOWN state renders neither a pencil nor a greyed one: a disabled look-alike
invites a dead click.
Two adversarial passes refuted the first attempt and both defects are fixed. A
latched boolean denied an owner their own event forever once a listing had
missed it; the ruling is now rebuilt rather than accumulated, so a later listing
overturns an earlier one.
Residual, deliberate and commented: "not mine" is inferred from absence, and the
reactive read and the listing are separate mechanisms, so a freshly arrived
event is ruled out for the window between them. Closing it needs a timer, which
the doctrine forbids.
|
||
|
|
df971df135 |
Who I am comes from signing in; my profile is the document I own
The app derived its identity from a profile lookup and, when nothing matched,
picked somebody else. That is backwards: signing in returns who I am, and the
profile is looked up by it.
- Identity and profile are now two things. The identity is what
`ensureIdentity()` returns: opaque, never rendered, never written, never
passed to a data-layer call. The profile is Festipod's own object -- pseudo,
name, initials -- in a document we create and write.
- "My profile" is the profile document I own, resolved through
`listMyEntityDocs('protected')`. No username matching, no positional pick. A
failed listing leaves the answer UNKNOWN rather than collapsing to "none".
- Having no profile now resolves to having no profile. Two impersonation
fallbacks are gone, including one in `updateProfile` that would have written
your pseudo into a stranger's document.
- A profile is created at sign-in when none exists. The shape makes name,
initials and username mandatory, so it is written with placeholders that read
as instructions -- never a plausible human name, never anything derived from
the opaque identity.
Nothing succeeds in silence any more
`joinEvent` used to return without writing and without throwing when it could
not attribute the participation, while the screen announced success. It rejects
now, and the confirmation follows the write. Withdrawal likewise -- the doctrine
requires it to be authoritative. The host notification stops being written into
the joiner's own store, where its recipient could never read it, and the
optimistic notice shown to the wrong person goes with it.
The creator signs up through the common path: no owner branch anywhere, no
special case, the same deposit and the same derived count.
|
||
|
|
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. |
||
|
|
c1817607b4 |
Migrate Festipod onto the rebuilt @ng-eventually/client surface
The SDK was rebuilt: reading is possession instead of an ACL, `Nuri` and `ReadCap` are template literal types, the cross-account fan-out is gone, and so is the global discovery index. Repair the typecheck gate FIRST — it was checking nothing. Under TypeScript 6 the deprecated `baseUrl` is reported as an ERROR that aborts compilation, so `tsc --noEmit` exited 0 having verified nothing, behind a single line that reads like a harmless warning. Dropping `baseUrl` (paths resolve relative to the file since 4.4) makes the gate real again — and it immediately surfaced 33 errors, three of which had been dormant for a long time. Types: 18 sites fixed AT THE SOURCE — the functions that produce a NURI now return `Nuri` — with `isNuri` guards only at genuine boundaries (an `@id` read back from a document, an argument coming from a Cucumber step). No cast, no `@ts-ignore`: silencing the compiler here would have removed the very guarantee the new types provide. Capabilities: the ACL is gone. `grantRead`/`protectedDocsOf`/`canRead`/ `makePublic` give way to `capFor`/`shareCap`/`publishRepoLink`, and `open` loses its `owner` argument. `declareConnections` now shares the caps of its OWN protected documents to each neighbour's wallet inbox. Discovery is REMOVED, not postponed: there is no discovery in the target model, a reader reaches a document only by following a link it was given. The module and its call sites are gone; the scenario is suspended with a comment saying what will bring it back — a Festipod DIRECTORY document, whose link the app knows. Kept rather than deleted: the product need has not gone away. Verification, and a correction to how it was measured. The @data baseline (20/22) had been taken on a bloated test wallet: 93 MB against a threshold documented around 99 MB, with the run stretching from 18 to 23 minutes. Restarting from a fresh profile drops it to 9m37 and turns BOTH baseline failures green — including the cold-reconnection one, which confirms the SDK's claim that a fresh session reads its own documents back with nothing re-declared. So the reference itself was degraded, on both sides of the comparison. Real state: typecheck 0, @ui 7/7, @data 20/21. The single failure is understood and left standing: the protected-connections probe reads the protected STORE document as a stand-in for an entity. Sharing a store cap would hand over its entire contents, present and future — precisely the gesture the model refuses. The scenario's own title says "the protected ENTITY"; the probe is what took the shortcut, and it is what has to change. |
||
|
|
7459d49e83 |
docs+fix: recadrer le polyfill comme compensateur d'écart, corriger la doctrine périmée
RECADRAGE — la doctrine était trop étroite. rule_app-uses-sdk-surface-only disait « le polyfill existe pour le WALLET VIRTUEL » : juste sur le fond, mais à la lettre l'émulation des caps qu'on vient de livrer n'entrait pas dans son mandat. Nouvelle formulation, portée aussi dans AGENTS.md : @ng-eventually/client est un POLYFILL, et ce mot dit toute sa mission : compenser l'écart entre le SDK tel qu'il devrait être et ce que NextGraph fournit aujourd'hui. Le wallet virtuel en est la plus grosse pièce, pas la totalité. Avec la conséquence opérationnelle : quand quelque chose ne marche pas, la question n'est jamais « comment contourner dans l'app » mais « qu'est-ce que le polyfill doit compenser ». Un contournement côté app est une violation même quand il fonctionne — il grave un état temporaire de NextGraph dans du code qui doit lui survivre. Et l'ignorance de l'état d'implémentation est durcie : ENTIÈREMENT, pas « sauf quand ça mord ». NOUVEAU — data-layer/knowledge_sdk-surface : le contrat SDK cible, écrit dans CE repo pour qu'un agent n'ait jamais à ouvrir le repo du polyfill. Couvre lectures réactives, écritures, placement par scope, inbox, discovery, capabilities (capFor/shareCap/publishRepoLink, livrées avec P1a), identité, sûreté SPARQL — et les surfaces exportées mais interdites à l'app. DOCTRINE PÉRIMÉE corrigée, après vérification dans le code : - rule_document-per-entity décrivait la lecture via readEntities/readUnion/ registerDoc/bumpRead : ZÉRO site d'appel, readEntities.ts supprimé. Réécrite sur watchShape/useShapeQuery. Le fond (un document par entité) est intact. - brief_2026-07-06 §P3 réaffirmait une phrase que son propre encadré déclare fausse : rétractée explicitement. - knowledge_data-modes citait useShapeWithDefaults(), qui n'existe nulle part. - ConnectScreen : les fiches avaient raison mais étaient vagues — l'écran existe, est routé et monté, et est bien absent du registre. Précisé. FIX CODE — build:orm était CASSÉ : il pointait ./src/shapes/, qui n'existe pas (les shapes vivent sous src/shared/shapes/), et sortait en erreur. Donc la commande que la doctrine prescrit après tout changement de .shex ne marchait pas. Corrigé et vérifié : exit 0. La fiche avait raison, c'est le code qui était faux — le point 4 approuvé, simplement situé dans l'autre fichier. Régénération NON embarquée : lancer build:orm reformate les bindings et retire l'annotation `: Schema`. C'est une montée de version d'outil, pas une correction de contenu — elle mérite son propre changement validé, pas un passage clandestin. Noté dans la fiche. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014GbGgNEHRejVKoREvFuDFg |
||
|
|
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 |
||
|
|
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
|
||
|
|
c5e627c5fc |
fix(participants): joindre participation→profil à travers les deux espaces d'id
Une Participation stocke son user comme principal stable `urn:festipod:user:<clé>`, alors qu'un UserProfile a pour `id` son NURI `did🆖`. La jointure brute `partUserIds.includes(u.id)` ne matchait donc jamais en mode connecté → chaque participant s'affichait « inconnu ». - `resolveParticipantUser` : match direct (espace seed demo) puis, à défaut, match sur `normalizeIdentifier(username)` après retrait du préfixe principal. - `USER_PRINCIPAL_PREFIX` : source unique du préfixe, partagée par l'écriture (`currentUserId`) et la lecture, pour qu'elles ne divergent pas. - EventDetailScreen : filtrer soi-même sur `currentUser?.id` (id de profil, même espace que `p.id`) et non sur `currentUserId` (principal). Aussi : épingle `packageManager` pnpm (l'install passe par pnpm, cf. rule_bun-first) — le runtime/test/build restent Bun. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014GbGgNEHRejVKoREvFuDFg |
||
|
|
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 |
||
|
|
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> |
||
|
|
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> |
||
|
|
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> |
||
|
|
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> |
||
|
|
619b94ac0e |
refactor(data): route entities by scope via the SDK — no store ids in the app
Festipod now treats @ng-eventually/client as a finished NextGraph SDK: the app decides only each entity's logical scope (events/PdR public, profiles/ participations protected, settings private) and calls the lib by scope. The old mono-store default and the FESTIPOD_MULTISTORE path collapse into ONE scope path. Removed every physical-store leak from the app data-plane (ngGraph, registration, FestipodDataContext, NextGraphContext, useShapeWithDefaults): no more did🆖${store_id} construction. The session is handed to the lib only at the sanctioned injection point (ngSession/configureStoreRegistry). Product behavior unchanged. @data 20/20; build + tsc clean. (_debt.md included; the T03.e doctrine pass settles accumulated doc-debt.) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
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>
|