chore: le Result porte le round 4

This commit is contained in:
Sylvain Duchesne
2026-09-24 20:16:02 +02:00
parent a4ec2546bf
commit 97699c232e
@@ -245,3 +245,32 @@ src exit=0 e2e exit=0 concept lint: 0 ERROR
`e2e/run.ts` INTERMITTENTLY fails to exit after printing its verdict - 3 runs in 7 here, one of them predating this task - and is then killed by its own 25-minute deadline, exiting 1 on a batch whose every check passed. Pre-existing and outside this task, but it makes the exit code an unreliable CI signal, and it is the kind of flake that gets read as a failure of whatever changed last. `e2e/run.ts` INTERMITTENTLY fails to exit after printing its verdict - 3 runs in 7 here, one of them predating this task - and is then killed by its own 25-minute deadline, exiting 1 on a batch whose every check passed. Pre-existing and outside this task, but it makes the exit code an unreliable CI signal, and it is the kind of flake that gets read as a failure of whatever changed last.
---
## Round 4 - the principle was wrong, and the suite was pinning the leak
Three findings, all confirmed by running them before anything was changed. Two of the three are mine rather than inherited, and both are the shape this batch has been chasing: **a rule replaced by a list, and a fix applied to one of two places.**
**F1 - the type-based policy was FALSE, and this is the one that needed a new answer rather than a new name.** "A primitive cannot reach an item, so it passes" - but a COUNT is a primitive that DESCRIBES the items. The file already knew it, because `size` and `length` were computed by an explicit two-name list, which is the forbidden name list reappearing as the fallback the moment the principle failed. Measured: a holder who may read 1 of 3 read `count` and `totalCount` as 3.
**And the suite asserted that as correct** - `expect(result.totalCount).toBe(7)` for a holder of one item, six lines above a test whose own comment calls an unfiltered count the defect. A suite that asserts both would have kept the leak green for ever. That test is rewritten and now asserts the refusal.
**The honest answer, stated plainly because the previous one pretended otherwise: it CANNOT be done by type.** A number is a number, and nothing observable about a getter says whether its 7 counts items or echoes a page size. So the default inverts: **what this file has DECIDED it serves; everything else refuses, primitive or not.** That is safe for an unseen shape BY CONSTRUCTION, which a passing default never was. The prohibition worth keeping was on a list that lets unknowns THROUGH - that breaks silently on a bump; one that refuses them breaks loudly, which is the property that matters. Two carve-outs, neither an SDK spelling: a member the target does not carry answers `undefined` (nothing is behind it), and the LANGUAGE's own primitive protocol keys, closed by the specification rather than by any package. `isLoading` is now DECIDED and served with its reason, rather than passing because it happened to be a boolean.
**F2 - the envelope had two traps to the collection's nine.** `delete result.data`, `result.isLoading = x`, `setPrototypeOf` and `preventExtensions` all reached the SDK's own object - and the last two are the pair round 3 found and trapped, on one view of two. The five write traps now live in ONE object spread into both, so one cannot be repaired without the other.
**F3 - the write gate was vacuous for an object naming no document.** `keep` answers `true` when `@graph` is absent, which is right for a READ (nothing to check) and meaningless for a WRITE: the object landed in the collection every identity shares. Only upstream's "@graph is missing" stopped it - the borrowed target guarantee this file removed for `set`/`deleteProperty` one round earlier and left standing here. A write must now NAME a document and the holder must hold it.
**Minor - `copyWithin`** is the one member upstream's own list mislabels as non-mutating, and its own `readOnlyArray` throws on reading it. Serving it over the snapshot made it succeed and discard in silence, so it is no longer served; the parse gate checks for exactly that one exception and nothing more.
**My own sweep of this round's repair** found nothing further: every count route on both views answers the READABLE count (1 of 3, never 3) - `size`, `Object.keys`, `Reflect.ownKeys`, `JSON.stringify`, spread, `toArray`, `Array.from`, the four combinators, `reduce` - and all four write routes refuse on both views with both targets intact.
### Round 4 gate, with EXIT CODES rather than summary lines
```
bun test, six consecutive runs, exit=0 each: 489 pass / 0 fail / 1465 expect() calls
(baseline 465 pass / 0 fail)
src tsc exit=0 e2e tsc exit=0 concept lint exit=0, 0 ERROR
orm-live-read exit=0: 5 passed, 0 failed, 5 declared
```