chore: T01.l est close, et les peers en etoile du polyfill deviennent une tache

This commit is contained in:
Sylvain Duchesne
2026-09-24 12:16:30 +02:00
parent ab5980c257
commit 2da26c5c4d
4 changed files with 49 additions and 3 deletions
@@ -45,7 +45,8 @@ The polyfill's published surface has drifted from the SDK's: invented names, inv
- T01.j A read that cannot reach its document must fail, not answer empty (deps: T01.i) - T01.j A read that cannot reach its document must fail, not answer empty (deps: T01.i)
- T01.l pnpm everywhere, and two dependency ranges that are looser than they look (deps: T01.j) - T01.l pnpm everywhere, and two dependency ranges that are looser than they look (deps: T01.j)
- T01.m There must be ONE answer to whether a document is open in this session (deps: T01.j) - T01.m There must be ONE answer to whether a document is open in this session (deps: T01.j)
- T01.k A provenance citation must say which version it describes (deps: T01.l) - T01.n The polyfill's peer ranges exclude exactly the versions it needs (deps: T01.m)
- T01.k A provenance citation must say which version it describes (deps: T01.n)
## Learnings digest ## Learnings digest
@@ -60,3 +61,4 @@ The polyfill's published surface has drifted from the SDK's: invented names, inv
- T01.h - NO version moved: all four `@ng-org/*` were already at their latest published version (web alpha.14, orm alpha.21, shex-orm alpha.9, alien-deepsignals alpha.13). The refresh this task was written for had already happened inside T01.d. What the install DID surface: the tree was BUN-installed, not pnpm-installed - every `@ng-org` symlink pointed into `node_modules/.bun/`, which still held web alpha.13, shex-orm alpha.8 and alien-deepsignals alpha.11 BESIDE the current ones. Resolved versions were right, so the single-instance hazard was latent rather than active; reconciled by `CI=true pnpm install --no-frozen-lockfile`, lockfile unmoved. Gates: `424 pass / 0 fail`, both typechecks exit 0, lint 0 ERROR, `orm-live-read` `5 passed / 0 failed`, e2e `43 passed / 0 failed`. THE HEADLINE, VERIFIED INDEPENDENTLY: the pinned `nextgraph-rs` checkout (`213338f6`) declares orm `0.1.2-alpha.18` while this repo compiles against `0.1.2-alpha.21` - THREE releases behind, so every `ref=` citation in the provenance mechanism describes a version no longer used. Second-order consequence to watch: a claim about alpha.21 REFUTED by reading the pinned tree is refuted against alpha.18, which is not the same statement. Three doctrine leaves corrected (`knowledge_what-the-orm-writes-and-deletes-upstream`, `knowledge_what-a-read-scope-is-upstream`, `knowledge_what-each-suite-judges`), each carrying facts that had become false. Also found: web/shex-orm/alien-deepsignals publish a bare `0.1.2` that semver ranks ABOVE the alphas but dates from 2025 - exact pins exclude it, but `ng-e2e-helpers`' peer range `>=0.1.2-alpha.13` would admit it. Follow-ups: `pnpm-workspace.yaml` lists only `packages/*` while root `workspaces` also lists `examples/*`, so `examples/notebook` is outside pnpm entirely; the dead `.bun` store is still on disk and a future `bun install` would re-point symlinks back into it; `playwright` 1.61.1 -> 1.63.0. - T01.h - NO version moved: all four `@ng-org/*` were already at their latest published version (web alpha.14, orm alpha.21, shex-orm alpha.9, alien-deepsignals alpha.13). The refresh this task was written for had already happened inside T01.d. What the install DID surface: the tree was BUN-installed, not pnpm-installed - every `@ng-org` symlink pointed into `node_modules/.bun/`, which still held web alpha.13, shex-orm alpha.8 and alien-deepsignals alpha.11 BESIDE the current ones. Resolved versions were right, so the single-instance hazard was latent rather than active; reconciled by `CI=true pnpm install --no-frozen-lockfile`, lockfile unmoved. Gates: `424 pass / 0 fail`, both typechecks exit 0, lint 0 ERROR, `orm-live-read` `5 passed / 0 failed`, e2e `43 passed / 0 failed`. THE HEADLINE, VERIFIED INDEPENDENTLY: the pinned `nextgraph-rs` checkout (`213338f6`) declares orm `0.1.2-alpha.18` while this repo compiles against `0.1.2-alpha.21` - THREE releases behind, so every `ref=` citation in the provenance mechanism describes a version no longer used. Second-order consequence to watch: a claim about alpha.21 REFUTED by reading the pinned tree is refuted against alpha.18, which is not the same statement. Three doctrine leaves corrected (`knowledge_what-the-orm-writes-and-deletes-upstream`, `knowledge_what-a-read-scope-is-upstream`, `knowledge_what-each-suite-judges`), each carrying facts that had become false. Also found: web/shex-orm/alien-deepsignals publish a bare `0.1.2` that semver ranks ABOVE the alphas but dates from 2025 - exact pins exclude it, but `ng-e2e-helpers`' peer range `>=0.1.2-alpha.13` would admit it. Follow-ups: `pnpm-workspace.yaml` lists only `packages/*` while root `workspaces` also lists `examples/*`, so `examples/notebook` is outside pnpm entirely; the dead `.bun` store is still on disk and a future `bun install` would re-point symlinks back into it; `playwright` 1.61.1 -> 1.63.0.
- T01.i - `ensureReposOpen` deleted (dead, grep-verified); its 3 tests went with it and that is legitimate, they asserted the BATCH's own properties (parallelism, deduplication, empty input) which no longer have a subject - the 4th, exercising `ensureRepoOpen` singular, was kept and relocated. `424 -> 421 pass`, the gap fully accounted. THE LEAD ON THE EXAMPLE WAS WRONG and the agent disproved it: the `pnpm-workspace.yaml` omission was real but NOT the cause of the `TS2345`; the actual cause was a `NoteShape` literal missing its `satisfies ShapeType<BaseType>` clause, so `valType` widened from `"iri"`/`"string"` to `string`. Proven by fixing only that, with the workspace untouched. The workspace gap was fixed anyway because item 3 needed it, and the duplication check came back clean - `examples/notebook` now resolves `@ng-org/*` to the same store paths as `packages/polyfill`. Bun store deleted (88M), and the RECURRENCE TRIGGER found: `bun.lock` was tracked in git while `.gitignore` only covered the older `bun.lockb`; now untracked, both ignored, and `packageManager: pnpm@10.26.0` declared. Left alone, reported: `docs/internal-contract.md`'s open-repo section was already stale (wrong line numbers, names a non-existent `ensurePhysicalRepoOpen`) and now also names the deleted `ensureReposOpen`; an orphaned `@ng-org/web@0.1.2-alpha.13` sits in the pnpm store with zero referencing symlinks. - T01.i - `ensureReposOpen` deleted (dead, grep-verified); its 3 tests went with it and that is legitimate, they asserted the BATCH's own properties (parallelism, deduplication, empty input) which no longer have a subject - the 4th, exercising `ensureRepoOpen` singular, was kept and relocated. `424 -> 421 pass`, the gap fully accounted. THE LEAD ON THE EXAMPLE WAS WRONG and the agent disproved it: the `pnpm-workspace.yaml` omission was real but NOT the cause of the `TS2345`; the actual cause was a `NoteShape` literal missing its `satisfies ShapeType<BaseType>` clause, so `valType` widened from `"iri"`/`"string"` to `string`. Proven by fixing only that, with the workspace untouched. The workspace gap was fixed anyway because item 3 needed it, and the duplication check came back clean - `examples/notebook` now resolves `@ng-org/*` to the same store paths as `packages/polyfill`. Bun store deleted (88M), and the RECURRENCE TRIGGER found: `bun.lock` was tracked in git while `.gitignore` only covered the older `bun.lockb`; now untracked, both ignored, and `packageManager: pnpm@10.26.0` declared. Left alone, reported: `docs/internal-contract.md`'s open-repo section was already stale (wrong line numbers, names a non-existent `ensurePhysicalRepoOpen`) and now also names the deleted `ensureReposOpen`; an orphaned `@ng-org/web@0.1.2-alpha.13` sits in the pnpm store with zero referencing symlinks.
- T01.j - SPLIT after six independent adversarial rounds. DELIVERED, none of it faulted across those six reviews: the four call sites propagate instead of collapsing (`docs.sparqlQuery`, `ensureRepoOpen`, `documentInboxAddress`, and `fetchReadCap`, which stops turning a rejected download into `false`), `fetchReadCaps` deleted (dead), the `knowledge_what-a-failed-read-answers-upstream` leaf, the empty-is-success guards, e2e named-scope coverage run live by TWO independent reviewers, and the REACHABILITY half of the barrier - a holder of nothing gets `isError` with `dataLen: 0`, a public document opens from a bare reference. NOT delivered, and now written into the contract as a non-guarantee pointing at T01.m: the ARRIVAL half holds only within one session, and fails across `configure()` and when a broker rejects after the open timeout. `1.0.0-dev.9`, `449 pass / 0 fail`, e2e `46 passed / 46`. THE ROOT CAUSE, which is T01.m's subject: "is this document open in this session" has THREE answers that can disagree - `held` and `syncState` in `open-repo.ts`, and `fanOuts` in `subscribe.ts`, the one nobody counted. Round 6 replaced one of two disagreeing records; there were three. TWO PROCESS LESSONS worth more than the code. First, a round counter is the wrong stopping rule: rounds 2-5 each fixed the reported corner and opened another, and what finally decided the split was a review producing the COMMON CAUSE rather than one more corner - stop when a review explains, not when a counter trips. Second, the implementer asserted three times that a change had simplified or shrunk something and all three were contradicted by measurement (534->563, 664->727, and a numstat of 34 insertions / 8 deletions for "a condition was removed"); two were relayed to the owner before being measured. The rule now held, and written into T01.m: a structural claim carries its `wc -l` or `git show --numstat` in the same sentence, or it is not written. Also found along the way, each fixed at its source: `subscribe.ts` already offered `onSetupFailed` for a refused subscribe and nothing had wired it; `resetOpenedRepos` abandoned in-flight opens without settling them, so a caller waited the full fallback and proceeded with sync state `"unknown"`, which `index-deposit.noRowsMeansAbsent` accepts as a definitive absence - a latent failure-as-absence bug found by instrumenting a one-in-four flaky test instead of re-running it. - T01.j - SPLIT after six independent adversarial rounds. DELIVERED, none of it faulted across those six reviews: the four call sites propagate instead of collapsing (`docs.sparqlQuery`, `ensureRepoOpen`, `documentInboxAddress`, and `fetchReadCap`, which stops turning a rejected download into `false`), `fetchReadCaps` deleted (dead), the `knowledge_what-a-failed-read-answers-upstream` leaf, the empty-is-success guards, e2e named-scope coverage run live by TWO independent reviewers, and the REACHABILITY half of the barrier - a holder of nothing gets `isError` with `dataLen: 0`, a public document opens from a bare reference. NOT delivered, and now written into the contract as a non-guarantee pointing at T01.m: the ARRIVAL half holds only within one session, and fails across `configure()` and when a broker rejects after the open timeout. `1.0.0-dev.9`, `449 pass / 0 fail`, e2e `46 passed / 46`. THE ROOT CAUSE, which is T01.m's subject: "is this document open in this session" has THREE answers that can disagree - `held` and `syncState` in `open-repo.ts`, and `fanOuts` in `subscribe.ts`, the one nobody counted. Round 6 replaced one of two disagreeing records; there were three. TWO PROCESS LESSONS worth more than the code. First, a round counter is the wrong stopping rule: rounds 2-5 each fixed the reported corner and opened another, and what finally decided the split was a review producing the COMMON CAUSE rather than one more corner - stop when a review explains, not when a counter trips. Second, the implementer asserted three times that a change had simplified or shrunk something and all three were contradicted by measurement (534->563, 664->727, and a numstat of 34 insertions / 8 deletions for "a condition was removed"); two were relayed to the owner before being measured. The rule now held, and written into T01.m: a structural claim carries its `wc -l` or `git show --numstat` in the same sentence, or it is not written. Also found along the way, each fixed at its source: `subscribe.ts` already offered `onSetupFailed` for a refused subscribe and nothing had wired it; `resetOpenedRepos` abandoned in-flight opens without settling them, so a caller waited the full fallback and proceeded with sync state `"unknown"`, which `index-deposit.noRowsMeansAbsent` accepts as a definitive absence - a latent failure-as-absence bug found by instrumenting a one-in-four flaky test instead of re-running it.
- T01.l - `ng-e2e-helpers`' peer range tightened to `>=0.1.2-alpha.13 <0.1.2`, MEASURED BY RESOLUTION: before, 4 versions admitted with `maxSatisfying` = the bare `0.1.2` of 2025-10-19; after, 3, and pnpm resolves `0.1.2-alpha.15`. The premise it was handed was half wrong and it said so: pnpm never actually RESOLVED to `0.1.2` because it prefers the `latest` dist-tag, which upstream happens to point at alpha.15 - so the trap was one dist-tag move from firing, and only resolution testing shows that either way. `playwright` 1.61.1 -> 1.63.0 (raises `engines.node` to `>=20`, drops `fsevents`), e2e `46 passed / 46` on the first run. Both `typecheck` scripts moved from `bunx tsc` to `pnpm exec tsc`, VERIFIED with `~/.bun/bin` off PATH and `command -v bun` empty. MEASURED about the optional peer: a consumer of the packed polyfill receives NO `@ng-org` package under pnpm, so the old `bun install` semantics survived the tool change - but THIS checkout does get the ORM, through `packages/polyfill/package.json:33`'s devDependency, so the runner's "install it in packages/polyfill" was a false lead and both texts now say the optional peer governs what a CONSUMER receives. `pnpm link` PERFORMED and it works: the polyfill resolves to the checkout and stays at ONE copy, so the single-instance hazard is untripped - but `@ng-org/web` and `@ng-org/orm` end up at TWO copies when the app also imports the SDK, pnpm warning by name, and `file:` gives one copy at the cost of repacking, which forfeits live editing. Left alone, reported: the polyfill's own four `"*"` peers admit ONLY the bare `0.1.2` of 16 published versions and EXCLUDE every alpha, because `*` matches no pre-release - inert while the polyfill is unpublished, certain to bite at first publish (now T01.n); `examples/notebook` has no `typecheck` script; `bun build` at `orm-live-read.ts:128` and `bun run` in `mint-wallet` are still bun-bound.
@@ -2,7 +2,7 @@
id: T01.g id: T01.g
title: Adversarial review of the whole published surface title: Adversarial review of the whole published surface
parent: T01 parent: T01
depends_on: [T01.a, T01.b, T01.c, T01.d, T01.e, T01.f, T01.h, T01.i, T01.j, T01.k, T01.l, T01.m] depends_on: [T01.a, T01.b, T01.c, T01.d, T01.e, T01.f, T01.h, T01.i, T01.j, T01.k, T01.l, T01.m, T01.n]
created: 2026-09-16 created: 2026-09-16
claimed_by: null claimed_by: null
claimed_at: null claimed_at: null
@@ -2,7 +2,7 @@
id: T01.k id: T01.k
title: A provenance citation must say which version it describes title: A provenance citation must say which version it describes
parent: T01 parent: T01
depends_on: [T01.l] depends_on: [T01.n]
created: 2026-09-21 created: 2026-09-21
claimed_by: null claimed_by: null
claimed_at: null claimed_at: null
@@ -0,0 +1,44 @@
---
id: T01.n
title: The polyfill's peer ranges exclude exactly the versions it needs
parent: T01
depends_on: [T01.m]
created: 2026-09-24
claimed_by: null
claimed_at: null
blocked: false
---
## Description
Two loose ends from T01.l, the first of which is a published-surface defect waiting for the first publish.
### 1. Four peer ranges that admit nothing usable
`packages/polyfill/package.json` declares all four `@ng-org/*` peers as `"*"`. In semver, `*` matches NO pre-release version. MEASURED by T01.l against the registry: of the 16 published versions, that range admits ONLY the bare `0.1.2` (dated 2025) and EXCLUDES every `0.1.2-alpha.N` - which is precisely what this package is built against and needs.
It is inert today only because the polyfill itself is not published (a registry query for it returns E404), so no consumer has ever resolved these peers. It is certain to surface at the first publish, as an unsatisfiable or wrongly-satisfied peer for every application installing this package.
Fix the four ranges so they admit the alphas this package actually works with and exclude the stale bare `0.1.2`. T01.l's tightening of `ng-e2e-helpers` to `>=0.1.2-alpha.13 <0.1.2` is the shape that was measured to work - decide whether the same bound is right here, given the polyfill is built against orm alpha.21 rather than web alpha.14.
**VERIFY BY RESOLUTION, NOT BY READING.** This is the whole lesson of T01.l: the range it was sent to fix had a real defect, but the stated failure mode was wrong, and only a resolution test showed which part was true. Query the registry, compute what the range admits before and after, and paste both counts. A range that looks right and resolves wrong is the entire failure mode here.
**Check `peerDependenciesMeta` at the same time.** All four are declared optional; confirm that is still the intent for each, since T01.l MEASURED that a consumer receives none of them under pnpm while this checkout gets the ORM through a devDependency.
### 2. The example has no `typecheck` script
`examples/notebook` typechecks correctly when invoked directly, but carries no `typecheck` script, so nothing in the repo runs it and its green state depends on someone remembering the command. Add the script. Do not build tooling around it.
### Verification
**Hard gate:** `bun test` -> 0 fail (baseline `449 pass / 0 fail`, measured); `pnpm exec tsc --noEmit -p packages/polyfill/tsconfig.json` -> exit 0; `pnpm exec tsc --noEmit -p packages/polyfill/e2e/tsconfig.json` -> exit 0; the example's typecheck via its new script -> exit 0; `python3 ~/.claude/skills/concept/lint.py` -> 0 ERROR.
A fresh `rm -rf node_modules && CI=true pnpm install --no-frozen-lockfile` must end with ONE copy of each `@ng-org` package - verify what each resolves to rather than trusting a silent install. Two copies of a package holding module-level state means two registries and two identities, which is this package's own named hazard.
Anti-cheat: no widening a range to silence a warning, and no `any`, `@ts-ignore`, deleted or skipped test.
Every quantitative claim carries the number that supports it in the same sentence.
## Progress
## Result