provident-storeroom 1.3.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.
@@ -188,3 +188,168 @@ export function commit(store, address, tier, value) {
188
188
  return record;
189
189
  }
190
190
 
191
+ // ---------------------------------------------------------------------------------------------
192
+ // **⟶ ADDED 2026-10-29 — `commitRemoval`: THE DELETE-SHAPED COMMIT ACT. `D-55` ITEM 3, `DEF-X`.**
193
+ //
194
+ // **THE RULING, THE ARCHITECT, VERBATIM: *"A delete is an editing verb and must pass constraints. This includes
195
+ // all of the relevant commit and message logic."*** (`strata/decisions.md` · `D-55` item `3`). **THE MEASURED
196
+ // OBSTACLE IT ANSWERS WAS PRINTED, NOT ASSUMED**: a `delete` opens its own commit window and reaches the
197
+ // addon's `delete` directly — **no `K-2`, no `K-3`, no log and no terminal entry** — and **the only pass shape
198
+ // the artefact had (`K-2` then `K-3`) LANDS A WRITE FIRST** (`storeAfter = {"c":2}`), so any pass driven
199
+ // through it judges a state the delete is not in. **SO THE DELETE GETS AN ATTEMPT OF ITS OWN KIND, AND THIS IS
200
+ // IT** — the commit act's remove-shaped sibling, and NOT a re-used `K-2`.
201
+ //
202
+ // **IT OPENS LIKE ANY OTHER, AND THE TWO THINGS IT SHARES WITH `K-2` ARE THE TWO THAT MATTER:**
203
+ // * **`D-44` IS KEPT BY THE CALLER AND RE-CHECKED HERE**: the attempt is opened inside the delete's own
204
+ // commit window (the member opens it before this act is reached, `index.mjs`'s `delete`), so no check this
205
+ // pass runs stands outside one — and a store this package did not build carries no commit scope at all, so
206
+ // `runAttempt` fails closed exactly as it does for a write.
207
+ // * **`D-53` IS KEPT BY THE SAME READ `K-2` TAKES**: `commitLogInFlight` answers the enclosing invocation's
208
+ // list where this delete was called from caller code that BELONGS TO an attempt (a constraint body's
209
+ // `delete`, a repair's), and `null` at a consumer's own call. **ONE LIST PER COMMIT, AND A DELETE'S ENTRIES
210
+ // JOIN IT** — there is no second list and no merge.
211
+ //
212
+ // **WHAT IT DOES NOT DO IS LAND A WRITE.** `K-2` calls the store's `write`. **This act mints an `AttemptRecord`
213
+ // of kind `delete` and calls THE STORE'S OWN LEG THE CALLER HANDS IT** — the addon's `delete`, already crossed
214
+ // by the entry — so **the state the attempt is judged at is the state the delete LEFT and never a state a probe
215
+ // wrote.** The act's own answer (`removal`) is the delete's declared receipt: its `op` is the DELETE's journal
216
+ // op and so THE ATTEMPT'S ONE LANDING (`attempt.ops`), which is what the pass's context carries and what the
217
+ // failing arm's composition rolls back; its `triggered` set is the store's own returned set, kept on
218
+ // `attempt.write` for the terminal act's discard rule exactly as a failed write's is.
219
+ //
220
+ // **⟶ AND IT MINTS THE `CommitRecord` TOO, 2026-10-29 — THE FOUR MEMBERS, THE SYMBOL-KEYED LOG, AND THE ONE
221
+ // IDENTITY THE WHOLE COMMIT'S MACHINERY IS KEYED BY.** The `DEF-X` build measured the four residuals this
222
+ // closes and printed them; each is a `K-2` behaviour that follows from the record and from nothing else:
223
+ // * **ADOPTION** — the record is what `K-2` hands the store's own verb at its fourth position, and what the
224
+ // entry holds so that the TERMINAL ARM performs the delivery and the save. A delete has no fourth position
225
+ // (its declared arity is `delete(address, tier)`), so the entry adopts the record it holds the same way it
226
+ // adopts a write's: it stands the delete's own returned receipt in the leg slot the terminal arm reads
227
+ // (`index.mjs`'s `delete`), and the RECORD is what `settleArm` hands that arm.
228
+ // * **THE `pending` RESIDUE** — written exactly as `K-2` writes it: the returned set WHOLE at this instant,
229
+ // and a handle leaves it only by its own pair's check-off (`releasePending`) or by the failing arm's
230
+ // discard (`terminal`). **NO SIFT**: the delete's own returned set is carried, member for member.
231
+ // * **THE `file`-TIER SAVE** — the terminal arm's save is gated on the record this act mints
232
+ // (`SETTLED_ON_THE_PASSING_ARM`, placed by the passing arm) and on the leg the entry adopted, so a
233
+ // `file`-tier delete SAVES its own op exactly as a `file`-tier write saves its own.
234
+ // * **`K-4`/`K-5` BY REF** — the record carries `attempt`, the `AttemptRef` those two items accept, so the
235
+ // delete's log and report are answerable after a PASSING delete and not only from a refusal's log.
236
+ // **THE RECORD'S FOUR MEMBERS AND ITS SYMBOL-KEYED LOG ARE `K-2`'S OWN, SPELLED THE SAME WAY**: the log is
237
+ // defined with `Object.defineProperty` under `COMMIT_LOG` and is not a fifth member (`A117` (D6)), so a census
238
+ // of any record moves by nothing.
239
+ //
240
+ // **THE `D-3` ARM IS KEPT AND A HELD ADDRESS IS JOINED RATHER THAN REFUSED** (the ground is printed at the act
241
+ // itself and it is a measurement, not a preference: a repair has no fourth position to name its commit at, so
242
+ // a `KC-7` here would make a lawful repair's `delete` unlawful):
243
+ // * **A REFUSED STORE VERB LEAVES NO ATTEMPT** (`D-3`'s neighbour): the addon's own refusal crosses back
244
+ // unchanged, and because the attempt is placed in `kernel.live` only AFTER the act answered an op, a
245
+ // refused delete holds nothing and closes nothing — `K-2`'s own rule, reached by the same structure.
246
+ // * **AND AN ACT THAT REMOVED NOTHING IS `D-3`'S SUCCESS WITH `tiers: []`**: no journal entry was appended,
247
+ // so there is no op, no landing to judge and no edit to undo — **this act answers `null`, the entry mints
248
+ // no pass, and nothing is added to any log.** A delete that changed nothing cannot be refused by a
249
+ // constraint, because it is not an edit. **NO `KC` TOKEN IS MINTED FOR IT AND NO TENTH TOKEN EXISTS.**
250
+ //
251
+ // **WHAT IT ANSWERS.** The attempt record, open and live — **carrying `attempt.record`, the `CommitRecord` this
252
+ // act minted** — for the pass the caller runs next (`entry`'s `constraintDelete`), or `null` on the `D-3` arm.
253
+ // **NO KERNEL ITEM IS MINTED**: `kernel-surface.md §1` item
254
+ // `2` closes the kernel's EXPORT SET at the five `K-1`–`K-5` items and that barrel
255
+ // (`src/constraint-kernel.mjs`) is untouched — this function is a part module's own act, reached by the entry
256
+ // the way every other kernel part is, and it is named by no `K-` number.
257
+ export function commitRemoval(store, address, tier, remove) {
258
+ const kernel = kernelFor(store);
259
+ // **NO `KC-7` REFUSAL IS TAKEN AT THIS OPENING, AND THE ASYMMETRY WITH `K-2` IS NAMED BECAUSE IT IS
260
+ // MEASURED.** `K-2` refuses a second attempt at a HELD address, and a REPAIR at that address is admitted only
261
+ // because it NAMES the enclosing commit at `write`'s fourth position (`D-46`). **A delete has no such
262
+ // position** — the member's arity is the declared `delete(address, tier)` and this pass changes no
263
+ // declaration — so a repair that edits by `delete` at the attempt's own address (the shape
264
+ // `tests/integration/js-constraints/constraint-kernel-negatives.test.mjs`'s `P-NEG-22` drives, and
265
+ // `parameter-semantics.md §2.7` blesses) would be refused by a hold it had no way to name its way out of,
266
+ // and a lawful repair would stop being lawful. **SO THIS ACT REFUSES NOTHING ON THAT GROUND**: it joins the
267
+ // address as a second attempt record and places itself in `kernel.live` below, which keeps the hold the
268
+ // enclosing attempt already has (`attemptHeldAt` stays true there for every nested `K-2`, unchanged) and
269
+ // takes the hold itself where the address was free. **THE COUNT `reversalAtRest` READS IS UNMOVED IN
270
+ // MEANING**: an open delete attempt IS an open attempt, so a reversal raised inside its pass is a step.
271
+ kernel.attempts += 1;
272
+ // THE ATTEMPT'S ADDRESS IS A LOCAL INDEX COPY of the caller's array, never `address.slice()` (`D-35`'s read
273
+ // half; the same form `commit` above takes and for the same measured reason).
274
+ const addressCopy = [];
275
+ for (let i = 0; i < address.length; i += 1) appendOwn(addressCopy, address[i]);
276
+ // THE COMMIT'S ONE LIST, BY REFERENCE, OR A FRESH ONE WHERE THIS DELETE OPENED THE COMMIT (`D-53`).
277
+ const joined = commitLogInFlight(kernel);
278
+ const ref = { id: `attempt-${kernel.attempts}` };
279
+ const attempt = {
280
+ // **THE KIND — the discriminator the write's attempt never needed**: an attempt that landed no write is
281
+ // judged against the DELETE's op, and it now owes the SAME `CommitRecord` a write's attempt does.
282
+ kind: 'delete',
283
+ ref,
284
+ // **THE NAME THE BODIES THIS PASS INVOKES JOIN** (`D-46`/`D-53`). The record below is that name from now
285
+ // on — `kernel-attempt.mjs`'s `evaluate` hands `attempt.record` to the invocation channel where one exists —
286
+ // and this one-member object STANDS AS THE RECORD'S ABSENT ARM, so a body's write joins the delete's commit
287
+ // instead of opening a second one on either arm.
288
+ name: { attempt: ref },
289
+ store,
290
+ address: addressCopy,
291
+ tier,
292
+ // THE SUBJECT IS WHAT THE DELETE REMOVES, NOT A VALUE: the after-state is absence, and the op carries what
293
+ // stood before it (its `before`) per tier (`D-55` item `3`'s *"the addresses and the tiers"*).
294
+ value: undefined,
295
+ commit: undefined,
296
+ // The store's own returned set, kept whole for the terminal act's discard rule (`terminal`).
297
+ write: null,
298
+ // THE DELETE'S JOURNAL OP IS THE ATTEMPT'S ONE LANDING — empty until the act answers one.
299
+ ops: [],
300
+ log: joined === null ? [] : joined,
301
+ ran: [],
302
+ appliedRepairs: [],
303
+ rollback: [],
304
+ checkpoint: null,
305
+ checkedOff: [],
306
+ status: 'open',
307
+ report: null,
308
+ };
309
+ // **THE RECORD, BUILT BEFORE THE STORE'S LEG IS CALLED — `K-2`'s OWN INSTANT AND `K-2`'s OWN FOUR MEMBERS**
310
+ // (`kernel-surface.md §2.1` item 2; `A112` re-spells the fourth: *"`pending` IS THE WHOLE RETURNED SET"*).
311
+ // Its members are FILLED from the receipt below; it is built here so the commit's identity exists before the
312
+ // act lands anything, exactly as it does for a write.
313
+ const record = {
314
+ attempt: attempt.ref,
315
+ op: null,
316
+ triggered: [],
317
+ pending: [],
318
+ };
319
+ attempt.record = record;
320
+ // AND THE COMMIT'S LIST TRAVELS ON IT UNDER THE SYMBOL KEY, VERBATIM AS `K-2` DEFINES IT (`D-53`): ONE LIST
321
+ // PER COMMIT, and the record is the identity it hangs from. **NO MEMBER IS ADDED** (`A117` (D6)).
322
+ Object.defineProperty(record, COMMIT_LOG, {
323
+ value: attempt.log,
324
+ enumerable: false,
325
+ writable: false,
326
+ configurable: true,
327
+ });
328
+ // **THE STORE'S OWN LEG, AND IT IS THE CALLER'S** (`kernel-surface.md §1` item 1's empty import footprint:
329
+ // this module reaches no entry and no addon of its own; the thunk is the entry's own addon call).
330
+ const removal = remove();
331
+ // `D-3`: nothing was removed — no entry was appended, so there is no op to name and no state to judge. **NO
332
+ // ATTEMPT IS OPENED AND NO RECORD CROSSES**: the receipt is the whole answer, its `tiers` and `triggered`
333
+ // empty, and the record built above dies with this attempt.
334
+ if (removal === null || typeof removal !== 'object' || removal.op === null || removal.op === undefined) {
335
+ attempt.status = 'closed';
336
+ return null;
337
+ }
338
+ attempt.write = removal;
339
+ attempt.ops = [removal.op];
340
+ // **THE RECEIPT'S OWN MEMBERS, READ OFF IT VERBATIM — `K-2`'s three assignments at its own record's name.**
341
+ // `triggered` is the store's returned set and `pending` is that set WHOLE (no sift; `A112`), so the residue
342
+ // the terminal arm delivers and the failing arm's discard reads are the delete's own, unfiltered.
343
+ record.op = removal.op;
344
+ record.triggered = removal.triggered;
345
+ record.pending = removal.triggered.slice();
346
+ // THE ATTEMPT IS PLACED LIVE HERE, BEFORE THE FIRST BODY OF THE PASS RUNS: the store's own `delete` invokes
347
+ // nothing, so there is no window between the act and this line in which a caller's code could interleave —
348
+ // and the address is held from this instant through the pass and its terminal act, exactly as `K-2` holds
349
+ // it. **THE MAP IS ALSO THE COUNT OF OPEN ATTEMPTS** the restart's at-rest discriminator reads
350
+ // (`constraints.mjs`'s `reversalAtRest`), so a reversal raised inside this pass is a step and not a
351
+ // judgement.
352
+ kernel.live.set(ref.id, attempt);
353
+ return attempt;
354
+ }
355
+
@@ -229,6 +229,61 @@ export function markBodyThrow(thrown, address, log) {
229
229
  }
