diff --git a/.tasks/doing/T01-align-the-published-surface.md b/.tasks/doing/T01-align-the-published-surface.md index e9ac223..5574ed0 100644 --- a/.tasks/doing/T01-align-the-published-surface.md +++ b/.tasks/doing/T01-align-the-published-surface.md @@ -41,6 +41,8 @@ The polyfill's published surface has drifted from the SDK's: invented names, inv - T01.f Remove readUnion, and the documents that cite it (deps: T01.e) - T01.g Adversarial review of the whole published surface (deps: all) +- T01.i Record the failure-reads-as-an-absence chain, and clear two leftovers (deps: T01.h) + ## Learnings digest - 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=@`. 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. diff --git a/.tasks/todo/T01.h-refresh-the-nextgraph-packages.md b/.tasks/doing/T01.h-refresh-the-nextgraph-packages.md similarity index 97% rename from .tasks/todo/T01.h-refresh-the-nextgraph-packages.md rename to .tasks/doing/T01.h-refresh-the-nextgraph-packages.md index 46eab8f..594b885 100644 --- a/.tasks/todo/T01.h-refresh-the-nextgraph-packages.md +++ b/.tasks/doing/T01.h-refresh-the-nextgraph-packages.md @@ -4,8 +4,8 @@ title: Refresh the NextGraph packages to their latest published versions parent: T01 depends_on: [T01.f] created: 2026-09-21 -claimed_by: null -claimed_at: null +claimed_by: T01.h-worker +claimed_at: 2026-09-21T14:17:00+02:00 blocked: false --- diff --git a/.tasks/todo/T01.g-adversarial-review-of-the-published-surface.md b/.tasks/todo/T01.g-adversarial-review-of-the-published-surface.md index b0c3b40..a12a894 100644 --- a/.tasks/todo/T01.g-adversarial-review-of-the-published-surface.md +++ b/.tasks/todo/T01.g-adversarial-review-of-the-published-surface.md @@ -2,7 +2,7 @@ id: T01.g title: Adversarial review of the whole published surface parent: T01 -depends_on: [T01.a, T01.b, T01.c, T01.d, T01.e, T01.f, T01.h] +depends_on: [T01.a, T01.b, T01.c, T01.d, T01.e, T01.f, T01.h, T01.i] created: 2026-09-16 claimed_by: null claimed_at: null diff --git a/.tasks/todo/T01.i-a-failure-that-reads-as-an-absence.md b/.tasks/todo/T01.i-a-failure-that-reads-as-an-absence.md new file mode 100644 index 0000000..28821f8 --- /dev/null +++ b/.tasks/todo/T01.i-a-failure-that-reads-as-an-absence.md @@ -0,0 +1,48 @@ +--- +id: T01.i +title: Record the failure-reads-as-an-absence chain, and clear two leftovers +parent: T01 +depends_on: [T01.h] +created: 2026-09-21 +claimed_by: null +claimed_at: null +blocked: false +--- + +## Description + +Three leftovers from T01.f. The first is the substantial one; the other two are mechanical. + +### 1. Open a `bug_` leaf for a read that answers empty where it should fail + +This repo has closed the "failure disguised as an absence" family repeatedly and it has two open `bug_` leaves on it already (`bug_a-lost-inbox-watch-is-never-retried`, `bug_an-empty-inbox-list-can-mean-a-failure`). This is another member, and it spans three layers. Write ONE leaf in `.project/concepts/app-contract/`, naming the chain rather than any single symptom. + +**The upstream fact that makes it a divergence and not a heritage - VERIFIED, do not re-derive it, and do NOT go read `nextgraph-rs` for it.** An anchored SPARQL query on a repo the verifier does not know REJECTS. The deciding line is `engine/verifier/src/request_processor.rs:269`: `let repo = self.repos.get(repo_id).ok_or(NgError::RepoNotFound)?;` inside `resolve_target_for_sparql`. It crosses into JS as a REJECTED PROMISE carrying the string `"RepoNotFound"` (`sdk/js/lib-wasm/src/lib.rs:403` for nodejs, `:606` for browser). Upstream's observable partition is "in `self.repos`" vs "not in `self.repos`": "does not exist" and "exists but my session does not know it" are INDISTINGUISHABLE, while "known but unsynced" collides with "genuinely empty". No read-capability check exists anywhere on that path - `resolve_target_for_sparql` reads only `nuri.target`. + +**The three layers of our chain**, each VERIFIED by reading the code in this working copy: + +- `fetchReadCap` (`packages/polyfill/src/emulated-verifier/public-store.ts:190`) collapses three unrelated situations into one `false`: the emulation is not in force (`:196`), the network could not be asked (`:223`), and the document exposes no cap (`:225`). Its own comment at `:220` states the collapse out loud. The function BELOW it, `downloadReadCap`, deliberately distinguishes resolving from rejecting and documents why - so the distinction exists and is destroyed one line later. Worse, NO caller reads the boolean at all: `ensureRepoOpen:208`, `docs.sparqlQuery:155`, `branch-registers.ts:316` and `fetchReadCaps:277` all call it as a bare statement. The information is not merely flattened, it is unrecoverable. +- The read then proceeds on a document that was never opened. +- `useShapeQuery` answers a SUCCESS holding zero objects. + +**What the target says the answer should be.** `useShapeQuery`'s return type is modelled on TanStack Query's `useQuery`, which distinguishes purely by whether the query function REJECTS: a rejection gives `isError` with the error, a resolution gives `isSuccess` with whatever data, empty included. That maps exactly onto upstream's `RepoNotFound`. So "the document is not reachable" must surface as `isError`, and `isSuccess` with zero objects must mean "it is there and holds nothing". + +**Also record, because it is the same family and currently unowned:** `useShapeQuery` never opens the documents named in `scope.graphs`, so an application that names an unopened document gets an empty success with no error. The `ensureReposOpen` barrier was removed from that path by T01.d. A fix for it was written during T01.f and REVERTED on instruction, because it was justified by a misdiagnosis (see the parent's digest); the defect itself is real and stands. + +**Severity is yours to argue in the leaf**, from the two existing bug leaves' bar. State what an application observes, not what the code does. + +**Explicitly OUT OF SCOPE - do not implement a fix.** Changing `fetchReadCap`'s return type to name the three cases, or making a read fail where it currently answers empty, is a DESIGN CHANGE the owner has not arbitrated. This task RECORDS the defect. It does not repair it. + +### 2. Remove `ensureReposOpen`, now dead + +T01.f removed its last caller. Verify that claim with a grep before deleting - if a caller survives, say so and leave it. `ensureRepoOpen` (singular) is NOT dead and must stay. + +### 3. The example's typecheck + +`examples/notebook/tsconfig.json` fails typecheck. T01.f reports this as pre-existing and verified as such. The owner has asked for the example to be repaired. Make it typecheck, without weakening the config - if the only way through is to loosen a compiler option, stop and report instead. + +**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; the example's own typecheck -> exit 0; `python3 ~/.claude/skills/concept/lint.py` -> 0 ERROR. Anti-cheat: no `any`, no `@ts-ignore`, no `as unknown as`, no deleted or skipped test, no weakened assertion, no loosened compiler option. + +## Progress + +## Result