feat!: useShapeQuery lit par la requete de forme du SDK
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
---
|
||||
id: T01.d
|
||||
title: useShapeQuery reads through the SDK's shape query
|
||||
parent: T01
|
||||
depends_on: [T01.c]
|
||||
created: 2026-09-16
|
||||
claimed_by: T01.d-worker
|
||||
claimed_at: 2026-09-21
|
||||
blocked: false
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
`useShapeQuery` stops resolving a logical scope, enumerating documents, awaiting a sync barrier and calling `readUnion`, and reads through the **SDK's shape query** instead. The `ShapeQuery` snapshot and the capability filter survive; the home-grown pipeline goes.
|
||||
|
||||
**What "the pipeline" is, concretely.** `packages/polyfill/src/surface/watch-shape.ts` owns four steps today: resolve the logical scope to a document set (`listMyEntityDocs` in `shared-wallet/account-registry.ts`), open them through `ensureReposOpen` / `getSyncState` (`emulated-verifier/open-repo.ts`) which is the sync barrier `isPending` waits on, call `readUnion(docs)`, then filter the union by the shape's `@type` IRI. All four are the mechanism a translated parameter forced into existence, and all four are what this child removes.
|
||||
|
||||
**A blocker to resolve BEFORE implementing, not after.** `.project/concepts/app-contract/bug_useshape-live-read-needs-a-newer-broker.md` is an **open MAJOR bug**: the SDK's ORM subscription **never starts** against the broker deployed today — `orm_start_graph` is missing from two hand-maintained, independently-copied dispatch tables in any build made before commit `d5ecd0fd` — and the failure is an eternal pending set with one `console.error` as its only trace, indistinguishable from "this scope holds nothing". That leaf states explicitly that `watchShape` is **not affected**, because it is built on `doc_subscribe` (continuously present in both tables under a stable name), and the contract's `## Non-guarantees` tells applications to use `watchShape` for a reactive read for exactly that reason.
|
||||
|
||||
So this change takes the one reactive read that works and puts it on the path that does not, and the workaround the contract publishes stops existing. Establish what happens to that guarantee — a broker redeployment, a documented narrowing of the contract, a fallback — **before** writing the change. If it cannot be settled from the leaves, **stop and ask**. Do not implement and discover the consequence afterwards.
|
||||
|
||||
**`isError` has no counterpart at any level**, and that is recorded rather than assumed: `OrmSubscription`'s readiness promise is built with `resolve` alone, nothing rejects it, and `resolveReady()` runs on one path only — the arrival of initial data — so upstream's *"I could not find out"* **is** its *"still pending"* (annotation on `ShapeQuery` in `watch-shape.ts`; `docs/api-contract.md` § 6). Whatever replaces the pipeline must still be able to publish `isError`, or dropping it is a contract narrowing of its own that has to be declared.
|
||||
|
||||
**Facts — cite them, do not re-derive them.** `.project/concepts/app-contract/knowledge_what-a-read-scope-is-upstream.md`: the two axes and nothing else; the graph filter is literal; `did:ng:i` drops the graph filter entirely and runs over the whole local dataset with **nothing** added to narrow it by what the reader may open; the only bound is what the verifier has materialised locally; `subjects` filters the **root** shape only; `OrmSubscription.readyPromise` distinguishes not-ready from ready-and-empty at level 3 while the React `useShape` adapter drops that distinction. Also relevant: `makeReadFilteredView` (`packages/polyfill/src/emulated-verifier/read-filter.ts`) is the emulation of what the broker will bound **at delivery**, applied per document on what comes back — that is where the rights filter belongs, and `packages/polyfill/src/surface/use-shape.ts` already shows the shape of it.
|
||||
|
||||
**Leave `readUnion` alone here.** After this child `watch-shape.ts` no longer calls it, but `packages/polyfill/src/emulated-verifier/index-deposit.ts:253` and `:304` still do. Removing the symbol is T01.f's work.
|
||||
|
||||
**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` — `## Guarantees` and `## Non-guarantees` first, because that is where a narrowing shows up, then `## Surface`, then `## Change policy`. Apply the contract's own rules to decide the level rather than assuming one. No other child edits the contract for this change.
|
||||
|
||||
**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).** `useShapeQuery` does NOT delegate to the SDK's `useShape` — that return type cannot carry the request state, which is the whole point. It KEEPS `watchShape`'s implementation: the observable and the state tracking are ours. What changes is the PARAMETER (the SDK's `Scope`) and what is queried underneath (the SDK's shape query instead of `readUnion`). **`isError` stays.** The reasoning "it survives only if the SDK exposes an error channel" is wrong: the polyfill's mission is to ENRICH the SDK in its announced direction, not to be bounded by what it exposes today. Wire real errors if you can; otherwise the field stays and is filled later.
|
||||
|
||||
## Progress
|
||||
|
||||
- Blocker confirmed LIFTED before writing anything: the deleted bug leaf's premise was the pinned packages, and `RdfOrmSubscription.getOrCreate` is reachable at `@ng-org/orm@0.1.2-alpha.21`.
|
||||
- Read the doctrine first (`surface-mirrors-the-sdk`, `no-divergence-from-nextgraph`, `knowledge_what-a-read-scope-is-upstream`), then the SDK's installed typings to establish what the shape query offers: `readyPromise` (pending vs ready-and-empty), `signalObject`, `addChangeListener`, `close`.
|
||||
- Established, by reading `dist/connector/RdfOrmSubscription.js`, that `orm_start_graph` failing is caught and only `console.error`'d and that `readyPromise` is built with `resolve` alone. So the ONLY real error channel on this path is the query refusing to open; `isError` is wired to that and the rest is recorded rather than invented (no timeout was added).
|
||||
- Chose injection over a runtime import: the package never hard-imports the SDK, and `useShape` is React's hook, unusable from an observable. `configure({ ormSubscription })` mirrors the `ng` / `useShape` fields beside it, and `RdfOrmSubscription.getOrCreate` is assignable to it with no wrapper (probed with `tsc` before committing to the type).
|
||||
- Rewrote both suites against a new `test/orm-fake.ts` - a small engine honouring the two scope axes the way `schema_shape_to_sparql` does, so the scope tests still prove something. Nothing deleted: 7 -> 11 and 12 -> 15 tests.
|
||||
- The e2e harness now injects the REAL ORM, so the two `useShapeQuery` journeys measure the deployed broker. First run died in the broker login (a transient before any assertion); the second passed 42/42.
|
||||
|
||||
|
||||
## Result
|
||||
|
||||
**Done.** `useShapeQuery` reads through the SDK's shape query (`RdfOrmSubscription.getOrCreate`, injected as `configure({ ormSubscription })`; absent, the read publishes `isError` naming it). Scope resolution, the `ensureReposOpen` barrier, `readUnion` and the `@type` filter are gone; `graphs`/`subjects`/`did:ng:i` reach the engine verbatim. The observable, snapshot, state tracking and capability filter stay ours. `data` items are the shape's own objects and `shapeType` is `ShapeType<T>`. **`isError` stays**, wired to the one real failure - the query refusing to open. A start the ENGINE refuses is swallowed upstream, so it reads `isPending` for ever: recorded in code and contract, no timeout invented. `userStoreDoc` deleted; `liveStartWithConfig` retired. `1.0.0-dev.6`, major.
|
||||
|
||||
**Files.** `packages/polyfill/` src (7), test (4, incl. new `orm-fake.ts`), e2e (4), `package.json`, `README.md`; `docs/{api-contract,internal-contract}.md`; the contract leaf. Out of boundary, deliberately: `examples/notebook/` + `bun.lock` - the change silently broke the reference consumer.
|
||||
|
||||
**Gate.** `bun test` 408 pass / 0 fail; both `tsc` exit 0; lint 0 ERROR; `42 passed, 0 failed, 42 total - batch 3.3 min`.
|
||||
|
||||
**Surprises.** VERIFIED: `listMyEntityDocs` is NOT dead - its own suite and an e2e journey call it. VERIFIED: `bun.lock` was stale before this; `bun install` synced it.
|
||||
|
||||
**Follow-ups.** `readUnion` keeps two `index-deposit.ts` callers (T01.f). `knowledge_what-the-orm-writes-and-deletes-upstream.md` links the deleted bug leaf. `watchShape` survives as a stale name in ~10 src comments.
|
||||
Reference in New Issue
Block a user