230
230
  }
231
231
 
232
+ /// **⟶ ADDED 2026-10-28 — `D-55` ITEM 1, THE ARCHITECT, VERBATIM: *"Include a thrown message in the message
233
+ /// log. Obviously."*** **THE MEASURED DEFECT (`DEF-AK`):** a constraint body that threw its own `Error`
234
+ /// crossed as the rank-`18` `ConstraintBodyThrew` whose `log` carried **only the attempt's `outcome` entry** —
235
+ /// the thrown value's own literal occurred **zero times** in it — while the crossed sentence said *"a
236
+ /// constraint body's throwable crosses as this refusal"*. **The commit's log now carries the throwable's own
237
+ /// form, written into the ONE `outcome` entry the terminal act appends at the frame that caught the body —
238
+ /// before the attempt is disposed of and before the record is handed off** (`kernel-attempt.mjs`'s `evaluate`;
239
+ /// no second entry and no fifth `kind` is minted).
240
+ ///
241
+ /// **WHAT THE FORM IS, AND WHY IT NAMES WHAT IT READ RATHER THAN A CLASS.** The value is the CALLER'S OWN —
242
+ /// it may be an `Error`, a string, a number, an object with no `message`, a `Proxy` — so the spelling is
243
+ /// built from **own data members only** (`ownMember`, `[[GetOwnProperty]]`, `D-35`'s read half) and from the
244
+ /// language's own `typeof`. **NO PROTOTYPE IS CONSULTED**: `instanceof`, `constructor.name` and
245
+ /// `Object.prototype.toString` all walk a chain a caller owns, and a `Proxy`'s trap would RUN here, on a
246
+ /// refusal path. **AND A CLASS IS NEVER CLAIMED**: *"an object whose own `message` member is the string …"*
247
+ /// is true of an `Error` and of a plain object alike, which is what the reading supports.
248
+ ///
249
+ /// **THE FORM IS NAMED AND NOT SILENTLY STRINGIFIED**: a thrown string is *"a string whose text is …"*, a
250
+ /// number *"a number whose value is …"*, a thrown `void 0` *"no value at all"*, an object without a `message`
251
+ /// *"an object carrying no own string `message` member"*. A consumer therefore reads WHAT KIND of value the
252
+ /// body threw beside whatever text it carried.
253
+ ///
254
+ /// **IT IS TOTAL, AND THE ONLY READ THAT COULD RUN CALLER CODE IS THE GUARDED ONE**: this frame invokes
255
+ /// nothing of the caller's — `ownMember` is `[[GetOwnProperty]]` and consults no chain — and the one trap it
256
+ /// can trip (a `Proxy`'s `getOwnPropertyDescriptor`) is inside a `try`, whose arm past it answers a sentence of
257
+ /// this frame's own. **NO `undefined` IS EVER PRINTED** (`native/contract.md §8.2` property (c); the `DEF-Q`
258
+ /// sweep reads every entry's message for it): a thrown `void 0` says *"no value at all"* and a missing member
259
+ /// is named as absent rather than spelled.
260
+ export function throwableForm(thrown) {
261
+ if (thrown === null) return '`null`';
262
+ const kind = typeof thrown;
263
+ if (kind === 'undefined') return 'no value at all (the body threw `void 0`)';
264
+ if (kind === 'string') return `a string whose text is ${JSON.stringify(thrown)}`;
265
+ if (kind === 'number') return `a number whose value is ${String(thrown)}`;
266
+ if (kind === 'boolean') return `a boolean whose value is ${String(thrown)}`;
267
+ if (kind === 'bigint') return `a bigint whose value is ${String(thrown)}`;
268
+ if (kind === 'symbol') return 'a symbol, which carries no text a log can print';
269
+ if (kind === 'function') return 'a function, which carries no message';
270
+ let message;
271
+ let name;
272
+ try {
273
+ message = ownMember(thrown, 'message');
274
+ name = ownMember(thrown, 'name');
275
+ } catch {
276
+ // A `Proxy` whose `getOwnPropertyDescriptor` trap throws cannot be read at all: this frame says so and
277
+ // runs nothing further (`D-35`: the refusal path invokes no caller code it was not already handed).
278
+ return 'an object this frame could not read, so its form is not printed';
279
+ }
280
+ const named = typeof name === 'string' && name.length > 0 ? ` named ${JSON.stringify(name)}` : '';
281
+ if (typeof message === 'string') {
282
+ return `an object${named} whose own \`message\` member is the string ${JSON.stringify(message)}`;
283
+ }
284
+ return `an object${named} carrying no own string \`message\` member`;
285
+ }
286
+
232
287
  /// **WHETHER A VALUE IS A REFUSAL THIS KERNEL BUILT** — the entry's crossing and this kernel's own repair
