37 lines
5.3 KiB
Markdown
37 lines
5.3 KiB
Markdown
---
|
|
id: T01.d
|
|
title: useShapeQuery reads through the SDK's shape query
|
|
parent: T01
|
|
depends_on: [T01.c]
|
|
created: 2026-09-16
|
|
claimed_by: null
|
|
claimed_at: null
|
|
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
|
|
|
|
## Result
|