feat!: useShapeQuery prend le Scope du SDK, et un store se nomme par sa reference

This commit is contained in:
Sylvain Duchesne
2026-09-16 16:47:09 +02:00
parent ee9319f89b
commit d0ca5c5d41
31 changed files with 812 additions and 291 deletions
@@ -45,3 +45,4 @@ The polyfill's published surface has drifted from the SDK's: invented names, inv
- T01.a — the taxonomy gains `announced-not-written`: its `ref` points at the `.md` in THIS repo where the statement is on record, never a source file, plus `announced-by=<who>@<date>`. Upstream citations now resolve-check only when the sibling checkout is present; announced records always. Two latent defects fell out: the test's closed set was missing `reserved-name`, and the rule said "five kinds" over six rows. All 92 existing refs already resolved.
- T01.b — deletion is answered at level 3 and fully wired: `DeepSignalSet.delete` -> a remove patch -> `graph_orm_update` -> `SparqlBuilder::remove_object`, which removes the SHAPE's predicates, not the subject; no document deletion exists at any level. `getObjects` returns a DETACHED clone; `insertObject` throws without `@graph`. Proposals: publish `OrmSubscription` as-is (the read filter applies to `signalObject`), publish NEITHER `DiscreteOrmSubscription` (JSON-CRDT, no `@graph`, nothing for a per-document filter to read) NOR `ngSession` (hands out the SDK handle and the session). MEASURED: `useShape` is still broken live, and the bug leaf's mechanism was FALSE - the deployment is AHEAD of our pinned `@ng-org/*`, its `orm_start_graph` takes `config` BEFORE the callback, so the five-argument client lands its callback in the wrong slot. T01.d's blocker STANDS. Also VERIFIED: ORM writes bypass `sparql_update` entirely, so the emulated write guard never sees an ORM add or delete.
- T01.c - `useShapeQuery` takes the SDK's `Scope`, re-exported as a passthrough. Our three literals became `StoreType`, internal only: VERIFIED they are upstream's own `store_type`, so only the NAME was invented. A store is now named by its REFERENCE (`privateStore()` / `protectedStore()` / `publicStore()`), and `createEntityDoc(store?)` takes one, private when omitted, as `doc_create` does. `listMyEntityDocs`, `resolveScopeGraph` and `resolveWriteGraph` unpublished, still internal - T01.d removes the read path's last caller. VERIFIED: public and protected share one store id here, so the reference must be the index document. `1.0.0-dev.5`.
@@ -1,38 +0,0 @@
---
id: T01.c
title: watchShape becomes useShapeQuery and takes the SDK's Scope
parent: T01
depends_on: [T01.a]
created: 2026-09-16
claimed_by: null
claimed_at: null
blocked: false
---
## Description
`watchShape` becomes **`useShapeQuery`** and takes the SDK's `Scope` — `{ graphs?: string[] | string; subjects?: string[] }` (`sdk/js/orm/src/types.ts`) — in place of the three invented literals `"public" | "protected" | "private"` (`packages/polyfill/src/model/types.ts`).
**The name and the parameter only.** The read path underneath stays as it is in this child; T01.d replaces it. The translation from the SDK `Scope` down to what the current pipeline needs lives here and is **explicitly temporary** — say so beside the code, because *a parameter that must be translated is the tell that a mechanism below it is about to become ours* (`.project/rules/nextgraph/surface-mirrors-the-sdk.md`).
**What stays, and why that is not a fault.** The `ShapeQuery` return shape (TanStack-like: `{ data, isPending, isSuccess, isError, error }`) and the capability filter both survive. That is the **justified** kind of difference: NextGraph's direction, as reported by its developers, is a `useShapeQuery` returning exactly that shape, and deviating *toward* the announced target is alignment. `useShape` (the existing passthrough to the SDK's current hook, `packages/polyfill/src/surface/use-shape.ts`) is a different symbol and keeps its own life — do not collapse the two.
**Facts — cite them, do not re-derive them.** `.project/concepts/app-contract/knowledge_what-a-read-scope-is-upstream.md`: a `graphs` entry is **one document's graph**, never a set of them; `did:ng:i` or `""` discards every other entry and means the whole local dataset **with no permission narrowing**; a store's graph holds only its containment triples, so *"search my private store"* returns empty **silently**; `[]` or `undefined` returns no objects at all; the graph filter is literal, with no prefix or hierarchy matching; **scoping a read by store is not expressible at all**.
**The boundary that must not be lost.** The store axis survives for **creation** — `docs.docCreate` takes a destination upstream, and `storeRegistry.createEntityDoc(scope)` keeps its three literals — and dies for **reading**.
**Open question — settle it in the open, or escalate.** After this change two different types would want the name `Scope`. The published `Scope` today is the three literals and is used by `storeRegistry.createEntityDoc / listMyEntityDocs / resolveScopeGraph / resolveWriteGraph`. The SDK's `Scope` is the object. The rule says publish the SDK's names, so the SDK's type should take the name and the **placement axis needs a name of its own** — but what that name is, and whether `listMyEntityDocs` (a *read* keyed by the store axis) survives at all, is settled by no leaf. Propose it in `## Progress`; if `listMyEntityDocs`'s fate is genuinely in question, **stop and ask** rather than removing a published symbol on your own judgment.
**This child owns the contract edit and the version bump for its own change.** Edit `.project/concepts/app-contract/polyfill-surface/contract_polyfill-surface.md`: `## Surface` (the signature, the `Scope` type block), `## Non-guarantees` (every mention of `watchShape`), `## Change policy` (the new pre-release string and what it narrows). By the contract's own `## Change policy` this is a **major** — a published symbol is removed, and an existing call changes a parameter a caller must react to. No other child edits the contract for this change; do not touch the sections another child owns.
**Also in scope:** `packages/polyfill/src/surface/watch-shape.ts`, `packages/polyfill/src/index.ts` (export and the comments around it), `packages/polyfill/src/model/types.ts`, `test/vocabulary.test.ts` and `test/app-surface.test.ts` (they pin the export inventory and the vocabulary), `test/watch-shape.test.ts`, `test/watch-shape-surfaces-failure.test.ts`, `docs/api-contract.md`, `packages/polyfill/README.md`, `packages/polyfill/docs/polyfill-reference.md`, `examples/notebook/app.ts`, and the `packages/polyfill/e2e/` callers.
**The annotation.** `useShapeQuery` has zero occurrences in `nextgraph-rs`; its justification is NextGraph's own direction as stated by its developers. Read T01.a's `## Result` and use the form it installed. Do **not** cite an approximate symbol — that is the `readUnion` failure this whole task exists to undo.
**Hard gate:** `bun test` → 0 fail; `bunx tsc --noEmit -p packages/polyfill/tsconfig.json` → exit 0; `bunx tsc --noEmit -p packages/polyfill/e2e/tsconfig.json` → exit 0; `python3 ~/.claude/skills/concept/lint.py` → 0 ERROR. Anti-cheat: no `any`, no `@ts-ignore`, no deleting or skipping a test, no widening an assertion.
**Resolved since planning (2026-09-16).** Our `Scope` type is NOT renamed — it DISAPPEARS. A renamed invented name is still an invented name. This extends to creation: the earlier note that "the store axis survives for creation" was preserving an invention because it looked necessary. Upstream `docCreate(sessionId, crdt, cls, dest, store?)` does take a store, so the NOTION survives; what is invented is our three-literal encoding. The creation path aligns on `docCreate`'s own parameters. **`listMyEntityDocs` is DELETED**: no upstream equivalent exists (no `doc_list`, no `list_docs`, nothing in the binding), and listing one's documents is an ordinary query over the store graph's `ldp:contains` — sugar over an expressible query, the same shape as `readUnion`.
## Progress
## Result