233
288
  /// arm both ask this and nothing else.
234
289
  ///
@@ -397,6 +452,37 @@ export function sameAddress(left, right) {
397
452
  return true;
398
453
  }
399
454
 
455
+ /**
456
+ * **⟶ ADDED 2026-10-29 — `isAddressPrefix`: THE PASS'S OWN MEMBERSHIP TEST, AND IT IS `D-56`'S SENTENCE IN
457
+ * ONE FUNCTION.** The architect, verbatim: *"A listener/constraint should fire when the descendants of its
458
+ * targeted address node change, because the descendants become part of the object that is read from that
459
+ * address"* (`strata/decisions.md` · `D-56`). **THE GROUND IS SEMANTIC AND NOT A PREFERENCE: the value read at
460
+ * a prefix CONTAINS its subtree, so a change at a descendant CHANGES what a read at the ancestor returns** —
461
+ * and a pair standing at the ancestor therefore **judges an object that moved.**
462
+ *
463
+ * **THE SET, STATED ONCE: a pair is asked when THE ACT'S ADDRESS IS AT OR BELOW THE PAIR'S ADDRESS** — *"the
464
+ * pair's address is a prefix of the act's address (equality included)"* (`D-56`). **So `isAddressPrefix(standing,
465
+ * act)` is `true` exactly where the pair stands AT the act's address OR AT ONE OF ITS ANCESTORS**, and `false`
466
+ * for a pair standing at a DESCENDANT — the unruled direction, which `D-56` requires to be MEASURED rather than
467
+ * implemented, and which this predicate therefore does not reach.
468
+ *
469
+ * **IT IS STRUCTURAL AND SEGMENT-WISE, WITH `string` AND `number` DISTINGUISHED, EXACTLY AS `sameAddress` IS**
470
+ * (`P: docs/pending.md · (M-3)` item (iv)): a pair at `['a', 0]` is NOT asked by an act at `['a', '0']`, so the
471
+ * widening changes WHICH DEPTHS are members and NOT how a segment is compared. **A SEGMENT COUNT OF ZERO IS A
472
+ * PREFIX OF EVERY ADDRESS** — the root pair's own reading, and the same one the trigger set's prefix rule gives.
473
+ */
474
+ export function isAddressPrefix(prefix, address) {
475
+ if (!Array.isArray(prefix) || !Array.isArray(address)) return false;
476
+ if (prefix.length > address.length) return false;
477
+ for (let i = 0; i < prefix.length; i += 1) {
478
+ const a = prefix[i];
479
+ const b = address[i];
480
+ if (typeof a !== typeof b) return false;
481
+ if (a !== b) return false;
482
+ }
483
+ return true;
484
+ }
485
+
400
486
  // ---------------------------------------------------------------------------------------------
