provident-storeroom 1.2.0 → 1.3.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.
@@ -0,0 +1,584 @@
1
+ # Release notes — `provident-storeroom` `1.1.0`
2
+
3
+ **⟶ THE FILE'S OPERATIVE READING, 2026-10-28 — THE HEAD IS CORRECTED HERE, AND THE DEFECT WAS ONE LEVEL IN: A DOCUMENT
4
+ DENYING THE STATE IT DESCRIBED.** `RCA-8(g)` ANNOTATE-BESIDE: **the title line above and every dated paragraph below
5
+ stand BYTE-FOR-BYTE as the reading of their own date; THIS clause and the `§MAIN` section beneath it are the operative
6
+ readings of the two things the head got wrong.** **(i) `1.2.0` IS NOT UNRELEASED.** `git tag --list` answers `v1.0.0` ·
7
+ `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"`
8
+ at its own bytes; and the registry carries `1.2.0`. **So the section that stood under *"UNRELEASED"* is the change list
9
+ of a CUT release, its *"no `v1.2.0` tag exists"* sentence and its *"what is installed today is still `1.1.0`"* sentence
10
+ are the reading of the date they were written, and its heading is corrected at its own line** — the withdrawn bytes are
11
+ recorded there, because a heading that denies its own tag is exactly the class of defect this project calls `DEF-AC`.
12
+ **(ii) THE PUBLISHED FIGURE IS `1.2.0`, NOT `1.1.0`, AND THE TREE READ HERE IS NINE COMMITS PAST THAT RELEASE WITH NO
13
+ TAG ON IT.** **So this file now carries a section for the tree that is not cut (`§MAIN`, prepared and marked so) beside
14
+ the section for the release that is (`§1.2.0`).** **No version is bumped, no manifest moves, and nothing in `§MAIN`
15
+ claims to be installable.**
16
+
17
+ **THE ONE THING THIS DOCUMENT IS FOR: `1.1.0` IS A MINOR, AND THIS IS WHERE ITS BREAKING CHANGES ARE
18
+ DECLARED.** `1.0.1` was published as a PATCH and it carried behaviour changes a patch may not carry. The
19
+ architect ruled the remedy a MINOR — *"the next release is `1.1.0`"* (`strata/decisions.md` · `D-42` item `1`)
20
+ — so **`1.0.1` stands as published with a disclosed breach, and `1.1.0` carries the declaration.** This file
21
+ is that declaration. **It is not a changelog of everything that happened; it is the list of things that can
22
+ break you, each with what it did before and what it does now.**
23
+
24
+ **WHERE IT BELONGS, AND WHY IT IS AT THE REPOSITORY ROOT.** This note is at the packaging root's PARENT, not
25
+ inside `bindings/js/`: the main manifest's `files` allowlist does not name it and must not, so **it ships in NO
26
+ tarball.** It addresses a consumer deciding whether to move off `1.0.1`, so it must be readable BEFORE an
27
+ install and must cover the six packages at once — the main package and the five platform packages share one
28
+ version and one decision.
29
+
30
+ **⟶ AND THE PARAGRAPH ABOVE IS SUPERSEDED BY MEASUREMENT, 2026-10-28 — `DEF-AC`, AND THIS FILE NOW SHIPS.** `RCA-8(g)`
31
+ ANNOTATE-BESIDE: **the paragraph above stands BYTE-FOR-BYTE and is the reading of its own date (the `1.1.0` cut, when
32
+ it did ship nowhere); it is read THROUGH this clause.** **THE DEFECT THE DOWNSTREAM FILED: the ONE behaviour change
33
+ `1.2.0` makes — a refused reversal's restore to the commit's OPENING mark — was declared in THIS FILE and in no
34
+ shipped document, so a consumer installing `1.2.0` received a behaviour change with no shipped declaration of it.**
35
+ **SO THE FILE IS LANDED THE WAY THE GUIDE IS LANDED**: `RELEASE-NOTES.md` at the repository root is the ONE authored
36
+ home, the release's assembly stages it **BYTE-FOR-BYTE** at the packaging root as `bindings/js/RELEASE-NOTES.md`,
37
+ and the main manifest's `files` allowlist **names it** — one additive documentation row beside `README.guide.md`,
38
+ staged by the same assembly step and admitted by the reachability rule's own declared non-code class. **A BARE
39
+ `npm pack` STILL WORKS** because the landed copy is a TRACKED, GENERATED file like `README.guide.md`: it is present
40
+ whether or not the assembly has run. **AND WHAT SHIPS IS THE CHANGE LIST, at `node_modules/provident-storeroom/
41
+ RELEASE-NOTES.md`.**
42
+
43
+ **THE VERSION THIS RELEASE DECLARES IS `1.1.0` AT ELEVEN SITES, MOVING TOGETHER** — the main manifest's
44
+ `version`, its FIVE exact `optionalDependencies` pins, and the FIVE `platforms/*/package.json` versions. **The
45
+ artefact is stamped from the manifest by `bindings/js/build.rs`, so `version()` answers `1.1.0`** and that is
46
+ the value to assert against, not the crate's own `Cargo.toml` version.
47
+
48
+ ---
49
+
50
+ ## §1.3.0 · **PREPARED — NOT CUT** — what `1.3.0` carries, ten commits past `v1.2.0`
51
+
52
+ **★ THIS SECTION DESCRIBES A TREE NOTHING CAN INSTALL, AND IT IS MARKED BEFORE IT SAYS ANYTHING ELSE, BECAUSE A MARK
53
+ ADDED AFTERWARDS IS THE DEFECT THIS FILE JUST CORRECTED ONE SECTION DOWN.** **THE PUBLISHED RELEASE IS `1.2.0`**: `git
54
+ tag --list` answers `v1.0.0` · `v1.0.1` · `v1.1.0` · `v1.2.0`, the tag `v1.2.0` names a tree whose
55
+ `bindings/js/package.json` reads `"version": "1.2.0"` at its own bytes, and the registry carries that release. **THE TREE
56
+ THIS SECTION IS DERIVED FROM IS NINE COMMITS PAST IT** — `git describe` reads `v1.2.0-9-g989c216`, `git rev-list --count
57
+ v1.2.0..HEAD` reads `9`, and **no version bump is in any of those nine commits: the manifest still reads `1.2.0` at
58
+ eleven sites and no tag names this tree.** **SO THE HONEST READING IS THIS: A CONSUMER INSTALLING `1.2.0` GETS `1.2.0`'S
59
+ BYTES AND `1.2.0`'S CHANGE LIST (the section below); WHAT THIS SECTION DECLARES IS PREPARED AND NOT CUT, and a prepared
60
+ change is not a shipped one.** **No consumer can install any of it yet, and every figure this section prints is a
61
+ figure of the TREE and not of a tarball.**
62
+
63
+ **⟶ AND THE VERSION THIS SECTION DECLARES IS `1.3.0` AT ELEVEN SITES, PREPARED AND NOT CUT, 2026-10-28.** `RCA-8(g)` ANNOTATE-BESIDE: **the ★ paragraph above stands BYTE-FOR-BYTE as the reading of its own date and is read THROUGH this clause; the two sentences of it that the bump supersedes are named here rather than overwritten.** **THE MANIFEST AT ITS OWN BYTES THIS DATE: `bindings/js/package.json`'s `version` is `"1.3.0"`, all five `optionalDependencies` pins are `1.3.0` with it, and the five `bindings/js/platforms/*/package.json` `version` fields read `1.3.0` with them — ELEVEN SITES, ONE REBUILD — and the rebuilt artefact is stamped from that manifest, so `version()` answers `1.3.0`.** **SO *"no version bump is in any of those nine commits: the manifest still reads `1.2.0` at eleven sites"* IS THE READING OF ITS OWN DATE, AND THE BUMP IS THIS PREPARATION'S (a working-tree change, not a commit): the tree is `10` commits past `v1.2.0` — `git describe` reads `v1.2.0-10-g14787b5` and `git rev-list --count v1.2.0..HEAD` reads `10` — and the eleven sites read `1.3.0`.** **AND WHAT THIS SECTION IS AND IS NOT, SAID PLAINLY BECAUSE THE EASIER THING TO GET WRONG IS THAT A BUMP IS A RELEASE: `1.3.0` IS THE PREPARED VERSION — at the eleven sites and in the rebuilt artefact — and it is NOT A RELEASE. `git tag --list` answers `v1.0.0` · `v1.0.1` · `v1.1.0` · `v1.2.0` and no `v1.3.0`, the registry carries no `1.3.0`, and no consumer can install any of this until the architect cuts the tag.** **The published release remains `1.2.0`, and the section below remains its change list, unchanged and still marked CUT AND PUBLISHED.** (The shipped guide's two generated figures — `scripts/publish.sh --figures` — agree with this clause: the version line reads `1.3.0` and the cut-state sentence reads *"`v1.3.0` DOES NOT EXIST, SO THE RELEASE THIS GUIDE DESCRIBES IS NOT CUT"*.)
64
+
65
+ **IT DECLARES TWO BEHAVIOUR CHANGES A CONSUMER WILL FEEL, AND THEN FOUR SMALLER ITEMS. THE TWO COME FIRST BECAUSE THEY
66
+ ARE THE CONTENT.**
67
+
68
+ **1 · ★ THE MESSAGE LOG BELONGS TO THE COMMIT NOW — SO A CROSSED REPORT CARRIES MORE ENTRIES THAN `1.2.0`'S DID.**
69
+ **`AT `1.2.0`:** the `log` was the **ATTEMPT's** own list — the constraint-judged write inside a commit carried the
70
+ entries its own pass appended, and where an attempt was **nested** inside another (a write a constraint body makes, a
71
+ repair's own write) the enclosing report carried the **enclosing** attempt's list: the nested refusal's entries were not
72
+ in it, and on the arm the downstream filed (`DEF-AK`) the inner refusal's own entry was **REPLACED** rather than joined.
73
+ **NOW:** the list belongs to the **COMMIT** — it is taken once per commit off the kernel's frame stack, defined on the
74
+ `CommitRecord` under a symbol, and **every attempt in that commit holds a reference to it** — so **a crossed report
75
+ carries the entries of EVERY attempt in the commit**, including a nested write made by a constraint body, whose refusal
76
+ now shows its own `constraint-check` and `outcome` entries verbatim before the enclosing `outcome` entry. **A CONSUMER
77
+ DIFFING `1.2.0` AGAINST THIS RELEASE SEES A REPORT CARRY MORE ENTRIES** — and that is the whole change: what is refused,
78
+ what is restored, and which status crosses have not moved with it. **The one declared reach that is still a new list: a
79
+ listener's write opens a commit of its own** (*"the triggering commit already passed constraints"*), so its entries are
80
+ not in the triggering commit's list; and a pair body asked at a reversal's judgement names no commit, so a write that
81
+ body makes opens one.
82
+ **AND THE ENTRY'S `seq` IS THE POSITION IN THAT LIST — NOT THE STORE'S JOURNAL ORDINAL.** **`1.2.0`'s declaration said
83
+ *"the ordinal of the list it stands in"* and that sentence was read as the journal's** (`DEF-AI`, measured): a refusal's
84
+ entry carried `seq` `1` while the store's own journal counter read `41`. **They are two ordinals with two homes:
85
+ `JournalOp.seq` is the store's append ordinal that orders the store's record, and this `seq` orders the COMMIT's message
86
+ log and nothing else; neither equals the other by design.** **One repair write appends five entries in one list at `seq`
87
+ `1..5`, strictly increasing in array order** — the pass's `constraint-check`, the repair's `repair-run`, the repair's
88
+ `commit`, the re-run's `constraint-check`, and the terminal `commit`/`outcome` — **and no entry is ever minted from the
89
+ store's journal counter.** **Say it to yourself before you count: `log.length` is every entry of the COMMIT, not "how
90
+ many constraints were evaluated" and not "how many attempts this write made".**
91
+
92
+ **2 · ★ A REVERSAL RAISED INSIDE A PASS IS NOW JUDGED — THREE HOSTS CHANGED FROM "A PASS RAN AND THE REVERSAL'S EFFECT
93
+ STOOD" TO "REFUSED AND RESTORED".** **`AT `1.2.0`:** a reversal raised where a pass was running could be **left unjudged
94
+ with its effect standing**, and the shipped guide said so falsely: *"And every pass that DOES run drains its own restart
95
+ request — so the fail-state is bounded by the two acts that run none."* **That sentence is WITHDRAWN AS FALSE** (`D-54`;
96
+ measured by the downstream): **a pass no longer consumes a request it did not raise, and the request is carried to the
97
+ pass's own terminal state.** **The rule, quoted from the shipped guide:** ***"no pass consumes a restart request it did
98
+ not raise, and a request raised inside a pass is carried to that pass's own terminal state and judged there — once,
99
+ never per step."*** **THE THREE HOSTS, EACH MEASURED, BEFORE → AFTER:**
100
+ - **A REPAIR BODY'S REVERSAL** — the judge was asked the pairs `[9,7]` and never `[5]`, with `0` judge invocations and the
101
+ refused `5` standing; **now the pairs asked are `[9,7,5]`, the invocation count is `1`, the write crosses
102
+ `ConstraintPassRefused`, and the store reads `9`** — the refused value does not stand.
103
+ - **A REVERSAL INSIDE A `delete`'S CONSTRAINT PASS** — the same `[9,7]`/`0`/standing-`5` reading, and **now the same
104
+ `[9,7,5]`/`1`/`9`**, with the delete's own `discarded` witness reading `[]` because nothing was discarded.
105
+ - **A REENTRANT REVERSAL-THEN-WRITE** — the write used to answer with the reversal's effect standing at `5`; **now it
106
+ REFUSES and the store reads `9`.**
107
+ **AND THE TWO DECLARED PASSLESS WINDOWS STILL RUN NO PASS, WITH THEIR EXITS UNCHANGED: a `delete`'s LISTENER DELIVERY
108
+ (a listener invoked by a delete's delivery runs no constraint pass, so a reversal raised inside it is unjudged and its
109
+ effect stands, and the `discarded` witness reports the discard) and a REGISTRATION'S OWN EVALUATION (`addConstraint`'s
110
+ and `constraintRegister`'s evaluation of a supplied body runs no pass, so an action raised there is unjudged and its
111
+ effect stands).** **Their exits are the two the guide declares and they are unchanged: RELOAD THE FILE — a restore that
112
+ moved a `file`-tier version saves the restored image, so a fresh boot over that path returns to judged bytes — or
113
+ REGENERATE THE STORE, replaying your writes.** **A reversal raised by the listener a `delete`'s delivery fires INSIDE a
114
+ pass is a THIRD case and not the declared window: that pass's terminal check now asks the pairs at the state the
115
+ reversal left.** `D-42`, `D-38`'s once-per-terminal-state rule, the restart budget (`3`), its spend-and-never-reset rule
116
+ and its failing-closed arm are **untouched**; what changed is where the request is carried and who may consume it.
117
+
118
+ **3 · THE RESTORE NO LONGER REPORTS A SUCCEEDED WHOLE-COMMIT UNDO AS A FAILURE (`DEF-AJ`).** **`AT `1.2.0`:** a message
119
+ could say *"0 of the 1 address it moved stand again"* when **every** address stood: the restore verified each moved
120
+ address against the watch's **pre-reversal capture** while the store returned it to the **journal mark the commit cached
121
+ at its OPENING**, and on the restart arm the commit had already landed `7` before the reversal ran, so the mark's `9`
122
+ did not equal the capture's `7` and a complete whole-commit undo read as a failure. **NOW:** a successful restore counts
123
+ every measured address restored to the mark, a standing **past** the capture is recorded as a `wholeCommit` and spoken as
124
+ such with the baseline named, and **only a refused mark, a cacheless window or an unrestored address say NOT undone.**
125
+ The message's meaning is the thing that moved; nothing about what a refusal restores moved with it.
126
+
127
+ **4 · THE `discarded` WITNESS NAMES WHAT MOVED, NOT THE WHOLE WATCHED REGISTRY (`DEF-AM`).** A delete's `discarded`
128
+ carries `addresses` — the addresses the discarded check would have been asked at — and **those are now the addresses the
129
+ reversal actually MOVED, measured once against the standing the store holds, and not every address the registry
130
+ watches.** **Measured: two pairs in the registry, one moved, and the witness names one.** **The witness is still a
131
+ WITNESS and not a repair** — the delete answers what it answered, throws nothing, and the reversal's effect still
132
+ stands in that declared window — and it is still **non-enumerable** (`Object.keys` never shows it; a spread copy loses
133
+ it).
134
+
135
+ **5 · THE CHANGE LIST ITSELF NOW SHIPS IN THE TARBALL (`DEF-AC` (a)).** **`RELEASE-NOTES.md` is the SEVENTH row of the
136
+ main manifest's `files` allowlist**, staged **byte-for-byte** from this repository-root home to the packaging root as
137
+ `bindings/js/RELEASE-NOTES.md` by the release's assembly — the same mechanism that lands `README.guide.md` and the
138
+ `LICENSE` copy — and the preflight REFUSES a drifted landing. **So a consumer installing the next release has this
139
+ change list at `node_modules/provident-storeroom/RELEASE-NOTES.md`**, and the packed member set reads **`20` bare / `21`
140
+ assembled** (`1` manifest + `5` root files + `13` entry modules + `1` kernel = `20`, plus the licence at assembly).
141
+ **This is the half of `DEF-AC` that is already cut in `1.2.0` and it stays cut in this tree.**
142
+
143
+ **6 · THE PREFLIGHT GENERATES AND ASSERTS THE SHIPPED GUIDE'S VERSION LINE AND CUT-STATE (`DEF-AC` (b)).** **The
144
+ guide's version line and cut-state sentence used to be HAND-WRITTEN, which is how the guide came to deny the release it
145
+ travelled in.** **Now `scripts/publish.sh` GENERATES both from `bindings/js/package.json`'s `version` and `git tag
146
+ --list`, and its preflight REFUSES when the shipped guide's two marked lines disagree** — a hand-written *"IS NOT CUT"*
147
+ beside a generated *"IS CUT"* fails, and the generated line passes; `--figures` runs that one check read-only. **A
148
+ hand-written line that contradicts the manifest can no longer travel.**
149
+
150
+ **NOTHING IN THIS SECTION IS CUT, NO TAG NAMES IT, NO VERSION BUMP IS PROPOSED, AND THE ELEVEN VERSION SITES STAY AT
151
+ `1.2.0`.** **The two behaviour changes are declared here because a consumer must be able to read them BEFORE the cut and
152
+ must not be told they are in a tarball that does not carry them** — the same honesty the corrected head above owes.
153
+
154
+ **⟶ AND WHAT THE PARAGRAPH ABOVE SAYS ABOUT THE FIGURES IS SUPERSEDED BY THIS PREPARATION, 2026-10-28 — WHAT IT SAYS ABOUT THE CUT IS NOT.** `RCA-8(g)` ANNOTATE-BESIDE: **the paragraph above stands BYTE-FOR-BYTE as the reading of its own date and is read THROUGH this clause.** **UNMOVED: *"NOTHING IN THIS SECTION IS CUT, NO TAG NAMES IT"* — `git tag --list` carries no `v1.3.0`, the registry carries no `1.3.0`, and a consumer cannot install any of this.** **SUPERSEDED: *"NO VERSION BUMP IS PROPOSED, AND THE ELEVEN VERSION SITES STAY AT `1.2.0`"* — the bump IS this preparation's, and the eleven sites read `1.3.0`** (the clause at this section's head states the manifest, the pins, the five platform manifests and the rebuilt artefact's `version()`). **The two behaviour changes are unchanged, and they are `1.3.0`'s content: what a consumer installing `1.2.0` gets is `1.2.0`'s bytes, and this section is the change list of the version being prepared, not of a released one.**
155
+
156
+ ---
157
+
158
+ ## §1.2.0 · **CUT AND PUBLISHED — tag `v1.2.0`** — what that release adds, derived from the tree
159
+
160
+ **⟶ THE HEADING THAT STOOD HERE WAS *"`## §1.2.0 · **UNRELEASED** — what the release in preparation adds, derived from
161
+ the tree`"*, AND IT IS CORRECTED RATHER THAN ANNOTATED AROUND, 2026-10-28.** `RCA-8(g)` ANNOTATE-BESIDE: **the withdrawn
162
+ bytes are quoted above and the section's body below stands BYTE-FOR-BYTE as the reading of its own date; every reading
163
+ in it that says the release is not cut is read THROUGH the head clause and the clause of 2026-10-28 at the end of this
164
+ section's opening paragraphs.** **THE DEFECT: a heading denying the release it describes is the `DEF-AC` class one level
165
+ in, and a reader opening this file met *"UNRELEASED"* over a version the registry carries.** **The tag is cut — `git tag
166
+ --list` answers `v1.0.0` · `v1.0.1` · `v1.1.0` · `v1.2.0` — and the release this section describes is published.**
167
+
168
+ **★ THIS SECTION DESCRIBES A RELEASE THAT HAS NOT BEEN CUT, AND IT IS MARKED SO IT CAN NEVER BE MISTAKEN FOR A
169
+ DECLARATION OF SHIPPED BYTES.** `bindings/js/package.json`'s `version` reads **`1.2.0`** at its own bytes this
170
+ date, all five `optionalDependencies` pins read `1.2.0` with it, the five platform manifests' `version` fields read
171
+ `1.2.0` with them — **eleven sites, one rebuild, and the artefact the manifest stamps answers `version()` =
172
+ `1.2.0`** — **and no `v1.2.0` tag exists: `git tag --list` answers `v1.0.0` · `v1.0.1` · `v1.1.0` and nothing else.**
173
+ **SO THE HONEST READING IS THIS: THE MANIFEST IS PREPARED AT `1.2.0` AND THE RELEASE IS NOT CUT — a prepared version
174
+ is not a released one, the tag is not cut until the architect cuts it, and no consumer can install any of this
175
+ yet.** **What `1.1.0` describes is still what is cut and published; what this section describes is the preparation,
176
+ derived from the tree on 2026-10-28, and the figure a reader checks against the registry today is still `1.1.0`.**
177
+
178
+ **AND WHAT SHIPS NOWHERE, SAID FIRST BECAUSE IT IS THE EASIEST THING TO GET WRONG: THIS FILE IS AT THE
179
+ REPOSITORY ROOT AND NO TARBALL CARRIES IT.** The main manifest's `files` allowlist names six rows — `index.mjs` ·
180
+ `README.md` · `README.guide.md` · `index.d.ts` · `src/entry/` · `src/constraint-kernel.mjs` — **and this document
181
+ is not one of them and must not be.** So a `npm install provident-storeroom` puts **no `RELEASE-NOTES.md`** on
182
+ disk: a consumer holding only `node_modules` has the package's own bytes and the shipped guide
183
+ (`README.guide.md`), and reaches this file only by reading the repository. **The same was already true of the
184
+ `1.1.0` declaration above and it is true of the `1.2.0` list below.**
185
+
186
+ **⟶ AND BOTH PARAGRAPHS ABOVE ARE RE-READ, 2026-10-28 — `DEF-AC`: THIS FILE SHIPS, AND IT IS THE CHANGE LIST A
187
+ CONSUMER OF `1.2.0` RECEIVES.** `RCA-8(g)` ANNOTATE-BESIDE: **the two paragraphs stand BYTE-FOR-BYTE as the reading
188
+ of their own date; THIS clause is the operative reading of BOTH the heading and the allowlist.** **(i) THE TAG IS
189
+ CUT:** `git tag --list` answers `v1.0.0` · `v1.0.1` · `v1.1.0` · `v1.2.0`, so **the `1.2.0` section's *"UNRELEASED"*
190
+ heading and its *"no `v1.2.0` tag exists"* sentence are the reading of the date they were written and are read
191
+ THROUGH this clause; the release is CUT, and this text travels in its tarball.** **(ii) THE ALLOWLIST NAMES SEVEN
192
+ ROWS, THIS FILE AMONG THEM:** `index.mjs` · `README.md` · `README.guide.md` · `RELEASE-NOTES.md` · `index.d.ts` ·
193
+ `src/entry/` · `src/constraint-kernel.mjs` — **the landed copy of this document is a TRACKED, GENERATED file at
194
+ `bindings/js/RELEASE-NOTES.md`, staged byte-for-byte from this authored home by the release's assembly, exactly as
195
+ `README.guide.md` is staged from `docs/guides/package-use.md`** (`scripts/publish.sh`'s notes stage; the preflight
196
+ REFUSES a drifted landing). **SO THE SENTENCE *"NO TARBALL CARRIES IT"* IS UNTRUE FROM THIS DATE AND IS READ THROUGH
197
+ THIS CLAUSE: a consumer installing `1.2.0` has this change list at `node_modules/provident-storeroom/
198
+ RELEASE-NOTES.md`.**
199
+
200
+ **WHAT HAS CHANGED SINCE `1.1.0`, IN FOUR ITEMS — ONE OF THEM A BEHAVIOUR CHANGE AND THREE OF THEM NOT.**
201
+
202
+ 1. **`DeleteRecord` GAINED `discarded` (ADDITIVE).** A delete's record carries a fifth own member beside
203
+ `address`, `op`, `tiers` and `triggered`: **`discarded`**, a list `0` or `1` long of `{ opId, addresses }` —
204
+ the reversal a delivery raised and the engine threw away, with the addresses the discarded check owed.
205
+ `discarded.length === 0` means the delivery discarded nothing. **It is a WITNESS and not a repair:** the
206
+ delete still answers everything it answered, throws nothing, mints no status, and the reversal's effect still
207
+ stands. **AND ITS ENUMERABILITY CONSEQUENCE IS PART OF THE DECLARATION, NOT A FOOTNOTE: `discarded` is
208
+ DECLARED as a member in `index.d.ts` and is NON-ENUMERABLE at runtime.** Measured on this checkout
209
+ 2026-10-28: a delete's record answers `Object.keys` as `['address','op','tiers','triggered']` — **four** —
210
+ while `Object.getOwnPropertyNames` answers **five**, `discarded`'s own descriptor being
211
+ `{enumerable: false, configurable: true, writable: false}`. **So `Object.keys` never shows it, and a spread or
212
+ `Object.assign` copy of a receipt LOSES it:** both were driven and neither copy carried the member. **Read the
213
+ witness off the record itself** — a consumer that copies a receipt loses the witness, and nothing throws when
214
+ it does.
215
+
216
+ 2. **THE ONE BEHAVIOUR CHANGE: A REFUSED REVERSAL NOW RESTORES TO THE COMMIT'S OPENING MARK, NOT THE JOURNAL'S
217
+ HEAD.** `restoreReversal` used to restore the journal's head, on the reasoning that *"a reversal appends no
218
+ journal entry, so that head describes the state that stood BEFORE the action"*. **That reasoning fails on the
219
+ restart arm**: the enclosing write's own op stands at the head when a listener's reversal runs, so the
220
+ compensation restored the write's LANDING and **a write that reported a refusal had left its own value
221
+ standing** (`DEF-AE`: `9` stayed `9` where the refused write should have restored `7`). **Now the opening
222
+ caches its mark — the journal's last valid op id, or the floor where the journal holds no entry — and the
223
+ restore hands that cached mark back.** Measured: the refusing arm leaves the store where the ATTEMPT found it
224
+ (`tests/e2e/def-ae-restore-mark.test.mjs`, cells `1`–`2`, and the seven refusing arms swept around them).
225
+ **A lawful reversal is unchanged**, and a lawful write still answers its record with its value standing.
226
+
227
+ 3. **`context.value`'S RESPONSIBILITY IS NOW DECLARED (NO BEHAVIOUR CHANGE).** `D-50`, the architect, verbatim:
228
+ *"If a constraint needs to look for the data at a given location, it can read()"*. **`context.value` is the
229
+ value the write verb returned** — a repair that lands elsewhere hands your body the value that op landed — **so
230
+ a constraint that cares about any other location reads it itself.** Nothing about the machinery moved; the
231
+ guide's `§7.6` now says it, and the same section now names the exact-address consequence beside it: **a pass
232
+ judges the pairs standing at the address it writes, so a pair at another address is not asked** (measured: a
233
+ pair at `['b']` asked `0` times by a write at `['a']`).
234
+
235
+ 4. **THE PASS'S STATE MACHINE IS PINNED BY CELLS (NO BEHAVIOUR CHANGE).** The architect's state machine — one
236
+ action, the checks in order, **a repair restarting the checks from `#1` because a repair can break a check
237
+ already passed**, and termination being structural rather than counted — is now measured property by property
238
+ through the entry (`tests/e2e/repair-restart-state-machine.test.mjs`, whose cells also print that a pass
239
+ evaluates exactly the pairs at the attempt's own address). **The behaviour is the behaviour `1.1.0` already
240
+ ships; what is new is that it is pinned, so a later move meets a red instead of a reader.**
241
+
242
+ **AND ONE NUMBER A CONSUMER MIGHT WANT, WITH ITS WARNING: THE RESTART BUDGET IS `3` AND IT NOW SAYS SO IN THE
243
+ GUIDE.** `RESTART_LIMIT = 3` at `bindings/js/src/entry/constraints.mjs:1366` has been the behaviour since
244
+ `1.1.0`; the shipped guide declared it nowhere, and the guide's `§7.7` now does. **This is a DECLARATION and not
245
+ a change** — the bound, its spend-and-never-reset rule and its fail-closed arm stand exactly as they shipped.
246
+
247
+ **NOTHING ELSE IN THIS LIST IS A BREAKING CHANGE, AND NOTHING IN IT IS CUT.** Items `1` and `2` are the two a
248
+ consumer can feel; `3` and `4` are declarations and pins. **When `1.2.0` is cut, this section is the declaration
249
+ it carries — and until then the six figures a reader checks are PREPARED at `1.2.0` and the tag is NOT CUT, so what
250
+ is installed today is still `1.1.0`.**
251
+
252
+ ---
253
+
254
+ ## §0 · What to do if you are on `1.0.1`
255
+
256
+ 1. **Re-read your refusal mapper.** Read §2 items `B-1`, `B-2` and `B-5` before you upgrade. **A status you
257
+ branch on has changed meaning and one has changed value.** If your mapper treats `FileCommitFailed` as "a
258
+ constraint refused my write", it is now WRONG: that code is the SAVE's alone.
259
+ 2. **Wrap `rollback` in your throw path.** It throws now (§2 `B-3`) where a total function was declared.
260
+ 3. **Stop assuming a refused attempt leaves the refused value.** It leaves the state before the attempt
261
+ (§2 `B-4`).
262
+ 4. **Add `op` to anything that reads a delete's record** (§2 `B-7`). The record grew a fourth member; a
263
+ delete's reversal is now reachable by that member's id.
264
+ 5. **Do not carry a handle, an address object or a store instance across the recovery exits in §3.** Those
265
+ exits are the consumer's act, and the package does not perform them.
266
+ 6. **If you pinned `<1.0.1`** — the answer to `1.0.1`'s breach — **`1.1.0` is the version that supersedes it.**
267
+ Pin `1.1.0`, not `1.0.1`: the four items `B-1`…`B-4` reached `1.0.1`'s bytes UNDECLARED, and you get them
268
+ either way.
269
+
270
+ **A NOTE ON `B-1`…`B-4` AND THE WORD "AT `1.0.1`".** Those four were already in `1.0.1`'s shipped bytes —
271
+ `src/error.rs`'s `CODES` reads `[&str; 20]` at the `v1.0.1` tag, not `17`. **So for each of them this file
272
+ gives the reading at `1.0.0` (what moved), the fact that `1.0.1` shipped the move UNDECLARED, and the
273
+ reading at `1.1.0` (which is the same behaviour, now declared).** A consumer already on `1.0.1` has absorbed
274
+ them; **`B-5` onward are what `1.1.0` changes under them.**
275
+
276
+ ---
277
+
278
+ ## §1 · The shape of the list, and where it exceeded the ruling's count
279
+
280
+ `D-42` item `1` named **FOUR** breaking changes. **Derived from the tree rather than from the ruling's count,
281
+ the list is ELEVEN.** Four were shipped in `1.0.1` (`B-1`…`B-4`); seven more land in `1.1.0` (`B-5`…`B-11`).
282
+ **The list exceeded `D-42`'s four at `B-5`**, and every addition below was measured at its own site — the
283
+ commits named beside each item are the ones that made the change.
284
+
285
+ | # | the change | shipped in |
286
+ |---|---|---|
287
+ | `B-1` | the emitted vocabulary `17 → 20` | `1.0.1` (undeclared) |
288
+ | `B-2` | a constraint-refused write's status is no longer `FileCommitFailed` | `1.0.1` (undeclared) |
289
+ | `B-3` | `rollback` went from total to partial: **it throws** | `1.0.1` (undeclared) |
290
+ | `B-4` | a failing attempt leaves the pre-attempt state (`9 → 5`) | `1.0.1` (undeclared) |
291
+ | `B-5` | a refused reversal is RESTORED, and its restore WRITES the file | `1.1.0` |
292
+ | `B-6` | a constraint check always runs inside an open commit | `1.1.0` |
293
+ | `B-7` | `DeleteRecord` gained `op` — a record's shape grew | `1.1.0` |
294
+ | `B-8` | a delete's journal before-state was captured at the wrong home | `1.1.0` |
295
+ | `B-9` | an external callback's call opens its OWN commit | `1.1.0` |
296
+ | `B-10` | an action inside an open commit RESTARTS the constraint check | `1.1.0` |
297
+ | `B-11` | a directly driven pass reports a restart as a REPORT, not a throw | `1.1.0` |
298
+
299
+ ---
300
+
301
+ ## §2 · The breaking changes, one line of "before" and one of "now"
302
+
303
+ ### `B-1` · The emitted status vocabulary grew `17 → 20`
304
+
305
+ **AT `1.0.0`:** `CODES` was `[&str; 17]` — ranks `1`…`17`, the last being `ValueRefusedAtBoundary`.
306
+ **`1.0.1` SHIPPED THREE MEMBERS UNDER A PATCH WITHOUT SAYING SO.**
307
+ **AT `1.1.0`:** the set is **`[&str; 20]`**, ranks `1`…`20`, and the three appended members are:
308
+
309
+ - **rank `18` `ConstraintBodyThrew`** — a constraint BODY threw, where the package's crossing used to answer
310
+ the store's own code for a body that failed;
311
+ - **rank `19` `ConstraintRefusedAtRegistration`** — the store refused a pair AT REGISTRATION;
312
+ - **rank `20` `ConstraintPassRefused`** — the ONE status a constraint's refusal of a `write` answers, and of
313
+ a `rollback` whose terminal state a pair refuses.
314
+
315
+ **WHAT BREAKS YOU:** a mapper with a closed `switch` over the seventeen old members now meets three values it
316
+ has no arm for, and `errorMessage(code, args)` resolves a clause for each. **NO RANK OF `1`…`17` MOVED**, so
317
+ an ordinal you already stored is still that member (`FileCommitFailed` is still rank `15`).
318
+ **ONE MEMBER IS NOT A CONSUMER'S TO SEE:** `ConstraintRefusedAtRegistration` is the store's and crosses only
319
+ where the store mints it.
320
+
321
+ ### `B-2` · A constraint-refused write's status is `ConstraintPassRefused`, not `FileCommitFailed`
322
+
323
+ **AT `1.0.0` (and under `1.0.1`'s undeclared change, before it):** a write a constraint refused crossed as
324
+ `FileCommitFailed` — **which is the STORE'S SAVE CODE, rank `15`** — so the status named a layer the failure
325
+ did not happen at.
326
+ **AT `1.1.0`:** the refusal crosses as **`ConstraintPassRefused`, rank `20`**, the pass's own member.
327
+ **`FileCommitFailed` STAYS the save's code and is produced by save failures ONLY.**
328
+ **WHAT BREAKS YOU:** the most likely break in this release. **A consumer who treated `FileCommitFailed` as
329
+ "a constraint said no" now reads a save failure as a constraint refusal.** The refusal's own `KC` token still
330
+ rides `log[].state`; it is one status for all eight tokens, not one per token.
331
+
332
+ ### `B-3` · `rollback` went from total to partial, and it THROWS
333
+
334
+ **AT `1.0.1`'s DECLARATION:** `rollback(opId): RollbackRecord` — **a total function that answers a record and
335
+ fails never.** It was already untrue of `1.0.1`'s code; **this file declares it against the code, which is
336
+ where the truth is.**
337
+ **AT `1.1.0`:** a reversal is JUDGED. **A `rollback` whose TERMINAL state a registered pair refuses THROWS
338
+ `{status: 'ConstraintPassRefused', message, log}` at rank `20`.** A LAWFUL reversal is unchanged — same
339
+ `{opId, undoneOps, restoredTo}`, three members, no throw, no new member. **The judgement is of the state the
340
+ reversal LEFT, never a per-step approval** — an unwinding composition passes through intermediate states no
341
+ constraint would approve.
342
+ **WHAT BREAKS YOU:** a call you could not fail now throws a plain object literal (never an `Error` subclass).
343
+ **AND THE THROW IS NOT THE WHOLE OF IT — SEE `B-5`: THE REVERSAL IS ALSO UNDONE.**
344
+ **THE JUDGEMENT IS NOT A WALK AND NOT FREE:** it takes a pre-reversal standing of every enabled pair's every
345
+ address before the store call and asks the terminal state after it. A store that registered no constraint
346
+ reaches no read at all, and the member is what it was.
347
+
348
+ ### `B-4` · A failing attempt leaves the PRE-ATTEMPT state, not the refused value
349
+
350
+ **AT `1.0.0`:** seed `5`, refused write `9`, repair `1` — **the store was left holding `9`, the value the
351
+ constraint REFUSED.** A per-op walk over the repair's op reversed it AND marked it compacted, so the second
352
+ call over the write's op was answered `RollbackCompacted` and undid nothing. **The floor law made the walk
353
+ unsound and the violation stood.**
354
+ **AT `1.1.0`:** the failing arm leaves **`5`** — the state before the attempt. **A plain rejection still
355
+ leaves it, a repair answering `{ok: false}` still leaves it, and a PASSING repaired write still lands its
356
+ repaired value and composes nothing.**
357
+ **WHAT BREAKS YOU:** if you compensated for the old residue in your own layer, **your compensation now
358
+ unwinds a state that is already unwound.** The composition reports one step per address it issued a call at,
359
+ and `report.rollback` carries what actually happened.
360
+
361
+ ### `B-5` · A refused reversal is RESTORED, and its restore WRITES THE FILE
362
+
363
+ **AT `1.0.1`:** the compensating undo cleared every tier and rewrote at `mem` — **so a `file`-tier value was
364
+ DESTROYED by a refusal that then told you every address stood again at its pre-reversal state** (measured by
365
+ the downstream at 200 of 200), **and the restore was in memory only, so a re-boot read bytes describing the
366
+ PRE-restore state.** *(Commits `00f90df`, `9e16c0e`, `bd0fd8c`.)*
367
+ **AT `1.1.0`:** the restore puts back **exactly the per-tier standing it captured** — a present capture writes
368
+ each tier at its own tier, most persistent first, so a shadow reconstructs; an absent capture becomes ONE
369
+ untiered delete, where `AddressNotFound` **is** the owed state; **no tier that stood is ever cleared.** It
370
+ **verifies the result and counts an address restored only if it is exact**, so the message cannot claim what
371
+ did not happen — where it fails it names the address, what stands, what was owed, and how many of the
372
+ addresses it moved stand again. It restores **from the store's own journal** (`restoreMark`), not from a JS
373
+ capture, so a mixed-depth shadow and a higher tier whose parent the reversal moved below it are now reachable.
374
+ **AND `bd0fd8c` MAKES THE FILE AGREEMENT REAL:** the restore takes a `file`-tier snapshot before and after the
375
+ replay, and **where a version standing at `Tier::File` moved it SAVES the restored image** with the store's one
376
+ writer — so a re-boot returns to JUDGED bytes.
377
+ **WHAT BREAKS YOU — TWO THINGS, BOTH NEW BEHAVIOUR:**
378
+ - **THE BYTES AT `cfg.filePath` ARE NO LONGER "the last successful `file`-tier write's and no other state".**
379
+ A refused reversal's restore is now a SECOND producer of them. **A consumer who diffs those bytes, hashes the
380
+ file, or watches its inode/mtime will see it move on a path that used to write nothing** — and where no
381
+ file-tier version moved, **no byte is touched and the inode and mtime stay put.**
382
+ - **THE ASYMMETRY, SAID PLAINLY RATHER THAN LEFT TO BE DISCOVERED: a SUCCESSFUL `rollback` persists NOTHING.**
383
+ Measured: `file` `a=1`, `file` `a=2`, rollback the second — memory reads `1`, the bytes still say `{"a":2}`,
384
+ and a fresh boot reads `2`. **A reversal writes no bytes; a restore now does.**
385
+ **AND A SAVE THAT CANNOT LAND IS REPORTED, NOT SWALLOWED.** `FileCommitFailed` propagates after the replay and
386
+ the refusal says the store restored its state IN MEMORY while its FILE WAS NOT UPDATED. **The status stays
387
+ `ConstraintPassRefused`; nothing is minted for it.** **A constraint BODY that throws at the judgement now fails
388
+ closed**: the reversal is restored and the log names the throwing pair, with the body's own rank-`18` status
389
+ crossing — the code says what happened, the treatment is the violation's.
390
+ **STILL NOT RESTORABLE, NAMED SO IT IS NOT ASSUMED:** mixed-depth shadows *(reached by `restoreMark` now)*; an
391
+ address whose parent the journal never resolved *(reported as residue rather than guessed)*; state changed by
392
+ an act the store does not journal; an entry a failed file-save removed.
393
+
394
+ ### `B-6` · No constraint check without an open commit — a check always runs inside one
395
+
396
+ **AT `1.0.1`:** a CHECK could run where NO commit was open. That one state was every remaining window on this
397
+ branch's list — a listener body's write at a descendant, a pass driven by hand (`open=0`/`live=1`), and the
398
+ restart's dependence on the write path being the consumer. *(Commit `846f5ea`.)*
399
+ **AT `1.1.0`:** **an open commit is the PRECONDITION of the check**, enforced **where the check runs, not where
400
+ it is called from** — a seam a driver can bypass is not an invariant. `runAttempt` wraps its whole pass in the
401
+ store's own commit scope: **the scope opens one if none is open, joins the one that is, and closes only what it
402
+ opened.**
403
+ **WHAT BREAKS YOU:** **a store that carries no such channel is REFUSED, not run uncommitted** — the invariant
404
+ fails closed before status moves, before an entry, before any body, and without minting a token. **So a
405
+ consumer or a harness that hand-drives a pass over a store lacking that seam now gets a refusal where it used
406
+ to run.** **MEASURED: the production write's nesting is UNCHANGED** — the count during the pass is one at the
407
+ body's ask and one after its nested write. **A pass you drive directly now carries `open 1` where it carried
408
+ `0`**, opening exactly one commit itself and returning to zero.
409
+
410
+ ### `B-7` · `DeleteRecord` gained `op` — a record's shape grew
411
+
412
+ **AT `1.0.1`:** `delete` answered **`{address, tiers, triggered}`** — three members, **and no identity.** The
413
+ act was already journalled and `rollback` already accepted its id, **but no caller could NAME the act, so no
414
+ caller could reverse it**: an act that cannot be named cannot be reversed. *(Commit `c25765c`.)*
415
+ **AT `1.1.0`:** `delete` answers **`{address, tiers, triggered, op}`** — `op` is the store's own `JournalOp`,
416
+ **the same member, at the same name and the same type, that `WriteRecord` carries**, so `rollback(record.op.opId)`
417
+ is the one route to reversing a delete.
418
+ **`op` IS `null` ON THE ARM THAT REMOVED NOTHING** (`tiers: []`): that arm appends no journal entry, so it names
419
+ no op — **`null` is the truth rather than a minted id no reversal could find.**
420
+ **WHAT BREAKS YOU:** an object-shape assertion (`Object.keys(...).length === 3`, a strict deep-equal, a
421
+ serializer with a closed schema) breaks. **And `null` does not fold to `undefined`** — it crosses as `null`
422
+ unchanged.
423
+
424
+ ### `B-8` · A delete's journal before-state was captured at the wrong home
425
+
426
+ **AT `1.0.1`:** the entry's before-state was captured at the WRONG HOME — `pair_home` resolved the first
427
+ standing version's link — **so a STRUCTURED target journalled an EMPTY before-state and its reversal restored
428
+ NOTHING AT ALL.** Scalars looked healthy, which is why it went unseen; an EMPTY-ADDRESS delete had the same
429
+ fate. *(Commit `c25765c`, the defect the `B-7` exposure had been hiding.)*
430
+ **AT `1.1.0`:** the capture is at the correct home, and **an empty-address delete captures the register and
431
+ reverses too.**
432
+ **WHAT BREAKS YOU:** **code that worked around the old behaviour** — a manual re-write after reversing a
433
+ subtree delete — now restores over a correction that already happened. The reversal restores the values AND
434
+ their tiers, scalar and subtree alike. **A SECOND reversal of the same op answers `RollbackCompacted`**, which
435
+ is the existing floor and not a new rule.
436
+
437
+ ### `B-9` · An external callback's call opens its OWN commit
438
+
439
+ **AT `1.0.1`:** the join rule said a nested call *"JOINS the extant commit rather than opening a second"* — it
440
+ was written to keep ONE ACT's machinery from double-counting itself, **and it conflated INTERNAL NESTING (the
441
+ package's machinery inside its own verb) with EXTERNAL RE-ENTRY (your function, which the package invoked,
442
+ calling back in).** A listener body's write reused the outer verb's commit, so a listener's write at a
443
+ DESCENDANT address left the ancestor pair unasked and the violating effect stood. *(Commits `98cb3ac`,
444
+ `835f09f`; rulings `D-45`, `D-46`.)*
445
+ **AT `1.1.0`:** **a listener body — and every one of the caller-code invocation sites — OPENS A NEW COMMIT**,
446
+ because that is what entering at the package surface means. **The discriminator is the COMMIT ID a call NAMES,
447
+ not an authorship the package infers:** a call that supplies a commit id **JOINS** that commit; **a call that
448
+ supplies none OPENS a new one.** `write`'s FOURTH position already carries the commit, so **the surface did not
449
+ grow a member — it grew a rule about one it has.**
450
+ **A REPAIR JOINS** — a repair is not a separate act at the surface, it is PART OF THE ATTEMPT BEING TRACKED, so
451
+ it stays inside the attempt's own commit and the commit's record still describes the repair it performed.
452
+ **WHAT BREAKS YOU — THE OBSERVABLE COUNTS MOVE:** a listener body's write now **opens a second commit**, so an
453
+ attempt-opened observer **fires TWICE where it fired once**, and the descendant address is now asked by the
454
+ listener's OWN attempt rather than the outer write's. **The outer commit's count is unchanged across the call
455
+ and the outer write's record is untouched.** An ordinary write with a pair body and a listener body inside it
456
+ still opens ONE commit. **ONE EXTERNAL FUNCTION STILL INHERITS, named rather than hidden: the attempt-opened
457
+ observer itself**, which is called inside the opening before it returns — marking it would let a self-writing
458
+ observer open a commit at every notification, unbounded.
459
+
460
+ ### `B-10` · An action inside an open commit RESTARTS the constraint check
461
+
462
+ **AT `1.0.1`:** a reversal issued INSIDE an open commit — from a listener body, from a constraint body — was
463
+ **UNJUDGED**, so its violating effect STOOD, **while the identical call one frame out was refused and
464
+ restored.** The conservative direction had become the permissive one. *(Commit `fd11eff`.)*
465
+ **AT `1.1.0`:** **an action performed inside an open commit STARTS THE CONSTRAINT CHECK OVER AGAIN** — the
466
+ check the action invalidated is **re-run from its FIRST pair against the state the action left**, so no
467
+ violating effect can stand unjudged and no body is invoked mid-unwind either, because the restart is a fresh
468
+ pass and not a judgement of a partial composition. **The step itself is still not judged at its instant**
469
+ (`D-40`'s gate stands: the watch keeps answering empty and no body runs there).
470
+ **WHAT BREAKS YOU:** **a violating reversal inside an open commit is now REFUSED and restored, where it used to
471
+ stand.** Measured: a listener body asked `[9,7,5]` and the write was REFUSED, store at `7`; a constraint body
472
+ the same; a LAWFUL act asked `[9,7,5]` with NO refusal and its effect stands. **TERMINATION IS BOUNDED RATHER
473
+ THAN ASSUMED: three restarts per terminal act**, the budget spent and not reset by actions raised inside a
474
+ restart, **and past it the check FAILS CLOSED in the same form.** **So a consumer whose body writes and
475
+ reverses in a loop now meets a refusal at the third restart instead of an unbounded pass.**
476
+
477
+ ### `B-11` · A directly driven pass LEARNS a restart from the REPORT, not from a throw
478
+
479
+ **AT `1.0.1`:** the restart's request was consumed by the WRITE's frame, **so a body's `rollback` inside a
480
+ directly driven pass was neither judged at its instant nor restarted, and the request died with its window.**
481
+ *(Commit `0ade6b3`.)*
482
+ **AT `1.1.0`:** the request is a store-side fact and **its consumer is the store's own commit scope** — one
483
+ mechanism, not two. `runAttempt` drains it after its pass answers, inside the same scope. **And a directly
484
+ driven pass learns it from the REPORT rather than a throw: the outcome becomes `failed` and the refusal's own
485
+ constraint-check entry joins the attempt's log with its REAL ordinal**, so `K-3` does not throw while `K-5`
486
+ answers the same object.
487
+ **WHAT BREAKS YOU:** **a harness that drove a pass by hand and expected a THROW now gets none** — it must read
488
+ `report.outcome` and `report.log`. **The refusal's compensating restore runs before the report is amended**, so
489
+ your store stands restored; what you must READ is the report, never a throw.
490
+
491
+ ---
492
+
493
+ ## §3 · The two known fail-states, and their exits
494
+
495
+ **These are DECLARED, not fixed.** `D-43` item `2`, the architect, verbatim: *"Known fail-state, file reloads /
496
+ client regenerates store to return to valid state."* **A known fail-state with a documented recovery is a
497
+ specification; an unreachable window with no exit is a defect.** **The declaration of record is
498
+ `docs/specs/storeroom-js/contract.md`'s clause of 2026-10-27 and it is restated here because a consumer of the
499
+ tarball reads this file.**
500
+
501
+ **THE TWO WINDOWS.** Each holds a commit and **runs NO PASS**, so a request raised inside it has no consumer
502
+ and dies with that window:
503
+ 1. **A `delete`'s listener delivery** — a delete consults no constraint, so the delivery runs with an open
504
+ commit and no check; a `rollback` a listener body issues there is a step whose check never runs.
505
+ 2. **A REGISTRATION's own evaluation** — `addConstraint`/`register` asks a pair's body inside the registering
506
+ verb's own commit window, at an instant no `K-2`/`K-3` attempt stands at, so a `rollback` a registration
507
+ body issues dies the same way.
508
+
509
+ **NEITHER WINDOW IS NEW, NEITHER IS A DEFECT, AND NEITHER IS AN UNREACHED CASE OF THE RESTART.**
510
+ **WHAT IS TRUE OF BOTH, SAID BEFORE THE EXIT: the reversal's own effect STANDS in the live instance — so the
511
+ absence of a refusal there is NOT an approval and must not be read as one.**
512
+
513
+ **EXIT 1 — RELOAD THE FILE.** **Construct a fresh instance over the same `cfg.filePath`.** The boot reads the
514
+ bytes at the path and nothing else. **AND SINCE `1.1.0` THIS RETURNS TO JUDGED BYTES RATHER THAN TO BYTES
515
+ DESCRIBING AN UNJUDGED STATE**: a restore that moved a version standing at `Tier::File` now SAVES the restored
516
+ image (§2 `B-5`). **What it does not carry:** the boot reads the last successful save and no other state — no
517
+ journal, no registration, no `mem` or `temp` version — and a reversal that moved only `mem`/`temp` nodes does
518
+ not save, so a reload does not carry it. **This exit is the store's declared boot semantics, not a general
519
+ undo.**
520
+
521
+ **EXIT 2 — REGENERATE THE STORE.** **Discard the instance and construct the store again from the same
522
+ configuration** — a `mem` register with no `filePath`, or a `file` register over the same path, which is exit
523
+ `1` as well. **The new instance is a judged state by construction**: no attempt, no open commit, no window
524
+ carried over.
525
+
526
+ **THE CONSUMER'S OBLIGATION IS THE SAME ON BOTH EXITS, AND IT IS THE POINT: the recovery is YOUR act; the
527
+ package neither performs it nor pretends to.**
528
+
529
+ ---
530
+
531
+ ## §4 · What did NOT break — so the reading is not taken wider than it is
532
+
533
+ - **THE FOURTEEN OWN MEMBERS ARE UNMOVED** — `{ read, readLiteral, write, delete, rollback, listen, unlisten,
534
+ addConstraint, constraintOn, constraintOff, register, constraints, removeListener, removeConstraint }`.
535
+ **No member was added and none was removed.** The module namespace keeps its ONE name, `storeCreate`.
536
+ - **NO POSITION, ARITY OR CLASS TEST MOVED.** `write`'s fourth position was READ for a new purpose and its
537
+ class domain is unmoved: it accepts the commit RECORD and refuses everything else in the one `TypeError`.
538
+ - **THE THROWN FORM IS THE SAME ONE FORM** — a plain object literal `{status, message, log}`, never an `Error`
539
+ subclass — and `ConstraintPassRefused` is a member of the SAME set, not a new shape.
540
+ - **REGISTRATION ORDER IS A GUARANTEE, NOT A LUCK.** `constraints()` emits by a MONOTONE REGISTRATION
541
+ SEQUENCE, kept through overwrite, toggle and restore, so five snapshots are element-wise equal and equal to
542
+ registration order. **This is new in `1.0.1` and it is a GUARANTEE, NOT A BREAK** — before it, *"no order is
543
+ promised"* and a toggle could perturb it. It is declared here because a consumer diffing snapshots relies on
544
+ it and because a later change would be a break.
545
+ - **NO TABLE ROW, RANK OR TRIGGER MOVED** inside ranks `1`…`17`.
546
+ - **THE CRATE'S VERSION IS NOT THE RELEASE VERSION.** `bindings/js/Cargo.toml` reads the crate's own `0.1.0`
547
+ and `version()` answers the RELEASE's. They are deliberately distinct.
548
+
549
+ ---
550
+
551
+ ## §5 · Where the authority lives
552
+
553
+ - **The declaration of record** is the tree under `docs/specs/` — chiefly `storeroom-js/contract.md` (the
554
+ package surface), `storeroom-native/contract.md` (the store), and `storeroom-publish/contract.md` (the six
555
+ packages' coupling). **This note is a CONSUMER SUMMARY of them and yields to them.**
556
+ - **The rulings** are `strata/decisions.md` — `D-42` (the minor, the four, the restart), `D-43` (a listener's
557
+ own commit, the known fail-state, a journaled and reversible delete), `D-44` (no check without an open
558
+ commit), `D-45`/`D-46` (a callback opens, a repair joins, the commit id decides), `D-41`/`D-40` (the
559
+ attempt's footprint and the step's gate).
560
+ - **The shipped guide** is `README.guide.md` INSIDE the main package — the assembly stages
561
+ `docs/guides/package-use.md` there BYTE-FOR-BYTE, so when you read it from `node_modules/provident-storeroom/`
562
+ it IS that document.
563
+
564
+ **THE SIX PACKAGES, ONE VERSION, ONE DECISION:** `provident-storeroom` and the five
565
+ `@auspicion/provident-storeroom-js-{linux-x64-gnu, linux-arm64-gnu, darwin-x64, darwin-arm64, win32-x64-msvc}`
566
+ all read **`1.1.0`**, and the main manifest's five `optionalDependencies` pins are EXACT `1.1.0` — no caret, no
567
+ tilde, no range. **A manifest that reads anything else anywhere is the wrong package.**
568
+
569
+ **STATUS OF THIS RELEASE'S OWN REHEARSAL:** the six tarballs were packed, a `1.1.0` install was driven in a
570
+ `/tmp` consumer with no repository in its ancestry, and `version()` answered `1.1.0` at the rebuilt artefact and
571
+ at the installed package. **No tag was cut, nothing was pushed, nothing was published, and no registry install
572
+ was witnessed** — a registry-installed `1.1.0` is described here from the manifest and from a locally packed,
573
+ installed artefact, and is still not witnessed as a registry download.
574
+
575
+ **⟶ AND THE TWO PARAGRAPHS ABOVE ARE `1.1.0`'S OWN READING AND ARE READ THROUGH THIS CLAUSE, 2026-10-28: AT THIS
576
+ TREE THE SIX FIGURES READ `1.2.0`, AND `1.2.0` IS PREPARED AND NOT CUT.** `RCA-8(g)` ANNOTATE-BESIDE: **both stand
577
+ BYTE-FOR-BYTE; what each says of `1.1.0` was true of the tree it was read on.** **THE SIX FIGURES, RE-READ AT THEIR
578
+ OWN BYTES THIS DATE: the main manifest's `version` is `"1.2.0"`, its five `optionalDependencies` pins are exact
579
+ `1.2.0` with it, and the five platform manifests' `version` fields are `1.2.0` with them — eleven sites, moving
580
+ together and never singly** (`PUB-DD-10`) — **so the rule the paragraph above teaches is UNMOVED and its figure is
581
+ `1.2.0`: a manifest that reads anything else anywhere is the wrong package.** **THE CUT, AND WHAT THIS FILE STILL
582
+ DOES NOT CLAIM:** `git tag --list` carries `v1.0.0` · `v1.0.1` · `v1.1.0` and no `v1.2.0`, so **`1.2.0` is PREPARED
583
+ at those eleven figures and the tag is not cut until the architect cuts it**; the bytes a registry install yields
584
+ today are `1.1.0`'s, and nothing here says otherwise.