chore: T01.k est close, la version vit dans l-ancre et non sur 94 citations

Zero annotation touchee, trois citations reparees sur 94 - Scope,
BaseType et le compte de membres de NG - et la refutation de
where/orderBy/pageSize retiree des deux feuilles.
This commit is contained in:
Sylvain Duchesne
2026-09-24 15:07:14 +02:00
parent 139d21843a
commit a7f9f4e18a
2 changed files with 126 additions and 53 deletions
@@ -0,0 +1,126 @@
---
id: T01.k
title: A provenance citation must say which version it describes
parent: T01
depends_on: [T01.n]
created: 2026-09-21
claimed_by: worker-T01.k
claimed_at: 2026-09-24
blocked: false
---
## Description
The provenance mechanism cites a source tree that is THREE RELEASES BEHIND what this repo compiles against, and nothing in an annotation says so. A citation therefore proves "this was true at orm alpha.18", while a reader takes it for "this is true of what we build against".
### The established facts - VERIFIED, do not re-derive
Scanning every remote branch of `/home/sylvain/projects/nextgraph/nextgraph-rs`:
| source | `@ng-org/orm` version |
|---|---|
| `upstream/main` (the real upstream) | `0.1.2-alpha.18` |
| `origin/main` (a fork) | `0.1.2-alpha.15` |
| local checkout HEAD `213338f6` | `0.1.2-alpha.18` |
| what this repo compiles against | `0.1.2-alpha.21` |
So the local checkout is ALREADY at the newest source that exists anywhere reachable. alpha.19, 20 and 21 are published to npm from source that is in no branch of that repository. **There is nothing to re-anchor to, and updating the checkout is not the answer.** Do not spend time looking for a newer tree; that question is closed.
`grep -rho "ref=[^ ]*" packages/polyfill/src/ | grep -cE "engine/|sdk/"` counts 96 citations into that tree.
### The second-order trap, which has already fired
A claim about alpha.21 REFUTED by reading the pinned tree is refuted against alpha.18 - which is not the same statement. This happened during T01.h: the agent first wrote that alpha.21's `where`/`orderBy`/`pageSize` "became reachable", then retracted it because the pinned engine still has `start_orm` as `json!({})` behind a TODO. The retraction may be right, but the EVIDENCE does not support it at the strength claimed, because the pinned engine is not alpha.21. **Re-examine that specific correction** in `.project/concepts/app-contract/knowledge_what-the-orm-writes-and-deletes-upstream.md` and state what can honestly be said about it. A true citation attached to a claim it does not support is the exact failure `.project/rules/nextgraph/surface-mirrors-the-sdk.md` names.
### What to establish - the design is yours
The question is where the version belongs, and the obvious economy is worth weighing before anything else: **the version is a property of the CHECKOUT, not of each citation.** 96 annotations do not each need to carry it; one place that says "citations into `engine/` and `sdk/` are anchored at `213338f6` = orm alpha.18, while the build is alpha.21" may carry the whole fact. Weigh that against the alternative, do not assume it.
Then decide what the mechanism should ENFORCE, if anything. `packages/polyfill/test/provenance.test.ts` already checks that a `ref` resolves when the sibling checkout is present, and deliberately does NOT check that a claim is true. Adding a version check is a real option; so is deciding that this is doctrine, not a test. Read `.project/rules/nextgraph/no-divergence-from-nextgraph.md` for the taxonomy before proposing anything, and remember `announced-not-written` already exists for "the target is stated but the tree carries nothing" - part of this gap may be the same shape.
**Do NOT mass-rewrite 96 annotations** unless you have established that the fact cannot be carried in one place. A 96-site sweep is a large, risky, low-information change, and this repo's doctrine is explicitly that a good repair usually leaves a leaf SHORTER.
**Where a citation genuinely describes something that alpha.21 changed**, that is a real finding: record it. But establish it from the published package's own shipped types (`node_modules/@ng-org/orm`), not by guessing - and say plainly when the shipped artifact cannot settle the question, which it often will not since it carries types, not behaviour.
### Verification
**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 deleted or skipped test, no weakened assertion.
**An adversarial review is part of this task.** Angles: a citation that still claims more than its anchor supports; a version statement that is itself already stale; and whether whatever is added would survive the checkout moving. A finding counts only with a concrete scenario.
## Progress
## Result
Status: done
### 1. Where the version now lives, and why not on each citation
**In one place: the `ANCHOR` in `packages/polyfill/test/provenance.test.ts`, beside the resolver that opens the checkout.** Annotations touched: **0 of 94**. (The task file says 96; the same grep it prescribes returns **94** today, so the figure had drifted by 2 - VERIFIED, flagged here rather than silently reused.)
The economy holds, and it is stronger than "one fact stated 94 times". What a reader needs is not a version but the **gap**, and a gap has two ends. A per-citation version would record only the near end, would be wrong at all 94 sites at once the day the checkout moves, and would still leave the far end - the installed releases - unstated. The anchor records four rows, `(package, cited, built)`, and **three tests** check them:
| check | scope | behaviour when it cannot ask |
|---|---|---|
| the checkout declares the cited versions | 4 rows | passes empty-handed, like `deadCitations` |
| every workspace manifest pins the built versions | discovered from `packages/*`, `examples/*` | never absent |
| `packages/polyfill/node_modules` resolves the built versions | 4 rows | never absent |
Each was **mutation-tested**: falsifying `cited` -> `"@ng-org/orm - the checkout declares 0.1.2-alpha.18, the anchor says 0.1.2-alpha.19"`; falsifying `built` -> two manifest lines plus the installed-tree line; renaming a row to a package nothing pins -> `"the anchor names it, no workspace manifest pins it"`. With `NEXTGRAPH_RS=/nonexistent-checkout` the suite is `10 pass / 0 fail`. So it survives the checkout moving, by going red with the repair named.
A source-side pointer sits in `src/index.ts`'s provenance block (no numbers - numbers live in one place), and the doctrine sits in `.project/rules/nextgraph/no-divergence-from-nextgraph.md`.
### 2. What the mechanism enforces, and what it deliberately does not
It enforces that **the gap statement is still true at both ends** - the same class of check the file already made (a citation still *resolves*), never the class it refuses (a claim is *true*).
**It is NOT a new `kind`.** The taxonomy classifies what evidence ONE symbol has; how old ALL the evidence is belongs to the tree. `announced-not-written` is a different shape: there the tree carries nothing, here it carries an older something. A kind would have pushed a checkout-wide property onto 94 per-symbol labels.
It does not check the commit hash. `213338f6` is recorded as a `git show` pointer and explicitly labelled *not* the enforced identity: a checkout at another commit declaring the same releases describes the same code, and going red for a local commit is the red that teaches people to skip the suite.
### 3. `where` / `orderBy` / `pageSize` - what can honestly be said
**The retraction is refuted, and the original claim was closer to right than the retraction.** All VERIFIED, each from the artifact that can answer it:
- Level 3: `@ng-org/orm@0.1.2-alpha.21`, `dist/connector/RdfOrmSubscription.js` calls `ng.orm_start_graph(options.graphs, options.subjects, shapeType, session.session_id, options, this.onBackendMessage)` - the config **is sent**, at index 4.
- Level 2: `@ng-org/web@0.1.2-alpha.14`, `dist/index.d.ts` declares `orm_start_graph(graph_scope, subject_scope, shapeType, session_id, config, callback)` - **six** parameters, the config **is accepted**. The pinned tree's `sdk/js/lib-wasm/src/lib.rs` declares **five**, no config slot, which *proves by arity* that the pinned tree is older than the binding in this build. A claim about alpha.21 cannot be refuted there.
- Level 1, at `213338f6`: `OrmConfig` (`engine/net/src/orm.rs`) declares `where_`, `order_by`, `select`, `page_size`, `max_active_pages`; `from_json` parses all five and refuses a `pageSize > 0` without an `orderBy`; and every field is **consumed** - `schema_shape_to_sparql` emits `FILTER` (`|lt`/`|gt`), `ORDER BY`, `LIMIT`, `OFFSET`, and `initialize.rs` drives the paging window and page eviction. The `json!({})` is at the **entry point only**: `start_orm` takes no config argument, `request_processor.rs` calls it with three, and `from_json`'s own TODO names the missing hop - *"Use json in libwasm context"* - which is exactly the binding parameter the shipped binding now has.
So *"the richness is expressible at level 3 and discarded at level 1"* / *"no caller can reach any of it"* was **false at the pinned commit too**, and it is inference from an absent implementation, which this repo's own rule forbids. **Not settled by anything reachable:** whether the engine behind that binding feeds the JSON to `from_json`. The shipped wasm carries no source; only a live measurement answers it, and none has been made. (INFERRED corroboration, not proof: T01.h's live finding that the deployed `orm_start_graph` already took a config at index 4.)
Both leaves now say this. `knowledge_what-a-read-scope-is-upstream.md` owns the detail (+10 net lines, 78 lines total, still under the oversize threshold) and `knowledge_what-the-orm-writes-and-deletes-upstream.md` got **shorter** (-0 lines, the paragraph shortened and delegates).
### 4. Citations the installed releases actually invalidated: three of 94
Established from the shipped artifacts, never guessed. Unaffected and verified identical on both sides: the eleven level-2 citations (`doc_subscribe`, `sparql_update`, `sparql_query`, `doc_create`), plus `ShapeType`, `Schema`, `DeepSignalSet`.
1. **`Scope`** - the cited revision declares `graphs?` OPTIONAL; `@ng-org/orm@0.1.2-alpha.21` makes it **required**, and `index.ts` re-exports the installed one. No version note, unlike its neighbours `RdfOrmConfig` and `OrmSubscriptionHandle`, which already carried one. Repaired in that same established form.
2. **`BaseType`** - the cited revision keeps `"@graph"` commented out behind a TODO; `@ng-org/shex-orm@0.1.2-alpha.9` **declares** it. This one matters: the writes leaf documents that an insert must carry `@graph`, and a reader checking the citation would find the type does not declare it.
3. **`NG` - the member count.** The annotation and eight other places said **88 members**. The installed `@ng-org/web@0.1.2-alpha.14` declares **90** in its `NGModule` namespace (84 functions + 6 RDF classes), and two of the ninety - `graph_orm_next_page`, `graph_orm_previous_page` - appear **0 times** in the cited checkout's `lib-wasm/src/lib.rs`. Worst instance: `docs/api-contract.md` carried the 88 under a **VERIFIED** label, with the stale line range `index.d.ts:136-231` (the namespace is at 157 now), one paragraph after saying *"MEASURED against `@ng-org/web@0.1.2-alpha.14`"*. Repaired by naming the count ONCE with its release (in `surface/ng-proxy.ts`, where the type lives) and **deleting** it from the other eight, where the sentence said "all N members" and N added nothing but an occasion to rot. That leaves the claims shorter. INFERRED, not asserted anywhere: that 88 was alpha.13's count - alpha.13 is not on disk, so the text says only that the recorded figure was two short.
No `engine/` citation carries a quantitative claim of that kind (checked); `engine/` has no published version at all, and the anchor now says so rather than letting a reader think it can refute a release by opening it.
### 5. The gate, verbatim
- `bun test` -> ` 465 pass`, ` 0 fail`, ` 1316 expect() calls`, `Ran 465 tests across 40 files.` Baseline before any change was `462 pass / 0 fail` across 40 files - exactly the figure this task's caller measured, so no discrepancy to flag there. +3 is the three anchor checks.
- `pnpm exec tsc --noEmit -p packages/polyfill/tsconfig.json` -> `exit=0`
- `pnpm exec tsc --noEmit -p packages/polyfill/e2e/tsconfig.json` -> `exit=0`
- `python3 ~/.claude/skills/concept/lint.py` -> **0 ERROR**; warnings identical to baseline (3 oversized leaves, 2 open `_debt.md`, 1 unlisted `usage_`, 5 banned aliases), none added.
No `any`, no `@ts-ignore`, no test deleted, skipped or weakened. No citation was made to look right by changing what it claims.
### 6. Surprises
- **VERIFIED - the gap is four packages wide, not one.** The task frames it as orm alpha.18 vs alpha.21. In fact **all four** installed `@ng-org` packages are ahead of the checkout: orm 18->21, web 13->14, shex-orm 8->9, alien-deepsignals 11->13. The anchor records all four; an anchor naming only orm would have been a true statement covering a quarter of the surface.
- **VERIFIED - the pinned tree is provably older than the binding in the build**, by arity, not by version string. That single fact is what refutes the retraction, and it was available in two greps.
- **VERIFIED - `sdk/js/lib-wasm` has no `package.json`.** It is the Rust crate that builds the binding; twelve citations point into it and their published identity is `@ng-org/web`. The anchor says so explicitly.
- **VERIFIED - the published artifacts ship JS, not only types.** The task expected the shipped artifact would usually fail to settle behaviour. `@ng-org/orm`'s `dist/` carries the real call sites, which is what settled level 3 here.
- **VERIFIED, self-inflicted and caught by the gate** - a literal `/*` written inside a `//` annotation silently disabled the comment-stripping regex (`.replace(/\/\*[\s\S]*?\*\//g, "")`) in `moduleExports`, swallowing seven `export type { ... }` lines and failing `vocabulary.test.ts`. Fixed by rewording. The parsers in `provenance.test.ts` and `vocabulary.test.ts` are fragile this way: they strip block comments before line comments, so any `/*` inside a `//` comment eats the file to the next `*/`. Reported below.
### Follow-ups, none fixed
- `provenance.test.ts` / `vocabulary.test.ts` `moduleExports`: strips `/* ... */` before `// ...`, so a `/*` inside a line comment silently swallows source. A one-line reorder would close it.
- The `NG` member count is still hand-measured. It is now version-named in one place, but a test could count the installed `NGModule` and pin it, turning doctrine into a gate.
- `docs/api-contract.md` still cites line ranges into installed `.d.ts` files elsewhere; line numbers rot, and the repo's own rule says a ref cites a symbol.
- Whether the deployed engine honours `where`/`orderBy`/`pageSize` is open and only a live measurement closes it. It would be a real finding either way.
@@ -1,53 +0,0 @@
---
id: T01.k
title: A provenance citation must say which version it describes
parent: T01
depends_on: [T01.n]
created: 2026-09-21
claimed_by: null
claimed_at: null
blocked: false
---
## Description
The provenance mechanism cites a source tree that is THREE RELEASES BEHIND what this repo compiles against, and nothing in an annotation says so. A citation therefore proves "this was true at orm alpha.18", while a reader takes it for "this is true of what we build against".
### The established facts - VERIFIED, do not re-derive
Scanning every remote branch of `/home/sylvain/projects/nextgraph/nextgraph-rs`:
| source | `@ng-org/orm` version |
|---|---|
| `upstream/main` (the real upstream) | `0.1.2-alpha.18` |
| `origin/main` (a fork) | `0.1.2-alpha.15` |
| local checkout HEAD `213338f6` | `0.1.2-alpha.18` |
| what this repo compiles against | `0.1.2-alpha.21` |
So the local checkout is ALREADY at the newest source that exists anywhere reachable. alpha.19, 20 and 21 are published to npm from source that is in no branch of that repository. **There is nothing to re-anchor to, and updating the checkout is not the answer.** Do not spend time looking for a newer tree; that question is closed.
`grep -rho "ref=[^ ]*" packages/polyfill/src/ | grep -cE "engine/|sdk/"` counts 96 citations into that tree.
### The second-order trap, which has already fired
A claim about alpha.21 REFUTED by reading the pinned tree is refuted against alpha.18 - which is not the same statement. This happened during T01.h: the agent first wrote that alpha.21's `where`/`orderBy`/`pageSize` "became reachable", then retracted it because the pinned engine still has `start_orm` as `json!({})` behind a TODO. The retraction may be right, but the EVIDENCE does not support it at the strength claimed, because the pinned engine is not alpha.21. **Re-examine that specific correction** in `.project/concepts/app-contract/knowledge_what-the-orm-writes-and-deletes-upstream.md` and state what can honestly be said about it. A true citation attached to a claim it does not support is the exact failure `.project/rules/nextgraph/surface-mirrors-the-sdk.md` names.
### What to establish - the design is yours
The question is where the version belongs, and the obvious economy is worth weighing before anything else: **the version is a property of the CHECKOUT, not of each citation.** 96 annotations do not each need to carry it; one place that says "citations into `engine/` and `sdk/` are anchored at `213338f6` = orm alpha.18, while the build is alpha.21" may carry the whole fact. Weigh that against the alternative, do not assume it.
Then decide what the mechanism should ENFORCE, if anything. `packages/polyfill/test/provenance.test.ts` already checks that a `ref` resolves when the sibling checkout is present, and deliberately does NOT check that a claim is true. Adding a version check is a real option; so is deciding that this is doctrine, not a test. Read `.project/rules/nextgraph/no-divergence-from-nextgraph.md` for the taxonomy before proposing anything, and remember `announced-not-written` already exists for "the target is stated but the tree carries nothing" - part of this gap may be the same shape.
**Do NOT mass-rewrite 96 annotations** unless you have established that the fact cannot be carried in one place. A 96-site sweep is a large, risky, low-information change, and this repo's doctrine is explicitly that a good repair usually leaves a leaf SHORTER.
**Where a citation genuinely describes something that alpha.21 changed**, that is a real finding: record it. But establish it from the published package's own shipped types (`node_modules/@ng-org/orm`), not by guessing - and say plainly when the shipped artifact cannot settle the question, which it often will not since it carries types, not behaviour.
### Verification
**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 deleted or skipped test, no weakened assertion.
**An adversarial review is part of this task.** Angles: a citation that still claims more than its anchor supports; a version statement that is itself already stale; and whether whatever is added would survive the checkout moving. A finding counts only with a concrete scenario.
## Progress
## Result