401
487
  // THE KERNEL'S OWN CALLER-SIDE STATE — **THE INSTANCE OWNS IT, IN A SLOT ON THE INSTANCE ITSELF**, so
402
488
  // two stores never share a registry, this module retains no store of its own, and NOTHING STANDS AT
@@ -654,6 +740,44 @@ export function ownMember(value, name) {
654
740
  // `Object.getOwnPropertyNames` read past, and the record keeps its FOUR declared members.
655
741
  export const COMMIT_LOG = Symbol.for('provident-storeroom.commit-log');
656
742
 
743
+ // ---------------------------------------------------------------------------------------------
744
+ // **⟶ THE HANDOFF'S OWN SEAL — `D-55` ITEM 6, `DEF-AO`, ADDED 2026-10-28.** The architect, verbatim:
745
+ // *"Attempts don't log. This is what the commits are for. The commits have their log handed off to the client
746
+ // and are dropped after closing out."* **SO THE RECORD IS HANDED OFF ONCE, AT THE END OF THE COMMIT'S OWN
747
+ // PROCESSING, AND A RECORD THAT CHANGES AFTER THE HANDOFF IS NOT A RECORD.**
748
+ //
749
+ // **WHAT WAS MEASURED BEFORE THIS ACT EXISTED.** The drain amends the REPORT a pass already closed — it sets
750
+ // `outcome` and appends the refusal's entry to the report's list — and the list it appends to is THE COMMIT'S
751
+ // OWN, held by reference by every attempt of the commit, the crossed refusals included. A consumer that took
752
+ // the closed report (or a refusal carrying that list) before the drain therefore held a record that moved
753
+ // under it. **FREEZING AT THE SEAL MAKES THE HANDED-OFF RECORD FINAL**: the drain's amendment happens BEFORE
754
+ // this call (it is part of closing out), and nothing after it can append, renumber or re-member the list.
755
+ //
756
+ // **WHAT IT FREEZES, AND WHY THE LOG'S SEAL IS THE COMMIT'S.** `D-53` makes the message log THE COMMIT'S: an
757
+ // attempt's `log` is that one list, carried by reference, and every attempt of the commit appends to it — a
758
+ // constraint body's nested write, a repair's write, the re-run. **SO THE REPORT IS SEALED AT EVERY ATTEMPT'S
759
+ // HANDOFF AND THE LIST IS SEALED AT THE HANDOFF OF THE ATTEMPT WHOSE SCOPE OWNS IT** (the one whose commit is
760
+ // not an enclosing invocation's list, read off `commitLogInFlight`): freezing the list at a NESTED attempt's
761
+ // handoff would stop the enclosing attempt's own `append` and turn a lawful write into a raw host `TypeError`
762
+ // — measured, and the reason the `shared` argument exists. **THE LIST IS SEALED WITH ITS COMMIT**, which is
763
+ // what *"the commits have their log handed off to the client and are dropped after closing out"* says.
764
+ //
765
+ // **IT FREEZES TWO THINGS AND ONLY TWO**: the report's `log` array (where this attempt's scope owns it) and
766
+ // the report itself. **NO MEMBER MOVES, NO MEMBER IS ADDED, AND THE LIST'S IDENTITY IS UNCHANGED** — `D-53`'s
767
+ // *"the crossed report IS that list"* is a statement about WHICH array crosses, and the same array crosses
768
+ // (frozen): `K-5`, `K-4` and every refusal built from it read the same entries at the same `seq` ordinals.
769
+ // **A PASSING ATTEMPT'S REPORT IS SEALED TOO** — its log is handed off exactly once and drops with the commit.
770
+ export function sealHandoff(report, shared) {
771
+ if (report === null || typeof report !== 'object') return report;
772
+ // THE REPORT'S OWN `log` MEMBER, READ AS A MEMBER OF THE OBJECT THIS FRAME BUILT (`kernel-terminal.mjs`
773
+ // places the commit's list there and nothing else): an absent or non-array member is not this act's, and
774
+ // `refusalLog`'s own rule (`surface.md §2.5` item 4) is that a `log` is a list on every arm.
775
+ const log = report.log;
776
+ if (shared !== true && Array.isArray(log) === true) Object.freeze(log);
777
+ Object.freeze(report);
778
+ return report;
779
+ }
780
+
657
781
  // **THE COMMIT'S FRAME — THE ONE STRUCTURE THAT MAKES "THE SAME COMMIT" A FACT RATHER THAN A CONVENTION.** It is
658
782
  // a per-instance stack on the kernel record (`kernel.frames`, created by `kernelFor`), pushed by the kernel
659
783
  // around each invocation IT makes of caller code that belongs to an attempt — a constraint body at a landing,
@@ -851,31 +975,57 @@ function opSpelling(value) {
851
975
  * replacement for `byAddress`'s index, declared at `contract.md §2.8` item 6 (`HL-DD-6`) and
852
976
  * `kernel-selection.md §3` item 1.
853
977
  *
854
- * **THE SET IS THE PAIRS WHOSE OWN ADDRESS IS THE ATTEMPT'S — selected by an EXACT-ADDRESS match and NEVER
855
- * widened to `triggered`'s prefix set** (`P: docs/pending.md · (AA-1)`, verbatim: *"the pass selects the rows
978
+ * **⟶ THE PREDICATE IS WIDENED FROM EXACT ADDRESS TO PREFIX, 2026-10-29 — `D-56`, AND THE CLAUSE BELOW STANDS
979
+ * BYTE-FOR-BYTE AS THE RULE THIS ONE SUPERSEDES.** `RCA-8(g)` ANNOTATE-BESIDE: **the paragraph that stood here
980
+ * read** *"THE SET IS THE PAIRS WHOSE OWN ADDRESS IS THE ATTEMPT'S — selected by an EXACT-ADDRESS match and NEVER
981
+ * widened to `triggered`'s prefix set"* (`P: docs/pending.md · (AA-1)`, verbatim: *"the pass selects the rows
856
982
  * whose address IS the triggering address, which PRESERVES the EXACT-ADDRESS RULE rather than widening it to
857
- * `triggered`'s prefix set"*; `contract.md §2.8` item 1).
983
+ * `triggered`'s prefix set"*; `contract.md §2.8` item 1), **and the architect has superseded it**, verbatim:
984
+ * *"A listener/constraint should fire when the descendants of its targeted address node change, because the
985
+ * descendants become part of the object that is read from that address"* (`strata/decisions.md` · `D-56`).
986
+ * **THE TWO CLAUSES `D-56` NAMES BY NUMBER ARE `HL-8` ITEM 1'S OWN SENTENCE AND
987
+ * `attempt-lifecycle.md §2.4` ITEM 5 (iv)'s EXACT-ADDRESS RULE — both superseded, both annotated in place, and
988
+ * neither rewritten.**
989
+ *
990
+ * **THE SET IS NOW THE PAIRS WHOSE OWN ADDRESS IS A PREFIX OF THE ATTEMPT'S — equality included — WHICH IS
991
+ * `isAddressPrefix(standing, address)` AND IS `D-56`'s ONE SENTENCE**: a pair at `['tabs']` IS asked for a
992
+ * delete at `['tabs','landingPage']`, a pair at `['tabs','landingPage']` IS asked for that same delete, and a
993
+ * pair at `['tabs','landingPage','x']` is NOT asked because the act did not change it (**the unruled direction,
994
+ * measured and not implemented** — `D-56`'s own terms). **THE REASON IS THE READ:** the value read at a prefix
995
+ * contains its subtree, so a descendant's change changes what the ancestor's read returns, and the ancestor pair
996
+ * **judges an object that moved.**
858
997
  *
859
- * **IT IS A SCAN, AND THE COST IS PRICED AND ACCEPTED** (`P: docs/pending.md · (AA-1)`, verbatim: *"selecting
860
- * rows by address is a scan over `kernel.pairs` … THAT GROUND WAS WRONG AND IS WITHDRAWN: the store names the
861
- * ADDRESS, and the kernel selects"*).
998
+ * **IT IS STILL A SCAN, AND THE COST IS STILL PRICED AND ACCEPTED** (`P: docs/pending.md · (AA-1)`, verbatim:
999
+ * *"selecting rows by address is a scan over `kernel.pairs` … THAT GROUND WAS WRONG AND IS WITHDRAWN: the store
1000
+ * names the ADDRESS, and the kernel selects"*) — **and `D-56` re-prices it not at all**: the scan reads one
1001
+ * prefix test per standing address instead of one equality test per standing address, on the same rows.
862
1002
  *
863
- * **AND THE RESULT IS MATERIALISED ONCE, WHICH IS WHAT IMPLEMENTS `CX-12`'s *"the pass's evaluated set is
864
- * FIXED WHEN THE PASS BEGINS"*** (`P: docs/pending.md · R-B3`'s snapshot clause; `contract.md §2.8` item 6):
865
- * the returned list is a fresh array, so a `K-1` that returns DURING the pass enters the NEXT pass's set and
866
- * no earlier one, and a pair PURGED mid-pass is still evaluated by the pass that began with it
867
- * (`kernel-selection.md §3` item 5).
1003
+ * **AND THE RESULT IS STILL MATERIALISED ONCE, WHICH IS WHAT IMPLEMENTS `CX-12`'s *"the pass's evaluated set is
1004
+ * FIXED WHEN THE PASS BEGINS"*** (`P: docs/pending.md · R-B3`'s snapshot clause; `contract.md §2.8` item 6;
1005
+ * `D-56`'s third deferrer, which changes WHAT is materialised and not WHEN): the returned list is a fresh array,
1006
+ * so a `K-1` that returns DURING the pass enters the NEXT pass's set and no earlier one, and a pair PURGED
1007
+ * mid-pass is still evaluated by the pass that began with it (`kernel-selection.md §3` item 5). **THE PASS READS
1008
+ * THIS LIST AND NEVER THE REGISTRY**, and **`D-56` says nothing that moves that**: the ancestors are members of
1009
+ * the SAME one snapshot, taken at the SAME instant.
868
1010
  *
869
- * **THE ORDER IS THE PAIR'S FIRST-REGISTRATION ORDER AT THE ADDRESS** — `attempt-lifecycle.md:193`'s *"clause
870
- * (ii)'s first-registration order"* — **and `kernel.pairs`' own `Map` iteration supplies it**, which is the
871
- * order the ruling says is preserved *"by the store's own returned order, not by an index's append order"*
872
- * (`P: docs/pending.md · M-3`, its closing sentence; `contract.md §2.8` item 6's `HL-DD-6`).
1011
+ * **THE ORDER IS STILL THE PAIR'S FIRST-REGISTRATION ORDER — `HL-8` ITEM 6's RULE SURVIVES `D-56` — AND THE
1012
+ * ANCESTORS INTERLEAVE INTO THAT ONE ORDER RATHER THAN FORMING A SECOND.** `attempt-lifecycle.md:193`'s *"clause
1013
+ * (ii)'s first-registration order"* **is supplied by `kernel.pairs`' own `Map` iteration**, which is the order
1014
+ * the ruling says is preserved *"by the store's own returned order, not by an index's append order"*
1015
+ * (`P: docs/pending.md · M-3`, its closing sentence; `contract.md §2.8` item 6's `HL-DD-6`). **THE WIDENING ADDS
1016
+ * NO ITERATION AND NO SORT**: `D-39`'s registration sequence is the `Map`'s insertion order over the pair rows,
1017
+ * the one loop below walks it once, and **a row is admitted or not by its own `addresses` member** — so an
1018
+ * ancestor registered FIRST is asked FIRST, an ancestor registered THIRD is asked THIRD, **interleaved with the
1019
+ * act's own pairs by the one sequence both stand in.** There is no "ancestors first", no "ancestors last", and
1020
+ * no second list: **one order, one list, one snapshot.** **EACH ELEMENT IS EVALUATED AT MOST ONCE** — the `break`
1021
+ * below is that rule, so a pair standing at N addresses of which two are prefixes of the act is reached once
1022
+ * (`contract.md §2.6` item 5).
873
1023
  */
874
1024
  export function pairsAtAddress(kernel, address) {
875
1025
  const out = [];
876
1026
  for (const [key, pair] of kernel.pairs) {
877
1027
  for (const standing of pair.addresses) {
878
- if (sameAddress(standing, address)) {
1028
+ if (isAddressPrefix(standing, address)) {
879
1029
  appendOwn(out, key);
880
1030
  break;
881
1031
  }
@@ -521,11 +521,18 @@ function registrationRerun(kernel, attempt, produced) {
521
521
  // **THE ADDRESS IS IN HAND ONE FRAME ABOVE the `byAddress.get` call that stood here**, and the store→kernel
522
522
  // channel is `record` alone (`A117` (D6) forbids a fifth member).
523
523
  //
524
- // **AND THE SELECTION IS AN EXACT-ADDRESS MATCH, NEVER A PREFIX ONE**: the pass's set is the pairs WHOSE OWN
525
- // ADDRESS IS THE ATTEMPT'S, which preserves `attempt-lifecycle.md §2.4` item 5 (iv)'s exact-address rule
526
- // rather than widening it to the store's prefix-selected `triggered` (`P: docs/pending.md · (AA-1)`;
527
- // `contract.md §2.8` item 1). **EACH ELEMENT IS EVALUATED AT MOST ONCE**, so a pair standing at N addresses
528
- // whose two addresses both match is reached once (`contract.md §2.6` item 5).
524
+ // **AND THE SELECTION IS A PREFIX MATCH ON A PAIR'S OWN ADDRESS, AS OF 2026-10-29 (`D-56`).** `RCA-8(g)`
525
+ // ANNOTATE-BESIDE: **the sentence that stood here read** *"the pass's set is the pairs WHOSE OWN ADDRESS IS THE
526
+ // ATTEMPT'S, which preserves `attempt-lifecycle.md §2.4` item 5 (iv)'s exact-address rule rather than widening
527
+ // it to the store's prefix-selected `triggered`"* (`P: docs/pending.md · (AA-1)`; `contract.md §2.8` item 1) —
528
+ // **and `D-56` supersedes that rule**: *"A listener/constraint should fire when the descendants of its targeted
529
+ // address node change, because the descendants become part of the object that is read from that address"*.
530
+ // **SO THE SET HERE IS `pairsAtAddress`'s AND MOVES WITH IT** — the pairs whose own address is a PREFIX of the
531
+ // attempt's, equality included — because a registration's re-run is not a second kind of pass and mints no
532
+ // second membership rule. **THE WIDENING IS THE FUNCTION'S AND NOT THIS MODULE'S**: the call below is byte-for-
533
+ // byte what it was, and the registration instant inherits `D-56` by CALLING the one selection function.
534
+ // **EACH ELEMENT IS EVALUATED AT MOST ONCE**, so a pair standing at N addresses whose two addresses are both
535
+ // prefixes of the act is reached once (`contract.md §2.6` item 5).
529
536
  const list = pairsAtAddress(kernel, attempt.address);
530
537
  for (const key of list) {
531
538
  const pair = kernel.pairs.get(key);
@@ -549,6 +556,12 @@ function registrationRerun(kernel, attempt, produced) {
549
556
  // **⟶ ADDED 2026-10-26 (`DEF-S`, the same ruling): a body thrown at the synthetic attempt's re-run is
550
557
  // marked at this frame too — the mark carries the attempt's own log and address, and the entry's
551
558
  // crossing converts a marked value only** (`markBodyThrow`, `kernel-foundations.mjs`).
559
+ // **⟶ AND NEITHER THIS FRAME NOR THE REGISTRATION-TIME EVALUATION ABOVE APPENDS A THROWABLE ENTRY, 2026-10-28
560
+ // — `DEF-AK`'s RECORDED ARM IS THE LANDING'S.** The re-run appends no `constraint-check` entry for the
561
+ // evaluation it threw in (the pair's own function ran at the registration instant), and no `outcome`
562
+ // entry stands because this throwable propagates before any terminal act — so there is no entry of the
563
+ // closed four this frame could honestly extend, and no fifth `kind` is minted for it. The registration
564
+ // instant's `log` is `surface.md §2.5` item 4's declared `[]` and stays it (`DEF-S`'s own cell reads it).
552
565
  throw markBodyThrow(body, attempt.address, attempt.log);
553
566
  } finally {
554
567
  leaveCommitFrame(kernel);