Files
ng-eventually/.tasks/todo/T01.d-use-shape-query-reads-through-the-sdk.md
T
2026-09-16 12:20:57 +02:00

4.6 KiB

id, title, parent, depends_on, created, claimed_by, claimed_at, blocked
id title parent depends_on created claimed_by claimed_at blocked
T01.d useShapeQuery reads through the SDK's shape query T01
T01.c
2026-09-16 null null 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.

Progress

Result