50 lines
7.2 KiB
Markdown
50 lines
7.2 KiB
Markdown
---
|
|
id: T01.f
|
|
title: Remove readUnion, and the documents that cite it
|
|
parent: T01
|
|
depends_on: [T01.e]
|
|
created: 2026-09-16
|
|
claimed_by: null
|
|
claimed_at: null
|
|
blocked: false
|
|
---
|
|
|
|
## Description
|
|
|
|
Remove `readUnion` — and `UnionSubject` with it, if no published signature still uses it — from the published surface entirely, once nothing internal calls it. The remaining internal single-document reads become **anchored SPARQL queries**.
|
|
|
|
**Why it goes.** It has no counterpart upstream: it is N calls plus a batch policy. Its head annotation reads `kind=aligned level=2 ref=sdk/js/lib-wasm/src/lib.rs:sparql_query` — a citation that is exact, attached to a claim that is false, which is the worst failure mode of the annotation mechanism because a reader checks it, finds it correct, and moves on (`.project/rules/nextgraph/surface-mirrors-the-sdk.md`). A **composition cannot be aligned on a single function**.
|
|
|
|
**Call sites at planning time — verify, they may have moved.** In `src`: `packages/polyfill/src/emulated-verifier/index-deposit.ts:253` and `:304`, both single-document reads; `packages/polyfill/src/surface/watch-shape.ts:351`, which T01.d should have removed — confirm it is gone rather than assuming. Outside `src`: `examples/notebook/app.ts:108` and `:130`; `packages/polyfill/e2e/polyfill-entry.ts:402, 425, 427, 675`. Tests that exercise it: `test/read-model.test.ts`, `test/open-repo.test.ts`, `test/cross-user-access.test.ts`, `test/reload-own-document.test.ts`, `test/watch-shape.test.ts`, `test/inbox-dispatch.test.ts`, `test/listing-surfaces-failure.test.ts`, `test/deferred-inbox-processing.test.ts`. The anti-cheat applies in full: a test that proved behaviour **through** `readUnion` is re-pointed at whatever replaces it, never deleted and never weakened. `test/open-repo.test.ts` in particular uses it as the only integration path onto `ensureRepoOpen` — that coverage has to survive by another route.
|
|
|
|
**Anchored, not anchorless.** The mechanics are level-1 VERIFIED and recorded in `docs/api-contract.md` § 6: an anchor restricts the query to that repo's graph as default graph (`resolve_target_for_sparql`, `engine/verifier/src/request_processor.rs`), while an **anchorless** query unions every named graph in the session store. Replacing several anchored reads with one anchorless query would be a silent widening of what a read can reach — do not do it.
|
|
|
|
**Two documents cite it, and both must be revised.**
|
|
|
|
1. `.project/concepts/app-contract/polyfill-surface/contract_polyfill-surface.md` — `## Surface` (the signature and the `UnionSubject` interface) and `## Non-guarantees`, where the entry for the `useShape` broker bug currently tells applications *"Use `watchShape` for a reactive read and `readUnion` for a one-shot one — neither goes through that path"*. With `watchShape` renamed (T01.c), possibly re-pathed (T01.d), and `readUnion` gone, that published workaround **for an open MAJOR bug** no longer exists. Say what replaces it, or say plainly that nothing does. Do not leave the sentence standing.
|
|
2. The parent task states that `ng-helpers`' contract cites `readUnion` and must be revised. At planning time a grep of `.project/concepts/e2e-harness/ng-e2e-helpers/` found **no** such citation — neither `contract_ng-e2e-helpers.md` nor `usage_polyfill-e2e.md` names it — while the e2e **code** under `packages/polyfill/e2e/` uses it heavily. Find where the citation actually lives before revising anything; if it turns out to be only the code, record that in `## Result` so the parent's claim is corrected rather than silently dropped.
|
|
|
|
**This child owns the contract edit and the version bump for its own change** — a published symbol removed is a **major** by the contract's own `## Change policy`. No other child edits the contract for this change.
|
|
|
|
**Also in scope:** `packages/polyfill/src/surface/read-model.ts` (the module goes if nothing else lives in it), `packages/polyfill/src/index.ts`, `test/vocabulary.test.ts` and `test/app-surface.test.ts`, `docs/api-contract.md`, `packages/polyfill/README.md`, `packages/polyfill/docs/polyfill-reference.md`, and the `docs/` briefs only where they state something that is now false.
|
|
|
|
**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).** Do NOT read `ng-helpers` — not its code, not its tests. The isolation discipline says a counterpart is reached through its engagement and its usage declaration only. The planner's finding (that no contract leaf cites `readUnion`) was correct but obtained by a forbidden method. If this task needs to know who depends on `readUnion`, the answer belongs in a `usage_` declaration; `ng-helpers` has never authored one, and producing that is the fix, not a workaround by reading.
|
|
|
|
**Resolved since planning (2026-09-21) - `readUnion` HAS a declared consumer.** `ng-helpers` published its usage declaration (`polyfill-surface--ng-helpers`) and it says YES: `e2e/indexing-app.ts` calls `readUnion([doc])` twice, and `src/index.ts` REPUBLISHES it to ng-helpers' own consumers as the documented way to read an index. No ORM is wired there (`useShape: () => undefined`), so `useShapeQuery` is unreachable for them; their only fallback is an anchored `docs.sparqlQuery`. They declared this as a Need at full strength.
|
|
|
|
So the removal is not free. Before removing, decide with the owner what a consumer with no ORM reads with. Their declaration is the channel: read it at `ng-helpers`' repo through its published `usage_` leaf, never by reading their code.
|
|
|
|
**Also relevant, and worse than it looks.** That declaration reports `ng-helpers` still calling `createEntityDoc("public")` and `listMyEntityDocs("public")`, both invalid since `1.0.0-dev.5`, while its own test suite stays green because it runs against a fake that still models the superseded surface. A consumer can therefore be broken against us with nothing red on either side. Do not treat their green suite as evidence.
|
|
|
|
**Update (2026-09-21) - wiring the ORM does NOT unblock the removal, and the reason is ours.** `ng-helpers` wired the ORM as the contract prescribes and re-measured. Its verdict, recorded in its published usage declaration: neither shape read fetches a PUBLIC STORE's key, so a stranger reading a public index **from its reference alone** - the central case for an index - is served by no shape query at all. `readUnion` is the only thing that serves it today.
|
|
|
|
So the removal is blocked by a capability gap on OUR side, not by a consumer's reluctance. Before removing, either that gap closes or the removal does not happen. Read their declaration for the three secondary costs they list; do not read their code.
|
|
|
|
Their e2e also went from a measured `1 passed / 29 failed` (they were already broken against us, with both suites green) to `28 passed / 2 failed`, the two remaining being a provider defect: `useShapeQuery` never starts because `orm_start_graph is not a function`, swallowed, `isPending` for ever - measured on three consecutive runs, with the SDK's `ng` answering that member and `initNg` discarding its first argument.
|
|
|
|
## Progress
|
|
|
|
## Result
|