provident-storeroom 1.2.0 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.guide.md +212 -5
- package/README.md +36 -11
- package/RELEASE-NOTES.md +610 -0
- package/index.d.ts +77 -6
- package/index.mjs +120 -34
- package/package.json +7 -6
- package/src/entry/constraints.mjs +662 -73
- package/src/entry/delivery.mjs +19 -5
- package/src/entry/kernel-attempt.mjs +181 -23
- package/src/entry/kernel-commit.mjs +191 -2
- package/src/entry/kernel-foundations.mjs +266 -24
- package/src/entry/kernel-registration.mjs +81 -27
- package/src/entry/kernel-terminal.mjs +18 -5
- package/src/entry/state.mjs +47 -4
package/README.guide.md
CHANGED
|
@@ -21,6 +21,20 @@ WAS DRIVEN AGAINST A PUBLISHED TARBALL:** every behavioural claim below is eithe
|
|
|
21
21
|
measurement taken on this repository's checkout and named as one. **No `npm install` was run by the pass that
|
|
22
22
|
wrote this file against the registry, so a registry-installed `0.5.0` is described here and not witnessed.**
|
|
23
23
|
|
|
24
|
+
**⟶ AND THE TWO FIGURES THIS GUIDE CANNOT AFFORD TO HAND-WRITE ARE GENERATED AND ASSERTED FROM 2026-10-28 — `DEF-AC` (b), AND THE ASSERTION IS THE DELIVERABLE.** `RCA-8(g)` ANNOTATE-BESIDE: **the paragraph above stands
|
|
25
|
+
BYTE-FOR-BYTE and is read THROUGH this clause.** **THE DEFECT: the version line and the cut-state sentence below
|
|
26
|
+
were written BY HAND, which is how this guide came to deny the release it travels in** — a figure copied by hand
|
|
27
|
+
goes stale the moment the manifest or the tag set moves, and for eight dated paragraphs nothing reddened. **SO
|
|
28
|
+
`scripts/publish.sh` GENERATES BOTH from `bindings/js/package.json`'s `version` and `git tag --list`, and its
|
|
29
|
+
preflight REFUSES when these two lines do not read what it computed** (`scripts/publish.sh --figures` runs that
|
|
30
|
+
one check, read-only). **THE TWO LINES ARE THIS GUIDE'S OPERATIVE FIGURES; every dated paragraph below is the
|
|
31
|
+
reading of its own date and is read through them.**
|
|
32
|
+
|
|
33
|
+
PUBLISH-GUIDE-VERSION-LINE: THE VERSION THIS GUIDE DESCRIBES IS `1.4.0`.
|
|
34
|
+
PUBLISH-GUIDE-CUT-STATE: NOT STATED -- this document describes the behaviour at the version line above, and the cut-state is the release's to state, not a shipped document's (`D-57`).
|
|
35
|
+
|
|
36
|
+
**⟶ AND THE CUT-STATE SENTENCE ABOVE IS WITHDRAWN, 2026-10-28 — THE CUT-STATE IS NOT A DOCUMENT'S TO STATE.** `RCA-8(g)` ANNOTATE-BESIDE, and the architect's ruling verbatim (`strata/decisions.md` · `D-57`): *"Drop the cut-state."* **The two marked lines above stand BYTE-FOR-BYTE as the reading of their own date and are read THROUGH this clause — not one byte of either is deleted — and the sentence this clause withdraws is the SECOND of them, the one that claims to say whether the release this guide travels in is cut.** **THE REASON IS MEASURED, THREE TIMES OVER, AND IT IS STRUCTURAL RATHER THAN A MISTAKE: A SHIPPED DOCUMENT IS SEALED BEFORE THE TAG THAT MAKES ITS CUT-STATE TRUE**, so every release packed before its tag ships a document denying itself, and a guard running in that release's own preflight can only ever confirm *"not cut"* — `1.2.0` shipped *"NOT CUT"*, `1.3.0` shipped *"`v1.3.0` DOES NOT EXIST"*, and the change list's head was stale within minutes of the tag that falsified it. **SO THE CUT-STATE SENTENCE IS DROPPED AS A CLAIM AND THE VERSION LINE STAYS**, because the version line is the ONE figure a tarball can be right about: the manifest knows it and travels beside it. **`scripts/publish.sh --figures` GENERATES AND ASSERTS THE VERSION LINE AND NOTHING ELSE** — the cut-state generation and its assertion are removed in the same act, so a tag set that moves can no longer redden this guide — **and the same one line is asserted in THREE shipped documents: this guide, `README.md` beside it, and the change list** (`DEF-AC`'s remaining demand, *"guard the release notes too"* and *"`README.md` still declares no version at all"*). **THE TAG SET IS NOT DELETED AND NOT DENIED: it is a RECORD OF RELEASES held in the change list, PAST TENSE — a list of what has been cut — and never a claim about the current one.** **AND WHAT THIS CLAUSE DOES NOT SAY, SAID PLAINLY BECAUSE A READER WILL LOOK FOR IT: it does not say whether the version this guide prints is cut.** That is not a document's to state; the record of what has been cut is in `RELEASE-NOTES.md`, and the registry is the registry.
|
|
37
|
+
|
|
24
38
|
**⟶ CORRECTED 2026-10-26 — THE VERSION THIS GUIDE DESCRIBES IS `1.0.0`, NOT `0.5.0`, AND THE SENTENCE ABOVE STANDS
|
|
25
39
|
BYTE-FOR-BYTE AS THE READING OF THE DAY THIS FILE WAS WRITTEN AND IS READ THROUGH THIS CLAUSE** (`RCA-8(g)`
|
|
26
40
|
ANNOTATE-BESIDE; `DEF-2026-10-02`: no whole-file rewrite). **`bindings/js/package.json`'s `version` field read at its
|
|
@@ -49,7 +63,13 @@ measurements on a locally packed, installed artefact, and is still not witnessed
|
|
|
49
63
|
|
|
50
64
|
**⟶ AND THE RELEASE IN PREPARATION IS NOT THIS GUIDE'S VERSION, 2026-10-28 — THE MANIFEST IS `1.1.0`, AND THIS GUIDE CLAIMS NO `1.2.0`.** `RCA-8(g)` ANNOTATE-BESIDE: **the four dated paragraphs above stand BYTE-FOR-BYTE and are read THROUGH this clause; this clause re-reads one figure and adds one pointer, and moves no other claim of this file.** **THE FIGURE, AT ITS OWN BYTES, READ THIS DATE: `bindings/js/package.json`'s `version` is `"1.1.0"` and all five `optionalDependencies` pins are `1.1.0` with it** — the six figures move together and never singly (§0 item 1; `PUB-DD-10`) — **so `1.1.0` is the version this guide DESCRIBES, and it is the version an installed tree answers to.** **A RELEASE IS IN PREPARATION AND IT HAS NOT BEEN CUT: what it adds is listed in `RELEASE-NOTES.md` at the repository root, under a `1.2.0` heading MARKED UNRELEASED, and nothing in that section is a version this guide claims or a behaviour this guide measured as shipped.** **AND THAT FILE SHIPS NOWHERE:** it stands at the repository root and no tarball carries it (§11's `files` allowlist, six rows), so a consumer reading `node_modules/provident-storeroom` has this guide and the six figures of `1.1.0`, and the `1.2.0` list is a CHECKOUT reader's document. **The rule, in one line: this guide describes what was cut (`1.1.0`); `RELEASE-NOTES.md` describes what is being prepared (`1.2.0`, UNRELEASED); neither claims the other's figure.** (`D-41` item `1`; §11's manifest table reads the same six figures; `docs/specs/storeroom-publish/contract.md §2.3` item `3`.)
|
|
51
65
|
|
|
52
|
-
**⟶ AND THE FIGURES MOVE TO THE PREPARED RELEASE, 2026-10-28 — THE SIX FIGURES THIS GUIDE POINTS AT READ `1.2.0`, AND `1.2.0` IS PREPARED, NOT CUT.** `RCA-8(g)` ANNOTATE-BESIDE: **the five dated paragraphs above stand BYTE-FOR-BYTE and are read THROUGH this clause — so do their readings of `0.5.0`, `1.0.0`, `1.0.1` and `1.1.0`; each was true of the tree it was read on.** **THE MANIFEST AT ITS OWN BYTES THIS DATE: `bindings/js/package.json`'s `version` is `"1.2.0"` and all five `optionalDependencies` pins are `1.2.0` with it** — the six figures move together and never singly (§0 item 1; `PUB-DD-10`) — **and the five platform manifests' own `version` fields read `1.2.0` with them, which is eleven sites and one rebuild.** **SO THE SITES ARE RE-READ, NOT RE-SPELLED:** this section's version line, §2.2's five exact pins, §8.1's *"an installed `0.5.0`"* and §11's manifest table all read `1.2.0`; **every `0.5.0`, `1.0.0`, `1.0.1` and `1.1.0` this guide prints is the figure of its writing date and reads `1.2.0` from here on.** **AND WHAT THIS CLAUSE DOES NOT CLAIM, SAID FIRST BECAUSE IT IS THE EASIER THING TO GET WRONG: `1.2.0` IS PREPARED AT THOSE SIX FIGURES AND THE TAG IS NOT CUT — `git tag --list` carries `v1.0.0` · `v1.0.1` · `v1.1.0` and no `v1.2.0`, so nothing here says the release is cut, and the tag is not cut until the architect cuts it.**
|
|
66
|
+
**⟶ AND THE FIGURES MOVE TO THE PREPARED RELEASE, 2026-10-28 — THE SIX FIGURES THIS GUIDE POINTS AT READ `1.2.0`, AND `1.2.0` IS PREPARED, NOT CUT.** `RCA-8(g)` ANNOTATE-BESIDE: **the five dated paragraphs above stand BYTE-FOR-BYTE and are read THROUGH this clause — so do their readings of `0.5.0`, `1.0.0`, `1.0.1` and `1.1.0`; each was true of the tree it was read on.** **THE MANIFEST AT ITS OWN BYTES THIS DATE: `bindings/js/package.json`'s `version` is `"1.2.0"` and all five `optionalDependencies` pins are `1.2.0` with it** — the six figures move together and never singly (§0 item 1; `PUB-DD-10`) — **and the five platform manifests' own `version` fields read `1.2.0` with them, which is eleven sites and one rebuild.** **SO THE SITES ARE RE-READ, NOT RE-SPELLED:** this section's version line, §2.2's five exact pins, §8.1's *"an installed `0.5.0`"* and §11's manifest table all read `1.2.0`; **every `0.5.0`, `1.0.0`, `1.0.1` and `1.1.0` this guide prints is the figure of its writing date and reads `1.2.0` from here on.** **AND WHAT THIS CLAUSE DOES NOT CLAIM, SAID FIRST BECAUSE IT IS THE EASIER THING TO GET WRONG: `1.2.0` IS PREPARED AT THOSE SIX FIGURES AND THE TAG IS NOT CUT — `git tag --list` carries `v1.0.0` · `v1.0.1` · `v1.1.0` and no `v1.2.0`, so nothing here says the release is cut, and the tag is not cut until the architect cuts it.**
|
|
67
|
+
|
|
68
|
+
**⟶ AND THE TAG THAT CLAUSE SAID WAS ABSENT IS CUT, 2026-10-28 — `v1.2.0` EXISTS, AND IT IS THE RELEASE THIS GUIDE TRAVELS IN.** `RCA-8(g)` ANNOTATE-BESIDE: **the six dated paragraphs above stand BYTE-FOR-BYTE and are read THROUGH this clause — so does the sentence *"`1.2.0` IS PREPARED AT THOSE SIX FIGURES AND THE TAG IS NOT CUT … the tag is not cut until the architect cuts it"*; it was true of the tree it was written on.** **THE TAG AT ITS OWN NAME THIS DATE: `git tag --list` answers `v1.0.0` · `v1.0.1` · `v1.1.0` · `v1.2.0`; the tag `v1.2.0` names a tree whose `bindings/js/package.json` reads `"version": "1.2.0"` at its own bytes and whose five `optionalDependencies` pins read `1.2.0` with it; and `git describe` reads `v1.2.0-1-ge7c211f`.** **SO `1.2.0` IS CUT AND THIS GUIDE DESCRIBES IT — the text you are reading travels inside the `1.2.0` tarball, and a guide that told you its own release did not exist would be the denial this clause closes.** **`RELEASE-NOTES.md` at the repository root still heads its `1.2.0` section `UNRELEASED` and still says no `v1.2.0` tag exists; that file ships nowhere (§11's `files` allowlist, six rows) and its heading is behind the tag — this guide states what was cut, and what was cut is `1.2.0`.** **NO OTHER CLAIM OF THIS GUIDE MOVES: the `files` allowlist's six rows (§11), the packed set's nineteen-bare/twenty-assembled (§11), the twenty-status set (§11) and the fourteen own members all stand exactly as they stood.** (§0's figure rule; `docs/specs/storeroom-publish/contract.md §2.3` item `3` — the byte-for-byte guide stage this correction travels by.) **THE ONE THING THAT IS STILL UNMOVED IS THE ONE THING NO PASS WITNESSED, AND IT IS NOT A FIGURE:** no `npm install` was run against the registry, so a registry-installed `1.2.0` is described here from the manifest and from measurements on a locally packed, installed artefact, and is still not witnessed as a registry download. **NO OTHER CLAIM OF THIS GUIDE MOVES: the `files` allowlist's six rows (§11's dated re-read clause (i)), the packed set's nineteen-bare/twenty-assembled (§11's clause (ii)), the twenty-status set (§11's clause (iv)), the fourteen own members and every API clause stand exactly as they stood.** (`D-41` item `1`; `D-48` · `DEF-AC`; `docs/specs/storeroom-publish/contract.md §2.3` item `3` — the byte-for-byte guide stage this correction travels by; `scripts/publish.sh`'s eleven cells, and the tag coupling `release.md §2.5` puts AFTER them.)
|
|
69
|
+
|
|
70
|
+
**⟶ AND THE TREE THIS TEXT DESCRIBES IS NINE COMMITS PAST THE TARBALL IT TRAVELS IN, 2026-10-28 — THE MANIFEST IS `1.2.0`, `1.2.0` IS CUT AND PUBLISHED, AND `main` CARRIES BEHAVIOUR THAT IS NOT IN THAT TARBALL.** `RCA-8(g)` ANNOTATE-BESIDE: **the seven dated paragraphs above stand BYTE-FOR-BYTE and are read THROUGH this clause; this clause adds the one distinction none of them draws and corrects two sentences of the paragraph immediately above, and it moves no figure and no other claim of theirs.** **THE SIX FIGURES AND THE TAG, AT THEIR OWN BYTES THIS DATE:** `bindings/js/package.json`'s `version` is `"1.2.0"`, all five `optionalDependencies` pins are `1.2.0` with it, the five platform manifests' `version` fields read `1.2.0` with them — eleven sites, one rebuild — and `git tag --list` carries `v1.2.0`, **so the version line above is generated from exactly those two facts and it states that the release this guide travels in is CUT.** **AND THE TREE IS NINE COMMITS PAST THAT TAG:** `git describe` reads `v1.2.0-9-g989c216`, no version bump is in any of the nine commits, and **two of them change behaviour: `§8.0a`'s claim that the `log` belongs to the COMMIT — and that `seq` is the position in that list and not the store's journal ordinal — and `§12.6b`'s carry rule, that a reversal raised inside a pass is judged at that pass's terminal state, describe THIS TREE'S BEHAVIOUR AND NOT THE BEHAVIOUR OF THE `1.2.0` TARBALL** — `1.2.0` ships the per-attempt list and the sentence the carry rule withdraws. **So read this guide for what it is: it is the tree's text, and where it describes the two changes that are not in `1.2.0`, the change list at `node_modules/provident-storeroom/RELEASE-NOTES.md` is what tells you which release carries them — and until the next release is cut, nothing a consumer installs has them.** **AND THE TWO SENTENCES OF THE PARAGRAPH ABOVE THIS ONE THAT THIS CLAUSE CORRECTS:** *"that file ships nowhere (§11's `files` allowlist, six rows)"* is **false from 2026-10-28** — the change list is the allowlist's SEVENTH row and the release's assembly stages it byte-for-byte at the packaging root, so it ships (§11's `2026-10-28` clause (i)) — and *"its heading is behind the tag"* is **withdrawn at that file's own head**, where the `1.2.0` section is now marked **CUT AND PUBLISHED**. **THE VERSION LINE ABOVE IS NOT MOVED BY THIS CLAUSE AND MUST NOT BE: the manifest is `1.2.0` and `v1.2.0` exists, so the figure the preflight generates is the figure written there.** (`D-53`; `D-54`; `DEF-AI`; `DEF-AM`; `DEF-AJ`; the change list's `§MAIN`.)
|
|
71
|
+
|
|
72
|
+
**⟶ AND THE FIGURES MOVE TO THE PREPARED RELEASE, 2026-10-28 — THE ELEVEN SITES READ `1.3.0`, AND `1.3.0` IS PREPARED, NOT CUT.** `RCA-8(g)` ANNOTATE-BESIDE: **the eight dated paragraphs above stand BYTE-FOR-BYTE and are read THROUGH this clause — so do their readings of `0.5.0`, `1.0.0`, `1.0.1`, `1.1.0` and `1.2.0`; each was true of the tree it was read on.** **THE MANIFEST AT ITS OWN BYTES THIS DATE: `bindings/js/package.json`'s `version` is `"1.3.0"` and all five `optionalDependencies` pins are `1.3.0` with it** — the six figures move together and never singly (§0 item 1; `PUB-DD-10`) — **and the five platform manifests' own `version` fields read `1.3.0` with them, which is eleven sites and one rebuild; the rebuilt artefact is stamped from that manifest, so `version()` answers `1.3.0`.** **SO THE SITES ARE RE-READ, NOT RE-SPELLED:** this section's version line, §2.2's five exact pins, §8.1's *"an installed `0.5.0`"* and §11's manifest table all read `1.3.0`; **every `0.5.0`, `1.0.0`, `1.0.1`, `1.1.0` and `1.2.0` this guide prints is the figure of its writing date and reads `1.3.0` from here on.** **AND WHAT THIS CLAUSE DOES NOT CLAIM, SAID FIRST BECAUSE IT IS THE EASIER THING TO GET WRONG: `1.3.0` IS PREPARED AT THOSE ELEVEN FIGURES AND THE TAG IS NOT CUT — `git tag --list` carries `v1.0.0` · `v1.0.1` · `v1.1.0` · `v1.2.0` and no `v1.3.0`, and the registry carries no `1.3.0`; so nothing here says the release is cut, and the tag is not cut until the architect cuts it.** **AND THE TREE THIS GUIDE DESCRIBES IS TEN COMMITS PAST `v1.2.0`** — `git describe` reads `v1.2.0-10-g14787b5` and `git rev-list --count v1.2.0..HEAD` reads `10`, one commit past the nine the paragraph above reads — **so the two behaviour changes and the four smaller items `§1.3.0` of the change list declares are the CONTENT of `1.3.0`, and `1.3.0` is the version that carries them once it is cut; the `1.2.0` tarball carries neither, and `1.3.0` is the version this guide describes from here on.** **THE ONE THING THAT IS STILL UNMOVED IS THE ONE THING NO PASS WITNESSED, AND IT IS NOT A FIGURE:** no `npm install` was run against the registry, so a registry-installed `1.3.0` is described here from the manifest and from measurements on a locally packed, installed artefact, and is still not witnessed as a registry download. **NO OTHER CLAIM OF THIS GUIDE MOVES: the `files` allowlist's seven rows (§11's `2026-10-28` clause (i)), the packed set's `20` bare / `21` assembled (§11's clause (ii)), the twenty-status set (§11's clause (iv)), the fourteen own members and every API clause stand exactly as they stood.** (`D-41` item `1`; the change list's `§1.3.0`; `docs/specs/storeroom-publish/contract.md §2.3` item `3` — the byte-for-byte guide stage this correction travels by; `scripts/publish.sh`'s eleven cells and its two generated figures.)
|
|
53
73
|
|
|
54
74
|
**AND ONE THING A READER OF THE INSTALLED PACKAGE SHOULD KNOW BEFORE THE CITATIONS: WHEN THIS DOCUMENT IS AT
|
|
55
75
|
`node_modules/provident-storeroom/README.guide.md`, IT IS THIS GUIDE.** **Its ONE authored home is
|
|
@@ -399,7 +419,8 @@ is a set-position class refusal, not a tier.**
|
|
|
399
419
|
leaves every other tier's version at that key untouched** (`write-and-read.md §4.4`, arm `D-1`). **A `delete` with
|
|
400
420
|
no tier removes every version at the target and all downstream of each; a delete of the empty address empties the
|
|
401
421
|
register; and a delete that removes nothing is a SUCCESS — it answers `tiers: []` rather than throwing**
|
|
402
|
-
(`write-and-read.md §4.3`, `§4.4` arms `D-2`, `D-3` and `D-5`).
|
|
422
|
+
(`write-and-read.md §4.3`, `§4.4` arms `D-2`, `D-3` and `D-5`). **⟶ AND A `delete` THAT REMOVES SOMETHING NOW PASSES CONSTRAINTS** — see §7.8; a delete that removes
|
|
423
|
+
nothing is not an edit and runs no pass.
|
|
403
424
|
|
|
404
425
|
### §5.4 Undo is bounded by the journal, and `rollback` is not a single-op undo
|
|
405
426
|
|
|
@@ -439,9 +460,16 @@ guide's constraint story that is not yet observable.
|
|
|
439
460
|
1. **A reversal INSIDE an open commit is NOT judged.** If you reverse from inside a constraint body, a repair, a listener, or the kernel's own failing-attempt floor call, the state the reversal leaves is not the state that stands (the open window moves it again, and the caller sees nothing yet) — **and judging it would invoke your constraint bodies MID-ATTEMPT and break `D-40`'s promise that a failed attempt leaves the store as it found it.** **The judgement is the AT-REST reversal's: your own `rollback` at the surface, where the state it leaves is the state you then read.**
|
|
440
461
|
2. **A DISABLED pair is NOT judged.** A pair you `constraintOff`'d still appears in `constraints()`, but the judgement asks only pairs that are enabled — **because a disabled pair cannot fire, so nothing it would refuse can have run.** Re-enable it and it is asked again at the next reversal.
|
|
441
462
|
3. **A pair registered STRAIGHT THROUGH THE KERNEL'S `K-1` is NOT judged at a reversal.** The judgement reads the package's own registry, which the package's declared registration routes write — `cfg.constraints` at construction and `addConstraint` after it — while a pair placed by a kernel-direct call is evaluated at writes but is **not** in the state the judgement reads (and `constraints()` does not list it either). **Register through the package's routes and the promise covers you.**
|
|
442
|
-
4. **WHEN IT REFUSES AND NO ATTEMPT STANDS, `log` IS ONE `constraint-check` ENTRY — NOT `[]`.** The `log` member is normally *"the attempt's own message log"*, and `[]` is its value where a failure's cause has no attempt; **a reversal's judgement is one of those causes, and here the refusal AUTHORS ITS OWN RECORD instead of reporting an absence:** `log.length === 1`, `log[0].kind === 'constraint-check'`, `log[0].id` the refusing pair's own id, `log[0].state` `null`, `log[0].opId` the reversal's own op id or `null`. **So do not read an empty array as *"nothing failed"* at this arm, and do not read this one entry as an attempt's history — it is the refusal's.**
|
|
463
|
+
4. **WHEN IT REFUSES AND NO ATTEMPT STANDS, `log` IS ONE `constraint-check` ENTRY — NOT `[]`.** The `log` member is normally *"the attempt's own message log"*, and `[]` is its value where a failure's cause has no attempt; **a reversal's judgement is one of those causes, and here the refusal AUTHORS ITS OWN RECORD instead of reporting an absence:** `log.length === 1`, `log[0].kind === 'constraint-check'`, `log[0].id` the refusing pair's own id, `log[0].state` `null`, `log[0].opId` the reversal's own op id or `null`. **So do not read an empty array as *"nothing failed"* at this arm, and do not read this one entry as an attempt's history — it is the refusal's.** **⟶ AND FROM 2026-10-28 *"the attempt's own message log"* IS READ THROUGH `D-53` AS THE COMMIT'S OWN LIST** (§8.0a above): at this arm no attempt and no commit stands, which is what makes `[]` the wrong value here and one authored entry the right one.
|
|
443
464
|
**AND ONE THING ABOUT A FAILING REPAIR, WHICH IS THE SAME SHAPE AND IS DECLARED RATHER THAN DISCOVERED: THE FLOOR CALL RESTORES THE ADDRESS THE ATTEMPT WROTE; AN ADDRESS A REPAIR LANDED AT BESIDE IT IS NOT RESTORED.** `D-40`'s sentence — *"a failed attempt leaves the store exactly as it found it"* — is measured true of the attempt's own address and false of a beside address: the store's rollback scope is the addressed subtree at or after the named op's own position, so a repair that writes elsewhere leaves THAT value standing after the failed attempt. **If you need the beside address's pre-attempt value, keep it yourself: the store's own floor call does not put it back** (`docs/specs/storeroom-js/contract.md §2.6`'s clause of 2026-10-26, which is where the four narrowings, the `log` arm and this spatial scope are declared; `E2E-C-9` cell 6 in `tests/e2e/blind-constraints-v2.mjs` is the measured reach).
|
|
444
465
|
|
|
466
|
+
**⟶ AND THE ONE SENTENCE THAT WAS MISSING, DECLARED HERE IN THE TERMS YOUR OWN REVIEW ASKED FOR, 2026-10-28 — `D-55` · `DEF-AN`, THE ARCHITECT, VERBATIM: *"A constraint-refused edit rolls back to the previous safe state, with all edits in the commit undone. Specify this."*** `RCA-8(g)` ANNOTATE-BESIDE: **every clause of §5.4 above and below stands BYTE-FOR-BYTE and is read THROUGH this one; no rank, no status, no member and no promise moves — what this clause adds is the statement of WHAT THE ROLLBACK COVERS.** **THE RULE, IN ONE SENTENCE: a refused edit rolls the commit back to the state it opened at — every edit the commit made is undone, including writes that succeeded before the refusal.** **THE REASON, STATED SO YOU CAN REASON WITH IT: the restore names the mark the commit cached at its OUTERMOST OPENING** (§5.4's closing clause above), **and a commit is one unit of work, so the mark is not the refused op's own — it is where the WHOLE commit began.** **A write, a repair or a delete that landed EARLIER IN THE SAME COMMIT is undone WITH the refusal**, because the commit that carried it is undone; **that is WHY the mark is the outermost opening's and not the nearest one.** **WHAT STILL STANDS: a refused edit moves no state and mints no code beyond the one declared refusal; an edit in a DIFFERENT commit is not touched** — a listener body's own write opens its own commit (`D-53`), so it is a unit of its own and is not undone by yours. **MEASURED ON THIS CHECKOUT, 2026-10-28:** a write that succeeds inside a commit whose listener then reverses and is refused leaves the store at the state the commit opened at — the successful landing gone with the reversal — and the pair is asked at that state (`tests/e2e/discard-witness.mjs`'s `DW-10.5`).
|
|
467
|
+
|
|
468
|
+
**⟶ AND WHAT THE STORE HOLDS AFTER A REFUSED REVERSAL, DECLARED HERE BECAUSE `1.2.0`'s ONE BEHAVIOUR CHANGE IS THIS AND NO SHIPPED DOCUMENT SAID IT UNTIL NOW.** `RCA-8(g)` ANNOTATE-BESIDE: **every clause of §5.4 above stands BYTE-FOR-BYTE and is read THROUGH this one; this clause adds the declaration the release owed and moves no promise it prints.**
|
|
469
|
+
|
|
470
|
+
- **THE FACT: WHEN A REVERSAL IS REFUSED, THE COMPENSATING ACT PUTS THE STORE BACK AT THE MARK THE COMMIT CACHED WHEN IT OPENED — the journal's LAST VALID POSITION as it stood when that commit began (an op id, or the FLOOR where the journal held no entry then) — and never at whatever the journal's head happens to be when the refusal is built.** **Every address the reversal MOVED is read again afterwards and counted as restored only where it stands exactly as it stood before the reversal — the same value at the same tier, or absent where it was absent — and the refusal's one log entry says so address by address**, naming each address that could not be put back with what stands there instead and what it was owed (§8.1 row `20`; `js/contract.md §2.6`'s clause of 2026-10-27). **A reversal taken AT REST makes the two marks describe the same state** — it appends no journal entry — **so this is where the store stands either way at the surface.**
|
|
471
|
+
- **WHAT `1.2.0` CHANGED, IN ONE SENTENCE, SO YOU KNOW WHICH BEHAVIOUR YOUR VERSION HAS: BEFORE `1.2.0` THE RESTORE NAMED THE JOURNAL'S HEAD; FROM `1.2.0` IT NAMES THE OPENING MARK.** **Why the head was wrong is the measurement the ruling was taken on (`DEF-AE`):** *"a reversal appends no journal entry, so the head describes the state that stood before the action"* is true of a reversal **you** take at rest and **false of one taken inside a write's own commit** — there the ENCLOSING WRITE'S OWN OP stands at the head when a listener body's reversal runs, so the old compensation restored the write's LANDING and **a write that reported a refusal had left its own value standing** (measured: `9` stayed `9` where the refused write should have restored `7`). **In `1.2.0` a write that reports a refusal leaves the store where the ATTEMPT found it, not where the refusal's own op landed.** **A LAWFUL reversal is unchanged** (same `{opId, undoneOps, restoredTo}`, no throw), **and a lawful write still answers its record with its value standing.** **MEASURED ON THIS CHECKOUT, 2026-10-28:** a refused write restores the pre-attempt value at its own tier, and the reversal's own refusal restores the pre-reversal standing tier for tier (`tests/e2e/def-ae-restore-mark.test.mjs` cells `1`–`2`; `tests/e2e/refused-reversal-restore.test.mjs`'s shadow, higher-tier/parent and absent arms). **So the store after a refused reversal holds exactly the state the act FOUND — not a later landing that the refusal's own sentence has already told you was undone.**
|
|
472
|
+
|
|
445
473
|
---
|
|
446
474
|
|
|
447
475
|
## §6 · Watching for changes: the listener surface
|
|
@@ -728,16 +756,78 @@ body.**
|
|
|
728
756
|
|
|
729
757
|
**⟶ AND THE PASS ASKS EXACTLY THE PAIRS STANDING AT THE ADDRESS IT WRITES, 2026-10-28 — `D-50`'S CONSEQUENCE, WHICH NO CLAUSE ABOVE SPELLS OUT.** `RCA-8(g)` ANNOTATE-BESIDE: **every clause of §7.6 above stands BYTE-FOR-BYTE and is read THROUGH this one; this clause adds one declaration and moves nothing it prints.** **THE RULE: A WRITE'S CONSTRAINT PASS JUDGES THE PAIRS STANDING AT THE ADDRESS IT WRITES — the kernel's own `pairsAtAddress` reads the pair record at that one address — SO A PAIR AT A DIFFERENT ADDRESS IS NOT ASKED.** **If a constraint cares about another location it READS it** (`D-50`, the architect, verbatim: *"If a constraint needs to look for the data at a given location, it can read()"*). **MEASURED 2026-10-28, on this checkout, through the published entry, in a two-address drive:** a pair `b1` registered at `['b']`; **a write at `['a']` asked `b1` `0` times**; **the same write at `['b']` asked it once**, and the value the body was handed was the one that write returned. **So an action one address away is INVISIBLE to your pair** — it was never asked at the address the action moved — **and the body's own `store.read` is the only thing that makes it visible.** **That is the whole reason this section tells you to read what you care about: the pass will not ask a pair that does not stand where you wrote.** (`held-listener/contract.md §2.8` item `1`, `HL-8`: *"the pass selects the rows whose address IS the triggering address"*, preserving the EXACT-ADDRESS rule rather than widening it; `D-50`; §7.1–§7.5's declarations; the pass's own scan at `bindings/js/src/entry/kernel-foundations.mjs:782`.)
|
|
730
758
|
|
|
759
|
+
**⟶ AND THE PASS'S SET IS NOW A PREFIX SET, 2026-10-29 — `D-56`, WHICH SUPERSEDES THE CLAUSE ABOVE.** `RCA-8(g)` ANNOTATE-BESIDE: **the paragraph above stands BYTE-FOR-BYTE as the rule of the build that printed it, and it is READ THROUGH this one.** **THE ARCHITECT, VERBATIM** (`strata/decisions.md` · `D-56`): *"A listener/constraint should fire when the descendants of its targeted address node change, because the descendants become part of the object that is read from that address"*. **SO A PAIR AT `['tabs']` IS ASKED FOR A DELETE AT `['tabs','landingPage']`** — the value read at `['tabs']` CONTAINS the subtree the delete moved, so your pair judges an object that moved — **and a pair at `['tabs','landingPage']` is still asked for that same delete.** **WHAT IS STILL NOT ASKED IS THE OTHER DIRECTION**, which the ruling does not cover: a delete at `['tabs']` does not ask the pair at `['tabs','landingPage']`, and the object that pair judges then reads as ABSENT. **THE MEMBERSHIP RULE, STATED ONCE: your pair is asked when THE ACT'S ADDRESS IS AT OR BELOW YOUR PAIR'S ADDRESS.** **AND WHAT YOUR PAIR IS HANDED DID NOT MOVE**: the context carries THE ACT'S `address`, `tier` and `value` — so a pair standing at an ancestor is handed the value that landed AT THE ACT'S ADDRESS (absence, for a delete), **not** the object at its own. **A PAIR THAT JUDGES ITS OWN ADDRESS READS IT** (`§7.6`'s `read()` rule, which `D-56` leaves standing — it is no longer NECESSARY for an ancestor's interest in a descendant, and it is still correct for the pair's own object). **MEASURED 2026-10-29, on this checkout, through the published entry** (`tests/e2e/discard-witness.mjs`'s `DW-10.13`/`DW-10.15`/`DW-10.16`): a pair at `['window','tabs']` and a delete at `['window','tabs','landingPage']` — **the pair IS asked (2 asks: its registration's and the pass's), its refusal crosses, and the delete rolls back** (`window.tabs` reads `{"landingPage":{"active":true},"settings":{"active":false}}` again); a pair AT the act's own address is asked once by the pass, unchanged; a pair at the act's DESCENDANT is asked `0` times and its address reads ABSENT afterwards.
|
|
760
|
+
|
|
731
761
|
### §7.7 ★ The check is BOUNDED — THREE restarts per terminal act, and past the budget it fails closed
|
|
732
762
|
|
|
733
763
|
**★ THE LIMIT, DECLARED HERE BECAUSE NO CLAUSE OF THIS GUIDE SAID IT: A CONSTRAINT CHECK RESTARTS AT MOST THREE TIMES PER TERMINAL ACT.** The bound is **`RESTART_LIMIT` = `3`**, one exported constant at **`bindings/js/src/entry/constraints.mjs:1366`**. **THE BUDGET IS SPENT AND NOT RESET by an action raised inside a restart** — a body that reverses again inside a restart's own ask spends the SAME budget and not a fresh one — **AND PAST IT THE CHECK FAILS CLOSED IN THE ONE FORM:** the write throws the same plain `{status, message, log}` it throws for a refused pass (§8), with `status` **`'ConstraintPassRefused'`** and **ONE log entry** whose message says the budget was spent rather than that a pair refused a state it never judged. **The store is restored** — the request's own watch records what is owed — so the terminal act leaves no unjudged effect standing (`D-42` item `3`; `D-41` item `4`).
|
|
734
764
|
|
|
765
|
+
**⟶ AND THE ARM PAST THE BUDGET NAMES THE PAIR THAT WAS BEING ASKED, AND SAYS WHAT IT IS, 2026-10-28 — `DEF-AL`, MEASURED AND CLOSED HERE.** `RCA-8(g)` ANNOTATE-BESIDE: **the two clauses above stand BYTE-FOR-BYTE and are read THROUGH this one; this clause re-reads what the refusal NAMES and what its `message` SAYS, and moves no status, no form and no bound.** **WHAT WAS MEASURED:** the arm named the FIRST WATCHED ADDRESS rather than the one the budget was spent at, **so registration order decided the accusation — the pair it named had answered `true` on every ask it received**, and the crossed `message` was **byte-identical to a genuine refusal's**, so a consumer reading only the message could not tell a pair refusing from a budget running out. **WHAT IT DOES NOW:**
|
|
766
|
+
- **the one `log` entry names the pair that was BEING ASKED when the budget ran out, and the address it was being asked at** — not the first watched address — **and its sentence says that pair refused nothing and that the check could not settle**;
|
|
767
|
+
- **the crossed `message` keeps the store's generated sentence for `ConstraintPassRefused` and then says, in its own words, that this is a RESTART BUDGET EXHAUSTION and NOT a constraint pair refusing a state** — no pair refused the state the check was asking it for, a pair body acted inside the check at every restart, and the budget is spent. **A GENUINE refusal never carries that clause**, so the two are distinguishable by the message alone.
|
|
768
|
+
**`status` stays `ConstraintPassRefused`, the form stays `{status, message, log}`, ONE log entry stands, and the store is still restored by the refusal's own compensating act.** (`D-42`; `D-41` item `4`; §7.7's bound above.) **MEASURED 2026-10-28, on this checkout, with a passing pair registered FIRST: the entry named the passing pair and the passing pair's address before this clause, and names the pair the check was asking and its own address after it; the crossed message said `'t5' was refused by a constraint pass, the state this act would leave is not one the registered constraints admit` before and carries the budget's own clause after — beside a control in which a genuine refusal still reads as its refusing pair's own sentence.**
|
|
769
|
+
|
|
735
770
|
**MEASURED 2026-10-28, ON THIS CHECKOUT, THROUGH THE PUBLISHED ENTRY — AND THE MEASUREMENT IS THE POINT: THE RESIDUAL IS THE BUDGET, NOT THE STATE AND NOT THE JOURNAL.** A pair whose body raises ONE FRESH REVERSAL on every ask the restart makes, **with every ask passing**, was driven twice: **with `60` fresh reversals available it rests at exactly `3` restart asks** (the listener's own action plus one per ask — `4` actions in all), **and with `40` available it answers identically, `3`.** **The body had `57` and `37` reversals it never got to spend, and nothing structural stopped it: every ask it made passed.** The terminal write then refused `ConstraintPassRefused` with its log entry saying the budget was spent.
|
|
736
771
|
|
|
737
772
|
**AND WHY THE BOUND EXISTS, SAID PLAINLY: NO STRUCTURAL STATE DISTINGUISHES A BODY THAT KEEPS REVERSING FROM ONE THAT MEANS TO.** The machinery cannot tell a loop from a caller with more work to do, so the restarts are COUNTED and the check fails closed instead of spinning.
|
|
738
773
|
|
|
739
774
|
**WHAT DOES NOT SPEND THE BUDGET**, so a lawful in-commit action costs at most one round and no more: **a request whose watch shows no moved address is consumed and asks nothing** — it can neither loop nor exhaust the budget — and **the count belongs to the PASS's own scope**, so a nested pass (a listener body's own write) has a scope and a budget of its own, exactly as it has its own commit (`js/contract.md §2.6`'s clause of 2026-10-27, item `4`).
|
|
740
775
|
|
|
776
|
+
**⟶ AND WHEN TWO OF YOUR LISTENERS EACH REVERSE IN ONE COMMIT, THE CHECK JUDGES THE STATE THE COMMIT STANDS AT — BOTH RAISES, NOT THE LAST ONE, 2026-10-28 — `D-55` ITEM 6 · `DEF-AP`, MEASURED.** `RCA-8(g)` ANNOTATE-BESIDE: **every clause of §7.7 above stands BYTE-FOR-BYTE and is read THROUGH this one; the bound, the budget's own arm and `D-42`'s window rule do not move.** **WHAT WAS MEASURED ON `1.3.0`:** two listeners each reversing inside ONE commit left **only the LAST raise judged** — the earlier listener's violation was asked **zero** times, **registration order decided which raise was lost**, and when the survivor's judgement passed **the write returned normally with the violation standing**. **THE ROW'S OWN ESCAPE — declare that an overwritten raise may leave a violation unjudged — IS UNAVAILABLE**, because §5.4's safe-state rule is the promise. **WHAT THE PACKAGE DOES: the request's watch is MERGED to the EARLIEST reading of each address** — the state the commit opened at — **so the restarted check sees every address the WHOLE commit moved, whichever raise named it, and asks the pairs at the state the commit STANDS at.** **A raise is therefore never lost to a later one in the same commit**, and the write that used to answer normally now refuses. (`tests/e2e/discard-witness.mjs`'s `DW-10.7`: both pairs are asked at the drain, the write refuses `ConstraintPassRefused`, the whole commit is undone; under a revert of the merge the earlier pair is asked ZERO times and the write ANSWERS.)
|
|
777
|
+
|
|
778
|
+
### §7.8 ★ A `delete` IS AN EDITING VERB — it passes constraints, and its listeners run AFTER them
|
|
779
|
+
|
|
780
|
+
**★ FROM 2026-10-29 A `delete` RUNS A CONSTRAINT PASS OF ITS OWN** (`D-55` item `3`, verbatim: *"A delete is an
|
|
781
|
+
editing verb and must pass constraints. This includes all of the relevant commit and message logic."*). **`D-38`
|
|
782
|
+
said the same for `rollback`** (item `4`: *"A reverse is an editing verb and must be treated as above"*). **So the
|
|
783
|
+
three editing verbs — `write`, `delete`, `rollback` — all ask the pairs, and only a `read` does not (§7.5).**
|
|
784
|
+
|
|
785
|
+
**What you see, in order, when you call `store.delete(addr, tier?)`:**
|
|
786
|
+
|
|
787
|
+
1. **the delete lands** — the versions are removed exactly as they always were (`§5.3`);
|
|
788
|
+
2. **the pairs standing AT THE DELETED ADDRESS are asked against THE STATE THE DELETE LEFT** — the address's
|
|
789
|
+
absence, with the delete's own journal op handed to the pair as `context.op`, so a constraint can tell a
|
|
790
|
+
delete from a write and refuse one;
|
|
791
|
+
3. **every one of them passes ⇒ you get the record** — `{ address, tiers, triggered, op }` (`§3.3`), unchanged;
|
|
792
|
+
**one refuses ⇒ you get the ordinary refusal**: the same `{status, message, log}` every constraint refusal
|
|
793
|
+
throws (§8), `status: 'ConstraintPassRefused'`, its `log` naming the pair and carrying the kernel's token in
|
|
794
|
+
the terminal entry's `state`, **and the delete is UNDONE together with every edit the delete's own attempt made** (a repair's write included)
|
|
795
|
+
— the store stands at the state the commit opened at;
|
|
796
|
+
4. **and the listeners fire AFTER step 2** — so a listener fired by a delete observes a JUDGED state, never one a
|
|
797
|
+
constraint is about to refuse. **A refused delete fires no listener at all.**
|
|
798
|
+
|
|
799
|
+
```js
|
|
800
|
+
store.write(['window','tabs'], 'mem', { landingPage: { active: true } });
|
|
801
|
+
store.addConstraint({ address: ['window','tabs','landingPage'], id: 'landing-page-must-stand' },
|
|
802
|
+
(ctx) => (ctx.op !== null && ctx.op.kind === 'delete'
|
|
803
|
+
? { message: 'the landing page is not removable' }
|
|
804
|
+
: true));
|
|
805
|
+
|
|
806
|
+
store.delete(['window','tabs','landingPage']);
|
|
807
|
+
// throws { status: 'ConstraintPassRefused', message: 'the landing page is not removable', log: [ … ] }
|
|
808
|
+
// log[0] = { seq: 1, kind: 'constraint-check', id: 'landing-page-must-stand', opId: 'op-3', state: null, … }
|
|
809
|
+
// log[1] = { seq: 2, kind: 'outcome', id: null, opId: 'op-3', state: 'KC-3', … }
|
|
810
|
+
store.read(['window','tabs','landingPage']); // { active: true } — the delete was undone
|
|
811
|
+
```
|
|
812
|
+
|
|
813
|
+
**The pair is asked with `context.value` = the AFTER-state (absence), `context.tier` = the delete's tier or `null`,
|
|
814
|
+
and `context.attempt` = the attempt's own identity.** **A pair at an ANCESTOR address is not asked by the delete's
|
|
815
|
+
pass** — the pass keeps the write's exact-address rule and not the trigger set's prefix widening — even though
|
|
816
|
+
such a listener DOES fire for the delete (`§6.4`). **And a delete that removes nothing is not an edit**: `tiers: []`,
|
|
817
|
+
no pass, no refusal, no delivery (`§5.3`).
|
|
818
|
+
|
|
819
|
+
**Why you care: a delete cannot be used to slip a state past a constraint you registered, and a listener you write
|
|
820
|
+
for the use case in `§6.4` — "exactly one tab is active" — now runs on a state the pass has already approved.**
|
|
821
|
+
**WHAT THE WITNESS STILL SPEAKS FOR** (§8's `record.discarded`): a `rollback` your listener raises inside the
|
|
822
|
+
DELIVERY is still after every check, so it is reported and its effect stands — but a `rollback` a PAIR BODY raises
|
|
823
|
+
inside the delete's own pass is now drained and judged at the state the delete left (`§7.7`).
|
|
824
|
+
|
|
825
|
+
**MEASURED, ON THIS CHECKOUT, THROUGH THE PUBLISHED ENTRY:** a pair at the deleted address is asked **twice** —
|
|
826
|
+
its registration and the delete's pass — where before this clause it was asked **once**; a listener fired by a
|
|
827
|
+
delete reads the pass's ask already taken (`1`, where it read `0`) and the after-state the pass approved; a
|
|
828
|
+
refused delete leaves the store at the state it opened at; and the delete's log is the commit's ONE list with
|
|
829
|
+
`seq` `1..2` (`tests/e2e/discard-witness.mjs`'s `DW-10.8`–`DW-10.13`).
|
|
830
|
+
|
|
741
831
|
---
|
|
742
832
|
|
|
743
833
|
## §8 · Refusals
|
|
@@ -769,6 +859,71 @@ attempt at all, and every failure whose attempt exists carries that attempt's ow
|
|
|
769
859
|
`unlisten`, `register` and `removeListener`: every one was an `Object` whose `Object.keys` was
|
|
770
860
|
`["status","message","log"]`, whose `log` was `[]`, and which was not an `Error` instance.**
|
|
771
861
|
|
|
862
|
+
### §8.0a What the `log` IS: one list per COMMIT — and what that buys you
|
|
863
|
+
|
|
864
|
+
**⟶ ADDED 2026-10-28. THIS SUBSECTION IS THE ONE THING TO KNOW ABOUT `log`, AND IT CHANGES WHAT YOU WILL SEE
|
|
865
|
+
IN IT.** `RCA-8(g)` ANNOTATE-BESIDE: **the paragraph above stands BYTE-FOR-BYTE and is read THROUGH this
|
|
866
|
+
subsection.** **The architect, verbatim: *"If it's part of the same commit, then it uses the same message log.
|
|
867
|
+
If it opens a new commit, it uses the new commit's message log. Constraints/Repairs should always use the same
|
|
868
|
+
commit. Listeners should open a new commit, because the triggering commit already passed constraints"***
|
|
869
|
+
(`strata/decisions.md` · `D-53`).
|
|
870
|
+
|
|
871
|
+
**SO THE LIST BELONGS TO THE COMMIT AND NOT TO THE ATTEMPT.** One write is one commit, and everything that
|
|
872
|
+
commit does — the constraint passes that judged its value, a repair's own write, the re-run after that repair,
|
|
873
|
+
and the terminal arm's `outcome` entry — **appends to ONE array**. Consequences you can rely on, each measured
|
|
874
|
+
on this checkout:
|
|
875
|
+
|
|
876
|
+
- **`seq` runs `1..n` CONTINUOUSLY across those steps**, because it is *the ordinal of the list it stands in*
|
|
877
|
+
and there is one list. A repair's `commit` entry can stand in the middle of the list, between the refused
|
|
878
|
+
pass's entries and the re-run's: **it is a real entry of the same commit, not a second log.**
|
|
879
|
+
|
|
880
|
+
**⟶ AND WHAT `seq` IS TRUE OF, SAID PLAINLY, 2026-10-28 — `DEF-AI`, MEASURED.** **`seq` is the position of the
|
|
881
|
+
entry in the commit's own message log; it is not the store's journal ordinal, and the two differ by design.**
|
|
882
|
+
`RCA-8(g)` ANNOTATE-BESIDE: **the bullets above stand BYTE-FOR-BYTE and are read THROUGH this paragraph.** **THE
|
|
883
|
+
MEASUREMENT THAT OWES IT: a refusal's entry carried `seq` `1` while the store's own journal counter read `41`, and a
|
|
884
|
+
consumer read the two as one ordinal.** They are two ordinals with two homes — **`JournalOp.seq`, the store's
|
|
885
|
+
append ordinal that orders the store's record, and this `seq`, the kernel's append ordinal on the COMMIT's one
|
|
886
|
+
list** — and neither equals the other by construction. **AND THE ORDER ACROSS A COMMIT'S ATTEMPTS IS MEASURED, NOT
|
|
887
|
+
CLAIMED: one repair write appends the five entries `constraint-check` `1` · `repair-run` `2` · `commit` `3` ·
|
|
888
|
+
`constraint-check` `4` · `commit` `5` — strictly increasing in array order, continuous from `1`, across the pass,
|
|
889
|
+
the repair's own joined write and the re-run** (`tests/e2e/repair-in-commit.test.mjs`'s `D-46` (1) cell, printed).
|
|
890
|
+
**No entry is ever minted from the store's journal counter**: that would be a second ordinal with no consumer.
|
|
891
|
+
(`D-53`; `D-48` · `DEF-AI`; `index.d.ts`'s `LogEntry` carries the same sentence.)
|
|
892
|
+
- **A nested write a CONSTRAINT body makes is part of the same commit**, so **its entries are in YOUR list** —
|
|
893
|
+
which is why a constraint body that performs a refused write now shows you that refusal's own
|
|
894
|
+
`constraint-check` and `outcome` entries, verbatim, before the enclosing `outcome` entry. **That is `DEF-AK`'s
|
|
895
|
+
`constraint` host.** (Its `listener` host is unchanged, and the difference is the next bullet.)
|
|
896
|
+
- **A LISTENER's write OPENS A COMMIT OF ITS OWN**, so **its entries are NOT in the list of the commit that
|
|
897
|
+
triggered it**: *the triggering commit already passed constraints, so what the listener does is a new
|
|
898
|
+
question.* If you need to know what the listener's own write decided, catch the refusal the listener body
|
|
899
|
+
lets out — it carries **its own** `log`, and so does a refusal you throw out of a listener.
|
|
900
|
+
- **A failed `file`-tier SAVE now crosses WITH THE COMMIT'S ENTRIES — its `log` is no longer `[]`.** The save is
|
|
901
|
+
the write's LAST constraint: when it fails, the one `outcome` entry that ends your list carries **`KC-9`**,
|
|
902
|
+
and the entries before it are everything the commit did. **So a consumer can tell the two failing arms apart
|
|
903
|
+
from the log alone: `ConstraintPassRefused` ends at `KC-3`/`KC-5`/`KC-6`/`KC-8`, `FileCommitFailed` ends at
|
|
904
|
+
`KC-9`.** (See `§8.1`'s token line below — the kernel's set is `KC-1`…`KC-9` from this date, and the store's
|
|
905
|
+
closed status set has not moved.)
|
|
906
|
+
- **A refusal that names no attempt still carries `[]`** — a `listen` refusal, a construction refusal, a
|
|
907
|
+
detached member's — and a `TypeError` from a constraint method carries `[]` too. **The list is the COMMIT's
|
|
908
|
+
and there is no commit there.**
|
|
909
|
+
|
|
910
|
+
**READ THIS BEFORE YOU LOG-COUNT.** `log.length` is **not** *"how many constraints were evaluated"* and not
|
|
911
|
+
*"how many attempts this write made"*: it is **every entry of the commit**, in append order, from every attempt
|
|
912
|
+
the commit held. Count entries by `kind` and `id` (and by `opId` where you need the iteration) rather than
|
|
913
|
+
assuming a shape, because **a repair or a body's own write legitimately adds entries to the list you caught.**
|
|
914
|
+
|
|
915
|
+
**AND THE ONE PLACE THE INVARIANT IS NOT REACHED, SAID PLAINLY RATHER THAN LEFT TO A MEASUREMENT: a pair body
|
|
916
|
+
invoked at a REVERSAL's judgement has no commit record standing beside it to name**, so a write such a body
|
|
917
|
+
makes still opens a commit of its own and its entries land in that commit's list and not in the reversal's
|
|
918
|
+
refusal (`js/contract.md §2.6`'s `D-53` clause, item `6`).
|
|
919
|
+
|
|
920
|
+
**⟶ AND A THROWN MESSAGE STANDS IN THAT LOG, 2026-10-28 — `D-55` ITEM 1 · `DEF-AK`, MEASURED.** `RCA-8(g)` ANNOTATE-BESIDE: **every clause of §8.0a above stands BYTE-FOR-BYTE and is read THROUGH this one; the list's home, its six members, its one-`kind`-per-entry rule and its `seq` ordinal do not move.** **THE RULING, THE ARCHITECT, VERBATIM: *"Include a thrown message in the message log. Obviously."*** **WHAT `1.3.0` DID, MEASURED: a constraint body that threw its own `Error` crossed `ConstraintBodyThrew` whose `log` carried only the attempt's `outcome` entry — the thrown value's own literal occurred ZERO TIMES in it — while the crossed sentence said *"a constraint body's throwable crosses as this refusal"*, a carriage the log did not make.** **WHAT YOU GET NOW:**
|
|
921
|
+
- **the value the body threw is PRINTED IN THE LOG**, in the ONE entry the attempt's terminal act appends (the `outcome` entry): *"the constraint `c1` threw; the attempt is closed — the value it threw was …"*. **THE FORM IS NAMED AND NOT SILENTLY STRINGIFIED**: an `Error` (or any object) reads *"an object whose own `message` member is the string "…""*, a thrown string reads *"a string whose text is "…""*, a thrown number *"a number whose value is 42"*, a thrown `void 0` *"no value at all"*, and an object with no `message` *"an object carrying no own string `message` member"*. **Nothing is read off a prototype and no caller code runs to spell it**;
|
|
922
|
+
- **and the crossed `message` stops claiming the value crosses**: after the store's generated sentence it says, in its own words, that the value the body threw does **NOT** cross as this refusal — and it names where the form it threw stands, which is that `log` entry. **THE CALLER'S OWN TEXT IS QUOTED IN THE LOG AND NEVER IN THE SENTENCE**, so a forged `{status, message}` cannot make the package speak a refusal it did not find (`DEF-A`'s protection, unmoved);
|
|
923
|
+
- **where no attempt stands there is no commit log to carry it** — a registration-time body's throw crosses with `log: []`, which is `surface.md §2.5` item 4's declared arm — **and the crossed sentence says exactly that instead of pointing at a list that would not hold the form**. (`tests/e2e/discard-witness.mjs`'s `DW-10.3`/`DW-10.4` print the readings for an `Error`, a string and an object with no `message`; both cells are RED under a revert of the terminal sentence.)
|
|
924
|
+
|
|
925
|
+
**⟶ AND THE RECORD YOU ARE HANDED IS FINAL, 2026-10-28 — `D-55` ITEM 6 · `DEF-AO`, MEASURED.** `RCA-8(g)` ANNOTATE-BESIDE: **every clause above stands BYTE-FOR-BYTE and is read THROUGH this one; the log's home on the commit, its entries and its identity are unmoved.** **THE RULING, THE ARCHITECT, VERBATIM: *"Attempts don't log. This is what the commits are for. The commits have their log handed off to the client and are dropped after closing out."*** **SO: a closed attempt's report and its log DO NOT MUTATE AFTER THE HANDOFF — a consumer holding a refusal is holding the record, and a record that changes after it is given out is not a record.** **WHAT THE PACKAGE DOES: at the end of the commit's own processing — the restarted check's amendment included, which is part of closing out — the report and the log it carries are `Object.freeze`d**, so a later act can neither append an entry nor re-member the report. **THE LIST IS THE SAME LIST** (`D-53`: *"the crossed report IS that list"*) — the refusal you caught and `report(store, ref)`'s `log` are ONE array, now sealed. **AND THE LIST IS SEALED WITH ITS COMMIT**, never at a nested attempt's own handoff: a constraint body's nested write is part of your commit (`D-53`), so freezing the list from inside it would stop the enclosing attempt's own `append`. (`tests/e2e/discard-witness.mjs`'s `DW-10.6` holds the crossed refusal and the closed report, drives four further acts, and prints the bytes before → after beside `Object.isFrozen` on both; RED under a revert of the seal.)
|
|
926
|
+
|
|
772
927
|
**EVERY REFUSAL NAMES ITSELF** — by a `status` from the store's own closed set, and by a **human `message` that
|
|
773
928
|
carries the address, the domain, and what was found instead.** The message is generated from the code and the
|
|
774
929
|
failing trigger's arguments and never hand-authored at a call site
|
|
@@ -791,6 +946,17 @@ answers it again**, and the status stops naming a layer that did not fail. The a
|
|
|
791
946
|
own date and is read THROUGH this clause** (the guide's figures move with the set's own membership, which is
|
|
792
947
|
this section's own rule).
|
|
793
948
|
|
|
949
|
+
**★ AND THE KERNEL'S OWN TOKEN SET — the one that rides `log[].state` — GREW BY ONE ON 2026-10-28, WHILE THIS
|
|
950
|
+
SECTION'S SET DID NOT MOVE AT ALL.** **The architect, verbatim: *"Open the token set"*** (`strata/decisions.md` ·
|
|
951
|
+
`D-53`). **The kernel's states are `KC-1`…`KC-9` from this date; the appended member is the SAVE ARM's own,
|
|
952
|
+
`KC-9`, and `KC-8` keeps its declared meaning (the composition's floor) unmoved.** **WHY IT WAS OWED: a save
|
|
953
|
+
failure and a constraint refusal both ended at `KC-8` on the arm where the failed save's compensation met a
|
|
954
|
+
floor code, so `KC-8` crossed as two statuses and neither arm was derivable from the log's terminal state.** **So
|
|
955
|
+
now: a constraint's refusal ends at `KC-3`/`KC-5`/`KC-6`/`KC-8`, a failed save ends at `KC-9`, and the two are
|
|
956
|
+
told apart by the list you caught.** **NO MEMBER OF THE STORE'S CLOSED STATUS SET MOVES AND NO RANK OF IT
|
|
957
|
+
MOVES**: `KC-9` rides `log[].state` exactly as every other token does, and `status` still never carries a `KC`
|
|
958
|
+
spelling at all — the paragraph above's own law (`A120` (2)), unmoved.
|
|
959
|
+
|
|
794
960
|
**⟶ RE-DERIVED 2026-10-26 TO THE ARCHITECT'S RULING AND NOT TO THE BYTES** (`DEF-S` and `DEF-N`; the architect,
|
|
795
961
|
verbatim: *"Expand the returned error set for both S/N"*). **`StoreStatus` carries NINETEEN members, plus the open
|
|
796
962
|
escape arm `(string & {})`** — ranks `1`…`17` stand exactly as the paragraph below prints them, and the two
|
|
@@ -828,6 +994,13 @@ whose union stopped at `17` would mistype both.** **So: do
|
|
|
828
994
|
not `switch` on these without a `default`, and NEVER treat an unknown `status` as success.** The set is the
|
|
829
995
|
store's to close; **the package mints no member, widens no set and renames none.**
|
|
830
996
|
|
|
997
|
+
**⟶ AND AN INNER REFUSAL A CONSTRAINT BODY THREW NOW CROSSES BESIDE THE CROSSED ONE, 2026-10-28 — `DEF-AK`, MEASURED AND DECLARED.** `RCA-8(g)` ANNOTATE-BESIDE: **the paragraph above stands BYTE-FOR-BYTE and is read THROUGH this one; no rank moves and no member is minted.** **WHAT WAS MEASURED: a LISTENER body propagates an inner refusal VERBATIM — same `status`, same `message`, same two-entry `log` — while a CONSTRAINT body making the IDENTICAL write had it REPLACED by a generated `ConstraintBodyThrew` whose single log entry was the OUTER attempt's, with the inner refusal's own literal occurring ZERO TIMES anywhere. WHICH HOST YOU WERE INSIDE DECIDED WHAT YOU WERE TOLD.**
|
|
998
|
+
- **`ConstraintBodyThrew` is still the `status` that crosses** — the body did throw, and rank `18` says so — **but the inner refusal is now REACHABLE from what you catch, under `Symbol.for('provident-storeroom.inner-refusal')`:** a non-enumerable own member carrying the value the body threw, so **its own `status`, its own `message` and its own `log` are all there to read.** `Object.keys(caught)` still reads `["status","message","log"]` — three members in the form's own order — and the form is unmoved.
|
|
999
|
+
- **AND THE CROSSED SENTENCE STOPS CLAIMING MORE THAN IT DELIVERS.** Its own bytes — *"a constraint body's throwable crosses as this refusal"* — now stand followed by **the package's own clause saying the value the body threw does NOT cross as this refusal, naming the inner `status` and quoting the inner `message`, and saying where the inner refusal crosses instead.** A body that threw something that is **not** a refusal (**a `TypeError`, a string**) has **nothing carried beside the crossed form**; there is no inner refusal to name, and the generated sentence names the CODE rather than promising a value.
|
|
1000
|
+
- **A FORGED THREE-MEMBER OBJECT IS STILL NOT QUOTED.** The inner refusal is recognised by **a mark this package sets on the forms IT builds** — never by the shape a caller can spell — so a body throwing `{status, message, log}` of its own gets the generated sentence and nothing of its text is repeated back as though the package had found a refusal there (`DEF-A`'s protection, extended one layer in).
|
|
1001
|
+
- **THE LISTENER ARM IS UNCHANGED, AND THAT IS THE CONTROL:** nothing marks a listener's throw, so it still crosses **as itself**, by identity — `caught === thrown`, its own `status`, `message` and `log`. **A `rollback` whose judgement a pair's body threw** crosses `ConstraintBodyThrew` at rank `18` and carries any inner refusal the same way (`§5.4`).
|
|
1002
|
+
- **⟶ AND THE CONSTRAINT HOST'S LIST IS NOW THE COMMIT'S, WHICH IS THE DEEPER HALF OF THE SAME RULING (2026-10-28, `D-53`).** The inner refusal's own two entries are **IN the crossed report** — the `constraint-check` entry naming the inner pair and the `outcome` entry carrying `KC-3` — on the same array the enclosing attempt appends to, at a continuous `seq`; **`carriedInner.log` and `caught.log` are ONE array, not two.** **READ §8.0a ABOVE BEFORE COUNTING ENTRIES**: the listener's list is still its own, because the listener's write opens its own commit, and that difference between the two hosts is now the difference the ruling draws rather than an asymmetry nobody declared.
|
|
1003
|
+
|
|
831
1004
|
**AND `ValueRefusedAtBoundary` IS THE ONE YOU WILL MEET ON THE VALUE ROUTE:** the package's own walk decides a
|
|
832
1005
|
value's class **before any store verb is called** and answers this member with the failing target's spelling
|
|
833
1006
|
(§10). **`ValueNotAdmitted` stays at rank `14` and is the STORE's own answer on the native route — a pass that
|
|
@@ -979,6 +1152,22 @@ removed version still stands again after the re-boot; **the save is the REFUSED
|
|
|
979
1152
|
entry (a save is an effect and not an op), and a save that cannot land is reported (`FileCommitFailed`, the
|
|
980
1153
|
in-memory state restored and the bytes not updated) rather than swallowed.**
|
|
981
1154
|
|
|
1155
|
+
**⟶ AND A `file`-TIER `DELETE` NOW SAVES ITS OWN OP, 2026-10-29 — THE SECTION HEADING ABOVE IS READ THROUGH THIS
|
|
1156
|
+
CLAUSE AND SO IS EVERY SENTENCE OF IT THAT NAMES A `delete`.** `RCA-8(g)` ANNOTATE-BESIDE: **THE READING ABOVE WAS TAKEN ON A TREE WHERE A `delete` MINTED NO
|
|
1157
|
+
`CommitRecord`, so nothing ever named its journal op to the save — and a
|
|
1158
|
+
consumer was told to expect a durable deletion back after a restart.** **WHAT MOVED:** the delete now mints the
|
|
1159
|
+
same four-member record a write's `K-2` mints, the entry adopts it, and **the terminal arm saves the DELETE's own
|
|
1160
|
+
op on its passing arm** — the same `commit_file` a `file`-tier write's save is. **MEASURED, ON THIS TREE:**
|
|
1161
|
+
`store.write(['a'], 'file', 1)` then `store.delete(['a'], 'file')` leaves the bytes at `filePath` reading `{}`
|
|
1162
|
+
where they read `{"a":1}`, and a fresh `storeCreate` over that path answers `AddressNotFound` where it answered
|
|
1163
|
+
`1`. **WHAT STILL READS AS THE SECTION SAYS, SAID SO THE CORRECTION IS NOT TAKEN WIDER THAN IT IS:** **the `delete`
|
|
1164
|
+
VERB still commits nothing of its own** (`DD-28`; the save is the ARM's), **a `rollback` still performs no file
|
|
1165
|
+
I/O** (its control reading is unmoved), **a WHOLE-TARGET delete — no tier named — names an op standing at no
|
|
1166
|
+
tier, so nothing is saved and the version still comes back after a re-boot**, and **a save that cannot land is
|
|
1167
|
+
reported the write's way** (`FileCommitFailed`, the store's journal rollback first, the message second, the
|
|
1168
|
+
commit's log carrying the one `outcome` entry at `KC-9`) rather than swallowed. **So the one-line advice above is
|
|
1169
|
+
now: a `file`-tier deletion is durable; a whole-target deletion is not.**
|
|
1170
|
+
|
|
982
1171
|
### §9.4 When the save itself fails
|
|
983
1172
|
|
|
984
1173
|
**On a `file`-tier write's save that fails — the act itself failing, not a constraint — the store rolls the write
|
|
@@ -1170,6 +1359,8 @@ a change of licence.**
|
|
|
1170
1359
|
|
|
1171
1360
|
**⟶ AND FOUR FIGURES OF THIS SECTION ARE RE-READ AT THE SHIPPED TREE, 2026-10-26, EACH AT ITS OWN SITE, SO THE ALLOWLIST IS NOT LEFT AT A COUNT THE MANIFEST NO LONGER CARRIES.** `RCA-8(g)` ANNOTATE-BESIDE: **every table row, every ★ paragraph and every figure above stands BYTE-FOR-BYTE and is read THROUGH this clause; THIS clause re-reads four figures and moves no rule of theirs — the reachability rule, the licence reading's ground and the `engines` floor all stand whole.** **(i) THE `files` ROW READS SIX ROWS, NOT FIVE** — `bindings/js/package.json`'s `files` array, read at its own bytes: `index.mjs` · `README.md` · `README.guide.md` · `index.d.ts` · `src/entry/` · `src/constraint-kernel.mjs` — **`6 = 4 + 2` ✓, the four root files plus the entry directory and the kernel** — and **the sixth row is the consumer guide's landed copy, `README.guide.md`, staged byte-for-byte from `docs/guides/package-use.md` by the release's assembly and imported by nothing** (`scripts/publish.sh`'s guide stage; `distribution.md §4.2` item 3's documented-text class). **THE TWO *"FIVE"* READINGS ABOVE — the table's own *"five rows"* and the ★ paragraph's *"THE FIVE ROWS ABOVE"* — are the reading of their own date and are read THROUGH this clause; and so is the licence paragraph's *"the five allowlist rows"*, whose subject is UNMOVED: NONE of the SIX rows is a `LICENSE` row, and none will be unless `D-8`'s evidence condition is met.** **THE RULE THE COUNT CARRIES IS UNMOVED EITHER WAY: whatever the entry requires by path must ship, and the count is a fact of this tree and not a contract.** **(ii) THE PACKED SET READS NINETEEN MEMBERS BARE AND TWENTY ASSEMBLED** — measured at the shipped packers: **the bare tarball carries `19` files** (`1` `package.json` + `4` root files + `13` entry modules + `1` kernel, `1 + 4 + 13 + 1 = 19` ✓, the guide standing among the four root files), **and the assembled artefact carries `20`, the nineteen plus `LICENSE`, which lands only in the assembled tree** — so a consumer comparing a bare `npm pack` tree with the release's artefact should expect exactly one member's difference and it is the licence. **(iii) AND THE GUIDE ITSELF NOW SHIPS:** **this document's canonical text reaches `node_modules/provident-storeroom/README.guide.md` in an installed package, beside `README.md`** — so a reader of the installed tree holds the document this file is, at that one path, and the landing adds no code, no export and no import. **(iv) THE STATUS SET IS TWENTY** — the figure `§8.1`'s heading and table print, and the one `§8.1`'s own dated clause and `§12.3`'s entry still spell `nineteen`: **each is read through the correction of this date at its own site, and the crate's own `CODES` is `[&str; 20]` at its bytes**, so the declaration of record and the crate agree at twenty.**
|
|
1172
1361
|
|
|
1362
|
+
**⟶ AND THE CHANGE LIST NOW SHIPS AND THE ALLOWLIST NAMES SEVEN ROWS, 2026-10-28 — `DEF-AC` (a), MEASURED.** `RCA-8(g)` ANNOTATE-BESIDE: **every row and every dated clause above stands BYTE-FOR-BYTE and is read THROUGH this clause; THIS clause re-reads three figures of this section and adds one document, and it moves no rule of theirs.** **(i) THE `files` ROW READS SEVEN ROWS, NOT SIX** — `bindings/js/package.json`'s `files` array, read at its own bytes: `index.mjs` · `README.md` · `README.guide.md` · `RELEASE-NOTES.md` · `index.d.ts` · `src/entry/` · `src/constraint-kernel.mjs` — **`7 = 5 + 2` ✓, the five root files plus the entry directory and the kernel** — and **the seventh row is the CHANGE LIST's landed copy, `RELEASE-NOTES.md`, staged byte-for-byte from the repository root by the release's assembly and imported by nothing**, the same mechanism and the same declared non-code class as `README.guide.md`. **NONE OF THE SEVEN IS A `LICENSE` ROW**, and none will be unless `D-8`'s evidence condition is met. **(ii) THE PACKED SET READS TWENTY MEMBERS BARE AND TWENTY-ONE ASSEMBLED** — **the bare tarball carries `20` files** (`1` `package.json` + `5` root files + `13` entry modules + `1` kernel, `1 + 5 + 13 + 1 = 20` ✓, the guide and the change list standing among the five root files), **and the assembled artefact carries `21`, the twenty plus `LICENSE`** — so a consumer comparing a bare `npm pack` tree with the release's artefact still expects exactly one member's difference and it is still the licence. **(iii) AND THE CHANGE LIST IS ON DISK IN AN INSTALLED PACKAGE**: `node_modules/provident-storeroom/RELEASE-NOTES.md` is **the declaration of `1.2.0`'s one behaviour change** — *"a refused reversal now restores to the commit's opening mark, not the journal's head"* — and **that is the row's own subject: a consumer receives the behaviour change WITH a shipped declaration of it, not a root document it can only reach by reading the repository** (`scripts/publish.sh`'s notes stage; the preflight REFUSES a drifted landing and refuses an allowlist that does not name it). **THE RULE THE COUNT CARRIES IS UNMOVED EITHER WAY: whatever the entry requires by path must ship, and the count is a fact of this tree and not a contract.**
|
|
1363
|
+
|
|
1173
1364
|
**AND THE `engines` FLOOR IS ONE DECLARATION WITH ONE READER — YOURS.** `>=18.0.0` is what the artefact loads on
|
|
1174
1365
|
and it is honest rather than provisional: the addon's highest-referenced Node-API class is the `napi_*` pair at
|
|
1175
1366
|
**Node-API version 6** (`Node 12.17.0` / `10.20.0`), read at `bindings/js/build.rs:35`–`:36`, and the ESM entry uses
|
|
@@ -1329,14 +1520,27 @@ tree sharing no mutable member with the store or with another crossing**, which
|
|
|
1329
1520
|
|
|
1330
1521
|
**Two windows in the constraint machinery are known and are NOT defects. Each is a state you can leave.**
|
|
1331
1522
|
|
|
1332
|
-
1. **A `delete`'s listener delivery.** A listener invoked by a delete's delivery runs **no constraint pass** — there is no attempt for a pass to run over. **A `rollback` issued from inside it is unjudged, and its effect stands.**
|
|
1523
|
+
1. **A `delete`'s listener delivery** — **AND FROM 2026-10-29 THE DELETE ITSELF RUNS ONE, WHICH IS WHY THIS WINDOW IS DEFINED BY THE DELIVERY AND NOT BY THE VERB (§7.8: the delivery sits AFTER the pass, so a listener fired by a delete sees a JUDGED state).** A listener invoked by a delete's delivery runs **no constraint pass** — there is no attempt for a pass to run over. **A `rollback` issued from inside it is unjudged, and its effect stands.**
|
|
1333
1524
|
2. **A registration's own evaluation.** `addConstraint`'s and `constraintRegister`'s evaluation of a supplied body runs **no pass** for the same reason — **an action raised there is unjudged and its effect stands.**
|
|
1334
1525
|
|
|
1335
1526
|
**THE FIRST EXIT — RELOAD THE FILE.** Since `1.1.0`, **a restore that moved a version standing at the `file` tier SAVES the restored image**, so **the file records the state the store was restored to, and a fresh boot over that path returns to judged bytes.**
|
|
1336
1527
|
|
|
1337
1528
|
**THE SECOND EXIT — REGENERATE THE STORE.** Construct a new instance and replay your writes; **a judged state by construction.**
|
|
1338
1529
|
|
|
1339
|
-
**Neither window is new and neither is a defect**: both are places **a pass does not begin**, not places a judgement was skipped mid-flight.
|
|
1530
|
+
**Neither window is new and neither is a defect**: both are places **a pass does not begin**, not places a judgement was skipped mid-flight.
|
|
1531
|
+
|
|
1532
|
+
**⟶ AND THE SENTENCE THAT STOOD HERE IS WITHDRAWN AS FALSE, 2026-10-28 — `D-54`, MEASURED, AND IT IS WITHDRAWN RATHER THAN NARROWED. THE BYTES WERE: *"And every pass that DOES run drains its own restart request — so the fail-state is bounded by the two acts that run none."* THE CONSUMER MEASURED THREE HOSTS WHERE NEITHER HALF HOLDS — a repair body, a delete's pass, and a reentrant reversal-then-write — AND IN EACH ONE A PASS RAN, THE REVERSAL WAS LEFT UNJUDGED, AND ITS EFFECT STOOD (zero judge invocations; the refused `5` still there). What follows replaces them and is true of the code after that fix.**
|
|
1533
|
+
|
|
1534
|
+
**THE RULE NOW, IN ONE SENTENCE: NO PASS CONSUMES A RESTART REQUEST IT DID NOT RAISE, AND A REQUEST RAISED INSIDE A PASS IS CARRIED TO THAT PASS'S OWN TERMINAL STATE AND JUDGED THERE — ONCE, AS `D-38` DECLARES, AND NEVER PER STEP.** The budget, its three asks, the failing-closed arm and `D-42` are exactly as they were; what changed is where the request is carried.
|
|
1535
|
+
|
|
1536
|
+
- **A PASS THAT RUNS TO ITS PASSING ARM** drains the request its bodies raised after the pass and before its commit closes — unchanged, and the arm this section always described.
|
|
1537
|
+
- **A PASS THAT ENDS BY THROWING** reaches the SAME judgement: a repair body that reversed and then broke the attempt's re-run, or a pair that refused after its delivery's act, is judged at the TERMINAL state the pass left, inside the same window, before you meet the pass's answer. **A pass's own refusal is superseded by the verdict of the check it invalidated — the state that pass left**; a constraint body's own throwable and a store's own refusal still cross as themselves, so no caller's value is re-shaped.
|
|
1538
|
+
- **A NESTED PASS NO LONGER DROPS AN ENCLOSING WINDOW'S REQUEST.** The restart's consumer compares window identities; where they differ it ASKS NOTHING AND TAKES NOTHING. The request stands for the act that raised it (`D-42` item `3`), and the enclosing pass's own check is the one that judges it. **A request no pass ever matches still dies with its window**, and the two passless windows below have their own answers.
|
|
1539
|
+
- **AND THE PACKAGE'S OWN COMPENSATING ROLLBACK IS NOT AN ACTION OF YOURS.** The kernel's terminal act composes the attempt's rollback through the `rollback` member; while it does, it carries a flag and neither raises a request nor is judged as a step — `D-42`: *"a restart is only ever raised by a consumer's own `rollback` member call."*
|
|
1540
|
+
|
|
1541
|
+
**SO THE FAIL-STATE OF PASS-HOSTED REVERSALS IS EMPTY, AND WHAT GUARANTEES IT IS ONE CONSUMER: the drain in the pass's own commit scope, reached on BOTH arms, matching the window that raised the request, and judging at the pass's terminal state.** The exits below are now owed to the two passless windows alone.
|
|
1542
|
+
|
|
1543
|
+
**YOUR `delete` QUESTION, ANSWERED HERE, AND IT IS A THIRD CASE AND NOT THE DECLARED ONE.** **⟶ AND ITS FIRST CLAUSE IS SUPERSEDED FROM 2026-10-29 — `D-55` ITEM `3` BUILT THE PASS THIS SENTENCE SAID DID NOT EXIST: a `delete` now runs a constraint pass of its own kind, at the delete's own journal op, with the delivery after it (§7.8 below); the sentence is kept so the ruling it recorded is legible.** `RCA-8(g)` ANNOTATE-BESIDE: **every other byte of this paragraph stands — the declared window is still the LISTENER DELIVERY, which still runs no pass, the `discarded` witness still reports what that delivery discarded and the reversal's effect still stands, and the third case the consumer measured (a `delete` performed INSIDE a pass) is still judged by that pass.** **WHAT MOVED IS ONLY THE PREMISE: `D-1-bis` (`a delete consults no constraint`) is superseded by `D-55` item `3` (`A delete is an editing verb and must pass constraints`) for the delete's OWN act; it remains the ground of the DELIVERY window, which runs after that act's pass has closed.** The original bytes read: A `delete` runs **no constraint pass of its own** (`D-1-bis`); the window declared above is its LISTENER DELIVERY, which runs no pass and is UNCHANGED — the `discarded` witness still reports the discard and the reversal's effect still stands. **Where a `delete` is performed INSIDE a pass — the case the consumer measured — the delete's window IS that pass's window**, and a reversal raised by the listener its delivery fires is a DIFFERENT ACT AT A DIFFERENT INSTANT from the declared window: **a THIRD case, and it is now judged** — the request lands in the pass's window and that pass's terminal check asks the pairs at the state the reversal left. **A registration's own evaluation remains the second passless window, silent and identity-only** (`D-49`), and both exits below still leave it honestly.
|
|
1340
1544
|
|
|
1341
1545
|
**⟶ ADDED 2026-10-28 — `D-49`: THE DISCARD IS NOW OBSERVABLE ON THE `delete`'S OWN ANSWER, AND THE OTHER
|
|
1342
1546
|
WINDOW STILL CARRIES NOTHING. READ THIS BEFORE YOU RELY ON EITHER FAIL-STATE.**
|
|
@@ -1365,6 +1569,9 @@ window throws away**: if it fails and you raise a `rollback`, **the request dies
|
|
|
1365
1569
|
**THE EXITS ARE STILL YOURS AND ARE UNCHANGED: reload the file, or regenerate the store.** What changed is that
|
|
1366
1570
|
**the `delete` now tells you the discard happened** — *"a fail-state is honest only if the consumer learns it
|
|
1367
1571
|
happened"* (`D-49`).
|
|
1572
|
+
**⟶ AND AN ENTRY IS NEVER SHIPPED WITH AN EMPTY ADDRESS LIST, 2026-10-28 — `D-55` ITEM 2 · `DEF-AH`, MEASURED.** `RCA-8(g)` ANNOTATE-BESIDE: **every bullet above stands BYTE-FOR-BYTE and is read THROUGH this clause; the member's name, its non-enumerability, its `{ opId, addresses }` shape and the two limits stay exactly as they were.** **THE RULING, THE ARCHITECT, VERBATIM: *"A witness address of `[]` corresponds to the register itself. This is not a legal response for any other watched address."*** **SO THE TWO EMPTY THINGS ARE NOT THE SAME THING, AND THE SHIPPED `1.3.0` CONFLATED THEM: an ADDRESS of `[]` is THE REGISTER (the root) and is lawful; an entry whose address LIST is `[]` is never lawful.** **WHAT `1.3.0` DID, MEASURED: a delivery whose reversal moved NOTHING — a nested write, then a reversal of an op the watched address never held — reported `addresses: []`**, a list with no address in it. **WHAT YOU GET NOW:** **`record.discarded` is `[]` where nothing moved** — *if nothing moved there is no discard to report, so the entry is ABSENT and not empty* — **and a reversal that moves the ROOT reports `addresses: [[]]`**: one element, whose segment list is empty. **THE TWO READINGS BESIDE EACH OTHER, so a consumer can tell them apart: `[]` is "nothing was discarded"; `[[]]` is "the register itself is what moved".** **NO OTHER MEMBER MOVES:** `opId` is still the id the reversal named, the list is still `0` or `1` long, and nothing is minted into the status set. (`tests/e2e/discard-witness.mjs`'s `DW-10.1`/`DW-10.2` measure both arms; the empty-list arm is RED under a revert of the one line.)
|
|
1573
|
+
|
|
1574
|
+
**⟶ AND YOUR `delete`'S OWN PASS IS STILL A MEASUREMENT AND NOT A BEHAVIOUR, 2026-10-28 — `D-55` ITEM 3 · `DEF-X`.** `RCA-8(g)` ANNOTATE-BESIDE: **every clause of this section and of §12.6 above stands BYTE-FOR-BYTE; this clause answers the question a consumer asked about `1.2.0` by measuring `1.3.0`.** **THE RULING, THE ARCHITECT, VERBATIM: *"A delete is an editing verb and must pass constraints. This includes all of the relevant commit and message logic."*** **SO IT MUST — AND NO SHAPE AT THIS VERSION ADMITS ONE, WHICH IS WHAT THE MEASUREMENT SAYS RATHER THAN WHAT AN INTENTION SAYS.** **A `delete`'s path, verb by verb:** the entry's `delete` opens a commit window of its own (`openCommits` is `1` inside its delivery), calls the addon's `delete` entry, builds the record `{address, tiers, triggered, op}`, delivers the triggering set, reads the witness and closes the window. **IT NEVER CALLS THE KERNEL**: no `K-2` opens an attempt, no `K-3` runs a pass, no message log is minted and no terminal entry is appended — so the pair standing at the deleted address is asked exactly as often as its registration asked it, and NOT ONCE FOR THE DELETE. **AND THE ONE PASS SHAPE THE ARTEFACT HAS CANNOT STAND IN**: `K-2` **LANDS A WRITE OF ITS OWN BEFORE `K-3` JUDGES**, so a pass driven at the deleted address judges a state the delete is not in (measured: the store after it holds the value `K-2` wrote). **WHAT WOULD HAVE TO BE BUILT, SAID PLAINLY AND NOT BUILT HERE:** **a delete would have to be GIVEN a commit and an attempt of its own** — a `K-2`-shaped opening that takes the delete's own op instead of a write's, a commit list minted for it, a pass over the pairs standing at the deleted address against the state the delete leaves, a terminal arm that appends that log's `outcome` entry, and the delivery moved AFTER the pass so its listeners see a judged state — **and the current `delete` verb has none of those.** **A delete performed INSIDE a pass IS judged**, and that is the third case §12.6 above declares — but that is the pass's check, not the delete's. (`tests/e2e/discard-witness.mjs`'s `DW-10.8` prints the path, the counts and the pass shape it drove.)
|
|
1368
1575
|
|
|
1369
1576
|
### §12.7 A skip is not a green, and your own throws are not refusals
|
|
1370
1577
|
|
package/README.md
CHANGED
|
@@ -29,6 +29,10 @@ package's repository at
|
|
|
29
29
|
`https://github.com/LittleKingsguard/Provident-Storeroom/blob/main/docs/guides/package-use.md`, which is where
|
|
30
30
|
an edit belongs.
|
|
31
31
|
|
|
32
|
+
**⟶ AND THE ONE FIGURE THIS PAGE CAN BE RIGHT ABOUT IS GENERATED AND ASSERTED, 2026-10-28 — `DEF-AC`'s third part, and `D-57`'s one surviving figure.** `RCA-8(g)` ANNOTATE-BESIDE: **every clause above stands BYTE-FOR-BYTE and is read THROUGH this one; nothing above moves.** **THE DEFECT THIS CLOSES: THIS PAGE DECLARED NO VERSION AT ALL** — a shipped document a consumer reads first, saying nothing about which release it describes — **so `scripts/publish.sh --figures` now GENERATES the line below from `bindings/js/package.json`'s `version` and REFUSES when it disagrees, exactly as it does at the guide's and the change list's own marked lines: three shipped documents, three version lines, one generator, one assertion.** **AND THE CUT-STATE IS NOT STATED HERE, OR ANYWHERE ELSE IN THIS PACKAGE'S DOCUMENTS** (`D-57`, the architect verbatim: *"Drop the cut-state"*): a document is sealed before the tag that would make its cut-state true, so **the cut-state is not a document's to state**; the record of what has been cut is the change list's, and it is past tense. **EVERY OTHER CLAIM OF THIS PAGE STANDS EXACTLY AS IT STOOD.**
|
|
33
|
+
|
|
34
|
+
PUBLISH-README-VERSION-LINE: THE VERSION THIS README DESCRIBES IS `1.4.0`.
|
|
35
|
+
|
|
32
36
|
## The one entry
|
|
33
37
|
|
|
34
38
|
```js
|
|
@@ -54,7 +58,7 @@ reached INTERNALLY by the package's own resolution step and is not an entry poin
|
|
|
54
58
|
| `read(address, tier?)` | a leaf's literal, a branch's assembled value, or the empty address's whole register |
|
|
55
59
|
| `readLiteral(address, tier?)` | the same, narrowed to a leaf (`ReadTargetIsBranch` on a branch) |
|
|
56
60
|
| `write(address, tier, value, commit?)` | `{op, watched, triggered}` — **four positions, `commit` the fourth and optional** |
|
|
57
|
-
| `delete(address, tier?)` | `{address, tiers, triggered}` |
|
|
61
|
+
| `delete(address, tier?)` | `{address, op, tiers, triggered}`, plus a non-enumerable `discarded` witness |
|
|
58
62
|
| `rollback(opId)` | `{opId, undoneOps, restoredTo}` |
|
|
59
63
|
| `listen(handle, addresses)` | **the handle it watches under** (`Handle`) — **you spell it**, and position `2` is a SET |
|
|
60
64
|
| `unlisten(handle)` | `undefined` — the withdrawal: the store-side entry and this package's registry entry |
|
|
@@ -115,6 +119,17 @@ records, with the `outcome` entry's `state` carrying the `KC` token where the co
|
|
|
115
119
|
(`index.d.ts`, `LogEntry`; `surface.md §2.5` items 3–4). **The `message` is generated from the code and the
|
|
116
120
|
failing trigger's arguments**, never hand-authored at a call site.
|
|
117
121
|
|
|
122
|
+
**★ AND TWO THINGS ABOUT THAT LIST THAT A CONSUMER COUNTING IT NEEDS — DECLARED HERE BECAUSE THE SURFACE DOES NOT
|
|
123
|
+
TELL YOU EITHER BY ITSELF.** **ONE: THE LIST BELONGS TO THE COMMIT, NOT TO THE ATTEMPT.** One write is one commit,
|
|
124
|
+
and everything the commit does — the passes that judged its value, a repair's own write, the re-run after that
|
|
125
|
+
repair, the terminal arm's `outcome` entry — appends to **one** array, **so a crossed report carries the entries of
|
|
126
|
+
EVERY attempt in the commit, including a write a constraint body makes** (a listener's write opens a commit of its
|
|
127
|
+
own, so its entries are that new commit's). **`log.length` is therefore every entry of the commit and not "how many
|
|
128
|
+
constraints were evaluated".** **TWO: AN ENTRY'S `seq` IS THE POSITION OF THE ENTRY IN THAT LIST — NOT THE STORE'S
|
|
129
|
+
JOURNAL ORDINAL.** `JournalOp.seq` orders the store's own record; this `seq` orders the commit's message log, and
|
|
130
|
+
the two differ by design (a refusal's entry can carry `seq` `1` while the store's journal counter reads `41`). **The
|
|
131
|
+
kernel's `state` token set is `KC-1`…`KC-9`, `KC-9` being the save arm's own; the store's status set has not moved.**
|
|
132
|
+
|
|
118
133
|
## The status set is TWENTY, and open by design
|
|
119
134
|
|
|
120
135
|
**`StoreStatus` carries TWENTY members, ranked `1`…`20`, plus the open escape arm `(string & {})`** — the union
|
|
@@ -178,8 +193,9 @@ is REJECTED in both directions** — a listener and a constraint do not share on
|
|
|
178
193
|
constraint arm is declared, and none is owed.
|
|
179
194
|
|
|
180
195
|
**What a refusal costs you:** a write whose registered constraint refuses it **throws
|
|
181
|
-
`{status: 'ConstraintPassRefused', message, log}`** — rank `20` — with the failure's `log` carrying the
|
|
182
|
-
|
|
196
|
+
`{status: 'ConstraintPassRefused', message, log}`** — rank `20` — with the failure's `log` carrying the entries of
|
|
197
|
+
the write's whole commit (the attempt's own among them, and every attempt the commit held) and the kernel's `KC`
|
|
198
|
+
token on the `outcome` entry's `state`. **The value is not written
|
|
183
199
|
and a failed attempt leaves the store exactly as it found it**, whether the constraint refused outright or its
|
|
184
200
|
repair failed on the re-run (`D-40`).
|
|
185
201
|
|
|
@@ -187,9 +203,14 @@ repair failed on the re-run (`D-40`).
|
|
|
187
203
|
|
|
188
204
|
**Durability is produced by a successful `file`-TIER WRITE ONLY** (`DD-28`, `D-32`). **So a BARE `file`-tier
|
|
189
205
|
write persists**: `store.write(['k'], 'file', 1)` commits with no kernel attempt in the path, and a fresh
|
|
190
|
-
`storeCreate` over the same `filePath` resolves the address.
|
|
191
|
-
at any tier scope
|
|
192
|
-
|
|
206
|
+
`storeCreate` over the same `filePath` resolves the address. **⟶ A `DELETE` NOW SAVES AND A `ROLLBACK` STILL DOES NOT, 2026-10-28.** The sentence this replaces said **both**
|
|
207
|
+
performed no file I/O at any tier scope — **and that was measured false for a `delete` once it became an editing
|
|
208
|
+
verb with its own attempt**: a `file`-tier delete's save is the terminal arm's at the record's own op, so
|
|
209
|
+
**`delete(['k'], 'file')` followed by a fresh `storeCreate` over the same `filePath` answers `AddressNotFound`
|
|
210
|
+
rather than the version it removed** (measured `{"a":1}` → `{}`, boot `1` → absent). **A `ROLLBACK` still performs no
|
|
211
|
+
file I/O at any tier scope** — **a reversal appends no journal op (`M-9`), so its terminal arm has no op of its own
|
|
212
|
+
to save at** — **and a durable version a reversal removed in memory therefore stands again after a re-boot, which is
|
|
213
|
+
the store's declared boot and not a defect.**
|
|
193
214
|
|
|
194
215
|
**A kernel-driven write's `file`-tier save is a DIFFERENT save of a different scope**: it is the package's
|
|
195
216
|
terminal arm, performed on the passing arm alone, before any delivered listener body runs, keyed by the
|
|
@@ -268,8 +289,12 @@ empirically by `delivery` together with the e2e/install instruments. **No declar
|
|
|
268
289
|
exclusivity binds the allowlist** — a hand-maintained count in its place would re-create the constraint those
|
|
269
290
|
rulings lifted. **ONE DOCUMENTATION ROW IS ADDITIVE AND DECLARED AS SUCH:** `README.guide.md` names no module and
|
|
270
291
|
imports nothing, so it travels in the reachability rule's own declared non-code class (`README*` at the package
|
|
271
|
-
root) and no limb of that rule moves for it. **
|
|
272
|
-
|
|
292
|
+
root) and no limb of that rule moves for it. **AND A SECOND: `RELEASE-NOTES.md` is the change list, staged
|
|
293
|
+
byte-for-byte at the packaging root by the release's assembly, names no module and imports nothing, and is likewise
|
|
294
|
+
admitted and declared** — so the manifest's `files` allowlist reads **seven rows** and the packed set reads **`20`
|
|
295
|
+
members bare / `21` assembled** (`1` manifest + `5` root files + `13` entry modules + `1` kernel `= 20`, plus the
|
|
296
|
+
licence at assembly). **The prohibitions are unmoved:** no `.node`, no `build/Release` path and no `postinstall`
|
|
297
|
+
route in this package's own tarball, and no `LICENSE` row.
|
|
273
298
|
|
|
274
299
|
## Tests — a REPOSITORY instruction, not a consumer one
|
|
275
300
|
|
|
@@ -279,12 +304,12 @@ nothing by them. **A consumer's own tests are the consumer's.**
|
|
|
279
304
|
|
|
280
305
|
```sh
|
|
281
306
|
cargo test --manifest-path tests/Cargo.toml -p storeroom-tests --no-fail-fast # THE BATTERY'S LEG — 235 cells, 0 fail
|
|
282
|
-
cargo test --offline -p storeroom-native # THE STORE'S LEG —
|
|
283
|
-
cargo test # BOTH —
|
|
307
|
+
cargo test --offline -p storeroom-native # THE STORE'S LEG — 434 cells, 0 fail
|
|
308
|
+
cargo test # BOTH — 669 cells, 0 fail
|
|
284
309
|
```
|
|
285
310
|
|
|
286
311
|
**AND THE THIRD COMMAND IS NOT A FOURTH LEG:** a bare `cargo test` runs `default-members = [".", "tests"]`, so
|
|
287
|
-
it is EXACTLY the store's `
|
|
312
|
+
it is EXACTLY the store's `434` plus the battery's `235` — `434 + 235 = 669` ✓ — in one run (`D-34`: one
|
|
288
313
|
workspace at the repository root, `members = [".", "bindings/js", "tests"]`). **`-p` IS NOT DECORATION ON THE
|
|
289
314
|
BUILD SIDE EITHER:** the store's leg keeps `--offline`, and a workspace-root `cargo build --offline` would pull
|
|
290
315
|
this crate's `napi` crates and `--offline` would refuse them.
|