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.
@@ -6,7 +6,7 @@ import { arrayClass, classSpelling, hasLoneSurrogate, isPlainObjectValue, target
6
6
  import { encodeAddress, encodeOpId, handleWire, inDomainHandle } from './carrier.mjs';
7
7
  import { decodeSegments, spellConstraintAddress } from './frame.mjs';
8
8
  import { addressSpelling } from './kernel-foundations.mjs';
9
- import { entryOf, carryRefusal, commitFile, homeAddressKey, externalCall, SETTLED_ON_THE_PASSING_ARM, SAVE_FAILED_AT_THE_TERMINAL_ARM } from './state.mjs';
9
+ import { entryOf, carryRefusal, commitFile, homeAddressKey, externalCall, commitLogOf, SETTLED_ON_THE_PASSING_ARM, SAVE_FAILED_AT_THE_TERMINAL_ARM } from './state.mjs';
10
10
 
11
11
  /// `PAR-JS-13`: the returned record is `{op, watched, triggered}` — fresh on every call, never a view.
12
12
  ///
@@ -555,7 +555,13 @@ export function terminalArm(state, record) {
555
555
  // receiver … A listener that is itself a store member is NOT supported"* — the package's own internal
556
556
  // paths (this one and `registerOn`'s re-install) address the store they hold, and only a CALLER's call
557
557
  // takes its receiver from `this` (`§1`).
558
- carryRefusal(() => commitFile(state.receiver, answer.op.opId), [encodeOpId(answer.op.opId)]);
558
+ // **⟶ AND THE COMMIT'S OWN LIST GOES WITH THE CALL — `D-53`, 2026-10-28.** A save that fails is a constraint
559
+ // violated at the write's LAST position, so its refusal must cross carrying the COMMIT's entries (the pass's
560
+ // `constraint-check` entries and, by the time the caller reads it, the kernel's one `outcome` entry with the
561
+ // save arm's own `KC-9` in `state`). **THE LIST IS READ OFF THE RECORD THE KERNEL BUILT** (`commitLogOf(record)` — `record[COMMIT_LOG]`,
562
+ // a symbol key on `CommitRecord`'s own identity — no member is added, `A117` (D6)), and a record carrying none
563
+ // is a record this package did not build, whose failure keeps the declared `[]`.
564
+ carryRefusal(() => commitFile(state.receiver, answer.op.opId), [encodeOpId(answer.op.opId)], commitLogOf(record));
559
565
  } catch (saveFailure) {
560
566
  // **THE FAILURE CARRIES THE VERY THROWABLE AND ITS OWN MARK** (`docs/specs/storeroom-js/surface.md §2.11` clause 9): the kernel's catch
561
567
  // reads `SAVE_FAILED_AT_THE_TERMINAL_ARM` to tell a failed save from a body of the delivered set that
@@ -1,5 +1,5 @@
1
1
  // `D-13` (`docs/L1/kernel-subdivision.md` §1 part `9d`) — the pass: bytes moved from `bindings/js/src/constraint-kernel.mjs`; no logic changed.
2
- import { KC, KC_TOKENS, SETTLED_ON_THE_PASSING_ARM, SAVE_FAILED_AT_THE_TERMINAL_ARM, TERMINAL_ARM, markBodyThrow, refuse, isKernelRefusal, pairSpelling, addressSpelling, sameAddress, kernelFor, append, appendOwn, ownMember, withdraw, landedOp, landedOpDefect, pairsAtAddress, externalCall } from './kernel-foundations.mjs';
2
+ import { KC, KC_TOKENS, SETTLED_ON_THE_PASSING_ARM, SAVE_FAILED_AT_THE_TERMINAL_ARM, TERMINAL_ARM, markBodyThrow, refuse, isKernelRefusal, pairSpelling, addressSpelling, sameAddress, kernelFor, append, appendOwn, ownMember, withdraw, landedOp, landedOpDefect, pairsAtAddress, enterCommitFrame, leaveCommitFrame } from './kernel-foundations.mjs';
3
3
  import { commitAttemptIdOf, terminal, close } from './kernel-terminal.mjs';
4
4
  // ---------------------------------------------------------------------------------------------
5
5
  // `K-3` `runAttempt(store, commit) -> AttemptReport`
@@ -37,6 +37,35 @@ const COMMIT_SCOPE = Symbol.for('provident-storeroom.commit-scope');
37
37
  /// (`§1` item 2: `K-1`–`K-5` and no sixth name).
38
38
  const RESTART_DRAIN = Symbol.for('provident-storeroom.restart-drain');
39
39
 
40
+ /// **`D-54` (b)'s SECOND SPELLING OF THE SAME `Symbol.for` KEY** — the entry writes the restarted check's
41
+ /// refusal onto the report under it (`constraints.mjs`'s `carryRestartRefusal`) and this module reads it back
42
+ /// so the failing arm can answer with the very value the terminal judgement built, exactly as the passing arm
43
+ /// reaches it through `constraintWrite`. **NEITHER SIDE IMPORTS THE OTHER** (`kernel-surface.md §1` item 1's
44
+ /// empty import footprint), the key is invisible to `Object.keys` and to `Object.getOwnPropertyNames`
45
+ /// (`A117` (D6)), and no kernel item is minted (`§1` item 2: `K-1`–`K-6` and no seventh name).
46
+ const RESTART_REFUSAL = Symbol.for('provident-storeroom.restart-refusal');
47
+
48
+ /// **⟶ `D-54` (b)'s COMPENSATION FLAG — the same `Symbol.for` key `constraints.mjs` spells.** The kernel's
49
+ /// terminal act composes the attempt's rollback THROUGH THE APP'S PUBLISHED `rollback` MEMBER
50
+ /// (`kernel-terminal.mjs`'s `composeRollback`), so that call would otherwise take a restart watch and raise a
51
+ /// request — a phantom the failing pass's drain would then judge, asking pairs at the state the compensation
52
+ /// had just restored to. **`D-42` declares the rule the flag keeps: *"a restart is only ever raised by a
53
+ /// consumer's own `rollback` member call."*** The flag stands on the kernel record for the duration of the one
54
+ /// call, is a symbol key (invisible to `Object.keys`/`Object.getOwnPropertyNames`, `A117` (D6)), and mints no
55
+ /// kernel item (`kernel-surface.md §1` item 2).
56
+ const COMPENSATING = Symbol.for('provident-storeroom.compensating-rollback');
57
+
58
+ /// **THE KERNEL'S ONE TERMINAL ACT, TAKEN WITH ITS COMPENSATION FLAGGED** — the wrapper every terminal call in
59
+ /// this module uses so the composition inside `terminal` is never mistaken for an action of the caller's.
60
+ function terminalComposed(kernel, attempt, status, message) {
61
+ kernel[COMPENSATING] = true;
62
+ try {
63
+ terminal(kernel, attempt, status, message);
64
+ } finally {
65
+ kernel[COMPENSATING] = false;
66
+ }
67
+ }
68
+
40
69
  /// **⟶ ADDED 2026-10-27 — `D-46`'s NAME ON AN INVOCATION'S OWN ENTRY, AND IT IS THIS PASS'S ONE USE OF THE
41
70
  /// SEAM'S SIXTH CHANNEL.** **THE RULING, THE ARCHITECT, VERBATIM: *"A constraint's repair needs to stay as part
42
71
  /// of the same commit so that it can be tracked as part of the commit process … The original way of maintaining
@@ -69,6 +98,28 @@ function repairInCommit(store, commit, run) {
69
98
  return run();
70
99
  }
71
100
 
101
+ /// **⟶ AND THE CONSTRAINT BODY'S INVOCATION NAMES THE ATTEMPT'S COMMIT TOO — `D-53`, 2026-10-28.**
102
+ ///
103
+ /// **THE RULING, VERBATIM: *"Constraints/Repairs should always use the same commit"*** (`strata/decisions.md` ·
104
+ /// `D-53`). **THE INVARIANT THIS FRAME NOW KEEPS IS `D-45`'s *"it should be opening a new commit by default of
105
+ /// entering at the surface"* READ THROUGH `D-53`'s LATER AND NARROWER ONE: a call that NAMES a commit JOINS it,
106
+ /// and a call that names none opens one — and the kernel names the attempt's commit at EVERY invocation of
107
+ /// caller code that belongs to the attempt, a constraint body's as well as a repair body's** (`D-46`'s device,
108
+ /// applied to the second kind of body). **SO A CONSTRAINT NEVER OPENS A COMMIT: the check it is invoked for is
109
+ /// already inside one (`D-44`'s scope) and this name is what makes its verb JOIN that window instead of opening
110
+ /// a second.** **THE LOG IS THE SAME FACT SEEN FROM THE OTHER SIDE**: the entries such a verb mints are the
111
+ /// attempt's own commit's entries, which is what `DEF-AK` measures — *"one inner refusal, two hosts"* — and what
112
+ /// `D-53` dissolved `D-52`'s merge question with (*"the crossed report IS that list"*).
113
+ ///
114
+ /// **A LISTENER BODY STILL NAMES NOTHING, AND THAT IS THE INVARIANT AND NOT AN OMISSION**: it is invoked by the
115
+ /// ENTRY at the terminal arm, after the triggering commit has passed its constraints, so *"what the listener
116
+ /// does is a NEW QUESTION and must not inherit the old verdict"* — its own commit, its own list.
117
+ function constraintInCommit(store, commit, run) {
118
+ const channel = store !== null && typeof store === 'object' ? store[EXTERNAL_CALL] : undefined;
119
+ if (typeof channel === 'function') return channel(run, commit);
120
+ return run();
121
+ }
122
+
72
123
  export function runAttempt(store, commitRecord) {
73
124
  // THE SEAM, READ BEFORE ANY STATE MOVES: **a store this package did not build carries no such channel, so it
74
125
  // is not this package's store and no pass of this kernel may run against it.** The refusal is thrown BEFORE
@@ -91,12 +142,51 @@ export function runAttempt(store, commitRecord) {
91
142
  // open — a direct `K-2`/`K-3` drive, `D-44`'s window. **NO COUNT IS MINTED EITHER WAY**: the channel is
92
143
  // `openCommit`/`closeCommit` composed, and the count moves only where this call's own opening is the one that
93
144
  // took it.
145
+ // **⟶ AND A FAILING PASS'S TERMINAL STATE IS STILL A JUDGEMENT — `D-54` (b), 2026-10-28, MEASURED.** The bytes
146
+ // above reached the drain only where the pass ANSWERED A REPORT, so a pass that ended by throwing — a repair
147
+ // body that reversed the store had then broken the attempt's own re-run, or a delete's pass whose pair
148
+ // refused after its delivery's reversal — left the request its bodies raised standing, unjudged, with its
149
+ // effect in the store. **THE REQUEST IS CARRIED TO THE PASS'S TERMINAL JUDGEMENT**: the terminal act has run
150
+ // and the attempt is closed, so the state the pass left is the state the restarted check is asked at, and the
151
+ // drain runs here, inside the same scope, exactly as it does on the passing arm. **THE FAILING PASS'S OWN
152
+ // ANSWER IS NOT RE-SHAPED**: where the restarted check passes, or where the throwable is not the kernel's own
153
+ // (a caller body's value, a store refusal — each of which must cross as itself), the pass's throwable crosses
154
+ // unchanged. Only a KERNEL refusal — the pass's own terminal arm — is superseded by the refusal the restarted
155
+ // check built at that same terminal state, because that check's verdict is the state the pass left.
94
156
  return scope(() => {
95
- const report = passInCommitScope(store, commitRecord);
157
+ let report;
158
+ try {
159
+ report = passInCommitScope(store, commitRecord);
160
+ } catch (thrown) {
161
+ return drainFailingPass(store, commitRecord, thrown);
162
+ }
96
163
  return drainRestart(store, report);
97
164
  }, commitRecord);
98
165
  }
99
166
 
167
+ /// **THE FAILING PASS'S DRAIN — `D-54` (b)'s ONE NEW CALL SITE.**
168
+ ///
169
+ /// **WHY THE REPORT IS READ AND NOT BUILT**: the pass's terminal act (`kernel-terminal.mjs`'s `terminal`) has
170
+ /// already answered the attempt's ONE five-member report and placed it in the kernel's closed map before it
171
+ /// throws, so the log the drain re-appends onto is the attempt's own list and no second report is minted
172
+ /// (`A117` (D6): no member is added to any record). **A THROWABLE THAT STOOD BEFORE ANY TERMINAL ACT — `KC-2`,
173
+ /// a re-entrant `KC-7`, a store refusal at `K-2` — has no closed report and no request of this pass's window
174
+ /// either**, so it crosses unchanged and the drain is not reached: no pass ran.
175
+ ///
176
+ /// **THE ONE PRECEDENCE RULE, STATED ONCE**: a restarted check's refusal supersedes the pass's answer only
177
+ /// where that answer is the KERNEL's own (a `KC`-tokened refusal, `isKernelRefusal`). A constraint body's
178
+ /// throwable is the caller's and crosses as itself (`fail-surface.md §1.1` item `5`), a store verb's refusal
179
+ /// crosses verbatim (`§1.2` item `2`), and neither is re-shaped by a later check's verdict — the request is
180
+ /// still judged and the store still restored by that judgement's own compensating act.
181
+ function drainFailingPass(store, commitRecord, thrown) {
182
+ const closed = kernelFor(store).closed.get(commitAttemptIdOf(commitRecord));
183
+ if (closed === undefined) throw thrown;
184
+ const answered = drainRestart(store, closed);
185
+ const restarted = answered !== null && typeof answered === 'object' ? answered[RESTART_REFUSAL] : undefined;
186
+ if (restarted !== undefined && isKernelRefusal(thrown) === true) throw restarted;
187
+ throw thrown;
188
+ }
189
+
100
190
  /// **THE DRAIN, TAKEN INSIDE THE SCOPE THE PASS JUST ENDED IN.** It hands the store **the report the pass
101
191
  /// answered with** and answers the report the kernel returns: unchanged where no request stood or the
102
192
  /// restarted check passed, and amended — `outcome` `'failed'` and the refusal's entry in the log — where the
@@ -152,7 +242,7 @@ function passInCommitScope(store, commitRecord) {
152
242
  if (terminalArm !== null) {
153
243
  // A FAILING LANDING: the arm's own terminal act, its per-op rollback composition, and the throw
154
244
  // of `fail-surface.md §1.1`'s form carrying the log.
155
- terminal(kernel, attempt, terminalArm.status, terminalArm.message);
245
+ terminalComposed(kernel, attempt, terminalArm.status, terminalArm.message);
156
246
  // **⟶ INSTANT (b) ON THE FAILING ARM, AND IT PRECEDES THE REFUSAL** (`docs/decisions.md` · `A116`
157
247
  // (D1); `surface.md §2.4` item 9's table row (b): *"AT THE PASS'S TERMINAL ARM … after the last
158
248
  // constraint of the attempt has been evaluated and before `runAttempt` returns to the calling
@@ -239,7 +329,16 @@ function passInCommitScope(store, commitRecord) {
239
329
  // `'ok'`; where the failed save's own rollback took it the store answers `JournalOpUnknown` and the
240
330
  // composition's floor reads `KC-8` in the ONE `outcome` entry (item 5 (b); item 6 (a)).
241
331
  attempt.checkpoint = null;
242
- terminal(kernel, attempt, KC.violated,
332
+ // **⟶ AND THE ARM'S TOKEN IS THE SET'S NINTH MEMBER, `KC-9` — `D-53`'s THIRD RULING, 2026-10-28: *"Open the
333
+ // token set"*.** **`D-52` MEASURED THE CONTRADICTION THIS CLOSES**: *"a constraint refusal crosses
334
+ // `ConstraintPassRefused` with terminal `state: 'KC-3'`; and with an in-body reversal meeting the floor,
335
+ // `ConstraintPassRefused` with terminal `KC-8`; a save failure crosses `FileCommitFailed` with `log: []`, and
336
+ // the attempt's own log reads `KC-8` — the same token a passing-pair run carries. **SO `KC-8` CROSSES AS BOTH
337
+ // STATUSES.**"* **THE SAVE ARM THEREFORE GETS A TOKEN OF ITS OWN**, so that a save failure and a constraint
338
+ // refusal cross with different statuses AND each is derivable from the log's terminal state — **and
339
+ // `kernel-terminal.mjs`'s precedence clause is what keeps this token through the floor**, which is otherwise
340
+ // `KC-8` on both arms and would put the contradiction back.
341
+ terminalComposed(kernel, attempt, KC.saveFailed,
243
342
  typeof saveFailure.message === 'string'
244
343
  ? saveFailure.message
245
344
  : `the attempt's own save at ${addressSpelling(attempt.address)} failed`);
@@ -257,7 +356,10 @@ function passInCommitScope(store, commitRecord) {
257
356
  // stand in one log). **AND NOTHING IS APPENDED TO THIS LOG AFTER IT**: the delivered set's bodies ran
258
357
  // before this line and reach this attempt's log by no act (`§2.7` item 6), the record this attempt's
259
358
  // report carries being the same list this line closes.
260
- append(attempt, 'commit', null, attempt.ops[attempt.ops.length - 1].opId,
359
+ // **⟶ AND ITS ITERATION IS `{ pair: null, op }` — the `commit` entry names NO PAIR and the attempt's LAST OP**
360
+ // (`kernel-surface.md §2.1` item 3's `LogKind` table; `D-53`'s first ruling, which is what the iteration position
361
+ // carries).
362
+ append(attempt, 'commit', { pair: null, op: attempt.ops[attempt.ops.length - 1] },
261
363
  `every constraint registered at ${addressSpelling(attempt.address)} passed; the commit stands`);
262
364
  return closed;
263
365
  }
@@ -383,24 +485,30 @@ function evaluate(kernel, attempt, landing) {
383
485
  // THE ENTRY IS OPENED BEFORE THE BODY RUNS, so a body that reads the record's own log mid-flight
384
486
  // reads its own evaluation's entry — and it is WITHDRAWN where the body returns nothing
385
487
  // (`kernel-surface.md §2.5` item 1; `PAR-C-6`).
386
- append(attempt, 'constraint-check', pair.id, op.opId, `the evaluation of ${pair.id}`);
488
+ // **⟶ AND IT IS NAMED BY THE ITERATION — the pair AND the op, one identity — `D-53`'s FIRST RULING**
489
+ // (`kernel-foundations.mjs`'s `append`; `D-52`'s rule 1: *"an entry carries the PAIR identity and the OP
490
+ // identity together … they are one identity"*).
491
+ append(attempt, 'constraint-check', { pair, op }, `the evaluation of ${pair.id}`);
387
492
  const check = attempt.log[attempt.log.length - 1];
388
493
  let outcome;
494
+ // **⟶ AND THE FRAME: every list the body's own verbs append to between these two lines is THIS commit's
495
+ // list** (`D-53`'s first invariant). The frame is pushed for exactly the invocation and popped in a
496
+ // `finally`, so a throwable leaves no frame standing and no later write of this commit inherits a frame it
497
+ // does not own.
498
+ enterCommitFrame(kernel, attempt.log);
389
499
  try {
390
- // **⟶ AND THE BODY IS INVOKED WITH NOTHING NAMED, 2026-10-27 (`D-45`, `D-46` item `1`).** A constraint
391
- // body is EXTERNALLY-SUPPLIED FUNCTION code entering the surface, **and it has no commit in hand to
392
- // name** — it is handed the five-member `AttemptContext` and no commit at all (`kernel-surface.md §2.3`
393
- // item 1) — so **a verb it calls OPENS ITS OWN COMMIT** rather than joining the one this pass runs in:
394
- // *"It should be opening a new commit by default of entering at the surface"*. **THE NAME IS AN
395
- // INVOCATION'S AND NOTHING ELSE'S**: it is set before the call and restored after it, on the throwing arm
396
- // too, and the kernel's own acts of this pass name the pass's commit through `COMMIT_SCOPE` — so an
397
- // ordinary write still opens ONE commit and a callback's verb is still an act of its own.
398
- outcome = externalCall(attempt.store, () => pair.constraint(context));
500
+ // **⟶ AND THE BODY IS INVOKED WITH THE ATTEMPT'S COMMIT NAMED, 2026-10-28 (`D-53`).** `D-45` invoked it
501
+ // with nothing named and `D-46` narrowed that for repairs alone; **`D-53` states the invariant both halves
502
+ // obey: a CONSTRAINT never opens a commit and a REPAIR never opens one, while a listener's write always
503
+ // does.** The name is an INVOCATION's and nothing else's: it is set before the call and restored after it,
504
+ // on the throwing arm too, so an ordinary write still opens ONE commit and a callback's verb is still an
505
+ // act of its own.
506
+ outcome = constraintInCommit(attempt.store, attempt.record, () => pair.constraint(context));
399
507
  } catch (body) {
400
508
  // A body that threw returned nothing, so no `constraint-check` entry stands and the attempt is
401
509
  // closed (`PAR-C-6`; `NX-2`; `composition-and-states.md §2.7` item 13).
402
510
  withdraw(attempt, check);
403
- terminal(kernel, attempt, null, `the constraint ${pair.id} threw; the attempt is closed`);
511
+ terminalComposed(kernel, attempt, null, `the constraint ${pair.id} threw; the attempt is closed`);
404
512
  // **⟶ AND THE VALUE IS MARKED HERE, BY THE FRAME THAT CAUGHT IT FROM THE BODY — `DEF-S`, 2026-10-26,
405
513
  // on the architect's ruling *"Expand the returned error set for both S/N"*.** The mark is what the
406
514
  // entry's `constraintCrossing` converts on, and an UNMARKED value still crosses as itself
@@ -409,6 +517,10 @@ function evaluate(kernel, attempt, landing) {
409
517
  // `kernel-foundations.mjs`). The throwable's own identity is unmoved at THIS layer: a kernel-level
410
518
  // caller still meets the very value the body threw.
411
519
  throw markBodyThrow(body, attempt.address, attempt.log);
520
+ } finally {
521
+ // THE FRAME GOES WITH THE INVOCATION, ON EVERY ARM (`D-53`): a frame left standing would make the NEXT
522
+ // write of this store join a commit it is not inside.
523
+ leaveCommitFrame(kernel);
412
524
  }
413
525
 
414
526
  // THE CONSTRAINT'S OWN OUTCOME — A CLOSED THREE-ARM SET (`PAR-C-5`). A value outside the three
@@ -487,7 +599,7 @@ function evaluate(kernel, attempt, landing) {
487
599
  // INSTANT IT IS APPENDED — "THE OP THE REPAIR PRODUCED WHERE IT PRODUCED ONE, ELSE THE OP IT WAS
488
600
  // INVOKED FOR" (`kernel-surface.md §2.5` item 1's `repair-run` row) — so on EVERY arm the entry
489
601
  // names THE OP THIS EVALUATION CARRIED, and only the arm that produced an op rewrites it below.
490
- append(attempt, 'repair-run', pair.id, op.opId, 'the pair\'s repair was attempted');
602
+ append(attempt, 'repair-run', { pair, op }, 'the pair\'s repair was attempted');
491
603
  const entry = attempt.log[attempt.log.length - 1];
492
604
  let result;
493
605
  const repairContext = {
@@ -529,6 +641,9 @@ function evaluate(kernel, attempt, landing) {
529
641
  // so a repair that supplies it at `write`'s fourth position names the same commit by its own hand, and
530
642
  // the two spellings cannot disagree: **the entry's position wins where it is supplied, and this name is
531
643
  // the fallback where it is not** (`state.mjs`'s `openCommit`).
644
+ // **⟶ AND THE FRAME STANDS FOR THIS INVOCATION TOO (`D-53`'s first invariant): the repair's own verbs
645
+ // append to the attempt's list, and the frame is what makes `K-2` join rather than open.**
646
+ enterCommitFrame(kernel, attempt.log);
532
647
  result = repairInCommit(attempt.store, attempt.record, () => repair(repairContext));
533
648
  } catch (body) {
534
649
  // THE REPAIR-RUN ENTRY IS APPENDED BEFORE THE THROW, and the attempt's outcome entry carries
@@ -575,7 +690,7 @@ function evaluate(kernel, attempt, landing) {
575
690
  if (storeCode !== null) {
576
691
  entry.message = `the repair ${pair.id} was attempted and the store refused its own edit — the `
577
692
  + `store's answer is ${storeCode}`;
578
- terminal(kernel, attempt, KC.repairFailed, entry.message);
693
+ terminalComposed(kernel, attempt, KC.repairFailed, entry.message);
579
694
  // **⟶ AND THE FORM THAT CROSSES TO THE CALLER IS THE KERNEL'S OWN** (`kernel-surface.md §2.3`
580
695
  // item 2 (iii): the attempt *"moves to `FAILING` with `KC-4`"*, and *"the thrown form's `status`
581
696
  // is NEVER a store member"*) — **the very form the FAILING arm above throws**, carrying the
@@ -584,8 +699,10 @@ function evaluate(kernel, attempt, landing) {
584
699
  // body's own arm and this conversion is the refused-verb's.
585
700
  refuse(attempt.throwState, attempt.throwMessage, attempt.log);
586
701
  }
587
- terminal(kernel, attempt, KC.repairFailed, `the repair ${pair.id} threw; the attempt is closed`);
702
+ terminalComposed(kernel, attempt, KC.repairFailed, `the repair ${pair.id} threw; the attempt is closed`);
588
703
  throw body;
704
+ } finally {
705
+ leaveCommitFrame(kernel);
589
706
  }
590
707
 
591
708
  // THE REPAIR'S OWN RESULT — A CLOSED TWO-ARM SET (`PAR-C-8`), WHOSE SUCCESS ARM'S THIRD MEMBER
@@ -1,5 +1,5 @@
1
1
  // `D-13` (`docs/L1/kernel-subdivision.md` §1 part `9c`) — the commit act: bytes moved from `bindings/js/src/constraint-kernel.mjs`; no logic changed.
2
- import { KC, refuse, inDomainAddress, kernelFor, attemptHeldAt, appendOwn, addressSpelling } from './kernel-foundations.mjs';
2
+ import { KC, refuse, inDomainAddress, kernelFor, attemptHeldAt, appendOwn, addressSpelling, commitLogInFlight, COMMIT_LOG } from './kernel-foundations.mjs';
3
3
  // ---------------------------------------------------------------------------------------------
4
4
  // `K-2` `commit(store, address, tier, value) -> CommitRecord`
5
5
  // ---------------------------------------------------------------------------------------------
@@ -53,6 +53,17 @@ export function commit(store, address, tier, value) {
53
53
  // `appendOwn`), and NO ANSWER MOVES: the same segments, the same order, a FRESH array as before.
54
54
  const addressCopy = [];
55
55
  for (let i = 0; i < address.length; i += 1) appendOwn(addressCopy, address[i]);
56
+ // **⟶ AND THE ATTEMPT'S LOG IS THE COMMIT'S LOG — `D-53`, 2026-10-28.** A write the kernel reaches from inside
57
+ // an invocation it made of caller code that BELONGS TO AN ATTEMPT (a constraint body's verb at a landing, a
58
+ // repair body's write, a registration's body) is part of that attempt's commit, so it JOINS that commit's one
59
+ // list; a write reached with no such frame standing — a caller's own write, and a LISTENER BODY's write, which
60
+ // the entry invokes at the terminal arm — OPENS a commit and starts a fresh list, because *"the triggering
61
+ // commit already passed constraints"* and what the listener does now is a new question (`D-53`).
62
+ // **ONE LIST PER COMMIT AND NO MERGE**: the entries a nested write mints are this list's entries, their `seq`
63
+ // is this list's ordinal, and the crossed report of the enclosing refusal IS this list (`D-52`'s rule 2 is
64
+ // dissolved rather than answered — there are not two lists to reconcile).
65
+ const joined = commitLogInFlight(kernel);
66
+ const commitLog = joined === null ? [] : joined;
56
67
  const attempt = {
57
68
  ref: { id: `attempt-${kernel.attempts}` },
58
69
  store,
@@ -65,7 +76,8 @@ export function commit(store, address, tier, value) {
65
76
  // reads `triggered` as the store's returned set VERBATIM).
66
77
  write: null,
67
78
  ops: [],
68
- log: [],
79
+ // THE COMMIT'S OWN LIST, BY REFERENCE (`D-53`): the attempt holds a reference to it and mints no second one.
80
+ log: commitLog,
69
81
  ran: [],
70
82
  appliedRepairs: [],
71
83
  rollback: [],
@@ -111,6 +123,18 @@ export function commit(store, address, tier, value) {
111
123
  pending: [],
112
124
  };
113
125
  attempt.record = record;
126
+ // **⟶ AND THE LOG TRAVELS ON THE COMMIT'S OWN RECORD, UNDER A SYMBOL KEY — `D-53`.** The record is the
127
+ // commit's JS identity: the fourth position names it, the entry adopts it, and the terminal arm holds it when
128
+ // it must tell a failed save's crossing WHICH LOG the commit's verdict stands in (`delivery.mjs`'s save arm
129
+ // reads it through `state.mjs`'s `commitLogOf`, which spells the same key). **NO MEMBER IS ADDED**: `Object.keys` and
130
+ // `Object.getOwnPropertyNames` read past a symbol (`A117` (D6)), so `CommitRecord` keeps its four members and
131
+ // every census of it moves by nothing.
132
+ Object.defineProperty(record, COMMIT_LOG, {
133
+ value: commitLog,
134
+ enumerable: false,
135
+ writable: false,
136
+ configurable: true,
137
+ });
114
138
  // **⟶ INSTANT (a)'S SEAM CALL IS WITHDRAWN, 2026-10-18** (`docs/specs/storeroom-constraints/kernel-surface.md
115
139
  // §2.1` item 2's clause of that date: *"the call of `settle(record)` THIS ITEM MAKES AT INSTANT (a) … IS
116
140
  // WITHDRAWN"*). **WITH ONE ACTOR THERE IS NOTHING TO WITHHOLD**: the record is built and complete BEFORE the
@@ -38,12 +38,23 @@
38
38
  // ---------------------------------------------------------------------------------------------
39
39
  // `fail-surface.md §2` — THE KERNEL'S OWN CLOSED STATE SET, `8` MEMBERS AND NO NINTH.
40
40
  // ---------------------------------------------------------------------------------------------
41
+ // **⟶ AND THE SET OPENS TO `9` ON 2026-10-28 — `D-53`'s THIRD RULING, THE ARCHITECT'S WORD: *"Open the token
42
+ // set"*** (`strata/decisions.md` · `D-53`; `D-52`'s rule 3 is its measurement: **`KC-8` CROSSED AS TWO
43
+ // STATUSES** — a constraint refusal whose composition met the floor, and a failed save — **so neither arm was
44
+ // derivable from the log's terminal state**). **THE SENTENCE ABOVE STANDS BYTE-FOR-BYTE AND IS READ THROUGH
45
+ // THIS ONE: `9` MEMBERS AND NO TENTH, ON `14` SITES.** **THE APPENDED MEMBER IS `KC-9`, THE SAVE ARM'S OWN —
46
+ // `ConstraintSaveFailed`** — so a save failure and a constraint refusal cross with DIFFERENT statuses AND each
47
+ // is derivable from the log's terminal state: the save arm's ONE `outcome` entry carries `KC-9` and a constraint
48
+ // refusal's carries `KC-3`/`KC-5`/`KC-6`/`KC-8` (`KC-9`'s own row, `kernel-attempt.mjs`'s save arm, and
49
+ // `kernel-terminal.mjs`'s precedence clause: **the FLOOR DOES NOT OVERWRITE THIS TOKEN, which is what keeps the
50
+ // two arms apart where the save's own composition meets a floor code**).
41
51
  // `fail-surface.md §1.1` ITEM 1 (a) — THE ONE SPELLING, AND IT IS THE TOKEN: a refusal's `status` carries
42
- // `KC-1`…`KC-8` and NOTHING ELSE. The full names (`ConstraintViolated` …) are LABELS: they keep exactly
52
+ // `KC-1`…`KC-9` and NOTHING ELSE. The full names (`ConstraintViolated` …) are LABELS: they keep exactly
43
53
  // one declared use — the `name` column of `§2`'s table, and a citation beside a token — and NO clause
44
54
  // spells a member's VALUE as a name. `status` and `LogEntry.state` therefore read THE SAME TOKEN STRING,
45
- // related by IDENTITY (`§1.1` item 1 (c)). THE CENSUS DOES NOT MOVE: `8` members (`2 + 2 + 2 + 1 + 1`)
46
- // on `13` sites (`1 + 4 + 4 + 3 + 1`).
55
+ // related by IDENTITY (`§1.1` item 1 (c)). THE CENSUS MOVES BY THE RULING AND BY NOTHING ELSE:
56
+ // `8 → 9` members (`2 + 2 + 2 + 1 + 1 + 1`) on `13 → 14` sites (`1 + 4 + 4 + 3 + 1 + 1`, the last being the
57
+ // save arm's own, `kernel-attempt.mjs`).
47
58
  export const KC = {
48
59
  address: 'KC-1', // ConstraintAddressRefused
49
60
  arg: 'KC-2', // ConstraintArgRefused
@@ -53,9 +64,10 @@ export const KC = {
53
64
  repeat: 'KC-6', // ConstraintRepeat
54
65
  attemptOpen: 'KC-7', // ConstraintAttemptOpen
55
66
  rollbackFloor: 'KC-8', // ConstraintRollbackFloor
67
+ saveFailed: 'KC-9', // ConstraintSaveFailed
56
68
  };
57
69
 
58
- /** The eight tokens above, as the ONE list a member's own value is checked against — the kernel's own
70
+ /** The nine tokens above, as the ONE list a member's own value is checked against — the kernel's own
59
71
  * token is NEVER named as a store code (`fail-surface.md §1.1` item 1 (a); `§2`'s `KC-4` row). */
60
72
  export const KC_TOKENS = Object.values(KC);
61
73
 
@@ -484,6 +496,11 @@ export function kernelFor(store) {
484
496
  live: new Map(),
485
497
  closed: new Map(),
486
498
  attempts: 0,
499
+ // **⟶ AND THE COMMIT'S FRAME, 2026-10-28 (`D-53`)**: the stack of attempts whose commit the kernel is, at
500
+ // this instant, invoking caller code inside. `K-2` reads its top and joins that attempt's list; a write
501
+ // reached with no frame standing OPENS a commit and starts its own list. It is the instance's own, exactly
502
+ // as `live`/`closed` are (`D-30`), so no two stores share a frame and no module-level stack exists.
503
+ frames: [],
487
504
  store,
488
505
  };
489
506
  // **THE SLOT IS DEFINED AND THE RECORD IS PUBLISHED IN THE SAME ACT** (`Object.defineProperty`, exactly as
@@ -613,16 +630,91 @@ export function ownMember(value, name) {
613
630
  return descriptor.value;
614
631
  }
615
632
 
633
+ // ---------------------------------------------------------------------------------------------
634
+ // **⟶ THE MESSAGE LOG BELONGS TO THE COMMIT, NOT TO THE ATTEMPT — `D-53`, 2026-10-28.**
635
+ //
636
+ // **THE ARCHITECT, VERBATIM: *"If it's part of the same commit, then it uses the same message log. If it opens a
637
+ // new commit, it uses the new commit's message log. Constraints/Repairs should always use the same commit.
638
+ // Listeners should open a new commit, because the triggering commit already passed constraints"***
639
+ // (`strata/decisions.md` · `D-53`). **A COMMIT IS THE UNIT OF WORK AND AN ATTEMPT IS THE CONSTRAINT-JUDGED WRITE
640
+ // INSIDE IT**, so a commit that holds several attempts — a check, its repair, the repair's own write, the re-run
641
+ // — holds ONE list, and every one of those attempts appends to it: **`attempt.log` IS the commit's list, carried
642
+ // by reference, and it is not a copy of anybody's.**
643
+ //
644
+ // **WHAT THAT DISSOLVES, SAID BECAUSE IT WAS ASKED AS A QUESTION.** `D-52`'s rule 2 asked where a nested
645
+ // attempt's report is merged into its host's, and how a merged entry keeps *"its own attempt identity"* when
646
+ // `seq` is *"the ordinal of the list it stands in"* and the six declared members leave no room for a seventh.
647
+ // **THERE WAS NEVER A MERGE TO PERFORM: a constraint body's write is part of the same commit, so its entries
648
+ // ARE this list's entries, the crossed report IS this list, no entry is renumbered and `seq` keeps its
649
+ // declaration because there is ONE list.** `DEF-AK`'s *"one inner refusal, two hosts"* is the measurement of it.
650
+ //
651
+ // **THE KEY IS A `Symbol.for` SPELLING ON THE COMMIT'S OWN RECORD, AND BOTH SIDES SPELL IT** (`A117` (D6)): the
652
+ // entry's terminal arm attaches this list to a failed save's crossing, and it holds the `CommitRecord` and
653
+ // nothing else — so the list travels ON the record under a symbol key that `Object.keys` and
654
+ // `Object.getOwnPropertyNames` read past, and the record keeps its FOUR declared members.
655
+ export const COMMIT_LOG = Symbol.for('provident-storeroom.commit-log');
656
+
657
+ // **THE COMMIT'S FRAME — THE ONE STRUCTURE THAT MAKES "THE SAME COMMIT" A FACT RATHER THAN A CONVENTION.** It is
658
+ // a per-instance stack on the kernel record (`kernel.frames`, created by `kernelFor`), pushed by the kernel
659
+ // around each invocation IT makes of caller code that belongs to an attempt — a constraint body at a landing,
660
+ // at a registration's evaluation, at its re-run; a repair body — and popped in a `finally`.
661
+ //
662
+ // **`K-2` READS IT AND NOTHING ELSE DECIDES THE LOG**: a write the kernel reaches from inside such an
663
+ // invocation JOINS the innermost frame's list; a write reached from anywhere else — a caller's own top-level
664
+ // write, and A LISTENER BODY'S WRITE, which the ENTRY invokes at the terminal arm with no frame of this
665
+ // kernel's standing — OPENS a commit and therefore starts a list of its own (`D-53`: *"Listeners should open a new
666
+ // commit, because the triggering commit already passed constraints"* — what the listener saw was already
667
+ // approved, so what it does now is a NEW QUESTION and must not inherit the old verdict).
668
+ export function enterCommitFrame(kernel, log) {
669
+ appendOwn(kernel.frames, { log });
670
+ }
671
+
672
+ /** The pop, and it is a local index loop for the reason every other list surgery here is (`contract.md §2.1`'s
673
+ * clause of 2026-10-24 on `F7-1`): no `Array.prototype.pop` is read on a path a caller's prototype can reach. */
674
+ export function leaveCommitFrame(kernel) {
675
+ const kept = [];
676
+ for (let i = 0; i + 1 < kernel.frames.length; i += 1) appendOwn(kept, kernel.frames[i]);
677
+ kernel.frames = kept;
678
+ }
679
+
680
+ /** **THE LOG OF THE COMMIT THE KERNEL IS INSIDE, OR `null` WHERE IT IS INSIDE NONE** — the one read `K-2` takes
681
+ * to decide whether this write joins a commit or opens one. */
682
+ export function commitLogInFlight(kernel) {
683
+ return kernel.frames.length === 0 ? null : kernel.frames[kernel.frames.length - 1].log;
684
+ }
685
+
616
686
  // ---------------------------------------------------------------------------------------------
617
687
  // THE MESSAGE LOG (`kernel-surface.md §2.5` item 1; `PAR-C-4`). SIX MEMBERS, `seq` ASCENDING FROM
618
688
  // `1`, ONE KIND PER ENTRY: `constraint-check` | `repair-run` | `commit` | `outcome`.
619
- // ---------------------------------------------------------------------------------------------
620
- export function append(attempt, kind, id, opId, message, state) {
689
+ //
690
+ // **⟶ AND THE ENTRY IS NAMED BY AN ITERATION AND NOT BY TWO LOOSE IDENTITIES — 2026-10-28, `D-53`'s FIRST
691
+ // RULING: *"Both combined to identify which constraint iteration it is on"*.** **THE ARCHITECT'S ANSWER TO
692
+ // `D-52`'s rule 1, read with `D-53`'s own gloss: *"an entry carries the PAIR identity and the OP identity
693
+ // together, and the PAIR identifies WHICH CONSTRAINT of that pair's evaluations is being spoken about … The two
694
+ // columns are not alternatives — they are one identity"*.** **THE SUBJECT OF AN ENTRY IS THE ITERATION, AND AN
695
+ // ITERATION IS A PAIR AT AN OP** — which is why no single acted-upon object could supply both, and why the old
696
+ // `(attempt, kind, id, opId, message, state)` signature (two loose strings in two adjacent positions) could be
697
+ // fed one value where the other belonged.
698
+ //
699
+ // **SO THE SIGNATURE TAKES THE ITERATION AS ONE OBJECT AT ONE POSITION, and reads its two members as its OWN
700
+ // data members** (`D-35`'s read half: `[[GetOwnProperty]]` and never a `[[Get]]` that walks a caller's chain):
701
+ // * `iteration.pair` — the registry row (`{ addresses, id, constraint }`) the entry speaks about, or `null`
702
+ // where the kind names none (the `commit` entry names no pair, and the `outcome` entry names one only where
703
+ // the arm has one);
704
+ // * `iteration.op` — the journal op the iteration is judged at, or `null` where the kind names none.
705
+ // **`id` IS READ OFF THE PAIR AND `opId` OFF THE OP**: a caller cannot put an id in the op's place, because
706
+ // there is no place for one.
707
+ export function append(attempt, kind, iteration, message, state) {
708
+ const held = iteration !== null && typeof iteration === 'object' ? iteration : null;
709
+ const pair = held === null ? undefined : ownMember(held, 'pair');
710
+ const op = held === null ? undefined : ownMember(held, 'op');
711
+ const id = pair === undefined || pair === null || typeof pair.id !== 'string' ? null : pair.id;
712
+ const opId = op === undefined || op === null || typeof op.opId !== 'string' ? null : op.opId;
621
713
  appendOwn(attempt.log, {
622
714
  seq: attempt.log.length + 1,
623
715
  kind,
624
- id: id === undefined ? null : id,
625
- opId: opId === undefined ? null : opId,
716
+ id,
717
+ opId,
626
718
  message,
627
719
  state: state === undefined ? null : state,
628
720
  });