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.
@@ -1,5 +1,5 @@
1
1
  // `D-13` (`docs/L1/kernel-subdivision.md` §1 part `9b`) — the registration act, its evaluation, and the registry's removal: bytes moved from `bindings/js/src/constraint-kernel.mjs`; no logic changed.
2
- import { KC, KERNEL_REFUSAL, markBodyThrow, refuse, inDomainAddress, inDomainId, pairRowAt, sameAddress, kernelFor, append, appendOwn, landedOp, pairsAtAddress, externalCall } from './kernel-foundations.mjs';
2
+ import { KC, KERNEL_REFUSAL, markBodyThrow, refuse, inDomainAddress, inDomainId, pairRowAt, sameAddress, kernelFor, append, appendOwn, landedOp, pairsAtAddress, enterCommitFrame, leaveCommitFrame } from './kernel-foundations.mjs';
3
3
 
4
4
  /// **⟶ ADDED 2026-10-27 — `D-46`'s NAME ON A REPAIR INVOCATION'S OWN ENTRY, SPELLED HERE AS
5
5
  /// `kernel-attempt.mjs` SPELLS IT.** **THE RULING, THE ARCHITECT, VERBATIM: *"A constraint's repair needs to stay
@@ -31,6 +31,19 @@ function repairInCommit(store, commit, run) {
31
31
  if (typeof channel === 'function') return channel(run, commit);
32
32
  return run();
33
33
  }
34
+
35
+ /// **⟶ AND THE CONSTRAINT BODY'S INVOCATION AT THE REGISTRATION INSTANT NAMES THE REGISTRATION'S OWN COMMIT —
36
+ /// `D-53`, 2026-10-28: *"Constraints/Repairs should always use the same commit"*.** `D-46` named the commit for
37
+ /// the repair alone; **`D-53` states the invariant both kinds of caller-authored body obey, and a CONSTRAINT BODY
38
+ /// IS A CHECK: the commit the registering verb opened (`addConstraint`'s bracket, `docs/specs/storeroom-js/
39
+ /// surface.md §4.1`) is the commit it is asked inside, so a verb it calls JOINS that window and never opens a
40
+ /// second.** **A LISTENER BODY STILL NAMES NOTHING**: the entry invokes it at the terminal arm, where *"the
41
+ /// triggering commit already passed constraints"*, so what it does is a new question (`D-53`).
42
+ function constraintInCommit(store, commit, run) {
43
+ const channel = store !== null && typeof store === 'object' ? store[EXTERNAL_CALL] : undefined;
44
+ if (typeof channel === 'function') return channel(run, commit);
45
+ return run();
46
+ }
34
47
  // ---------------------------------------------------------------------------------------------
35
48
  // `K-1` `register(store, address, id, constraint) -> Handle`
36
49
  // ---------------------------------------------------------------------------------------------
@@ -247,6 +260,13 @@ function withdrawRegistration(kernel, address, id, fresh, home) {
247
260
  * invocation below is NOT wrapped — a throwing body reaches the caller as the very value it threw.
248
261
  */
249
262
  function registrationEvaluation(kernel, store, address, id, constraint) {
263
+ // **⟶ THE REGISTRATION'S OWN COMMIT AND ITS OWN ONE LIST — `D-53`, 2026-10-28.** A registration is a commit
264
+ // (`addConstraint`'s and `constraintOn`'s brackets open one, and `3-bis` (a) already declares *"the
265
+ // `CommitRecord` of the registration act"*), so the record is built FIRST and the list beside it: **a verb the
266
+ // body calls is part of this commit, its entries are this list's entries, and the synthetic attempt a repair
267
+ // needs (state `3`) is an attempt of this same commit** — it appends to this list and mints no second one.
268
+ const commitRecord = { attempt: null, op: null, triggered: [], pending: [] };
269
+ const commitLog = [];
250
270
  // **⟶ AMENDED 2026-10-26 (`DEF-S`; the architect's ruling, verbatim: *"Expand the returned error set for
251
271
  // both S/N"*): THE BODY'S THROW IS MARKED AT THE FRAME THAT CAUGHT IT, and no attempt stands at this
252
272
  // instant (`null`), so the mark carries the `[]` its clause declares. **THE THROWABLE'S IDENTITY IS
@@ -255,16 +275,23 @@ function registrationEvaluation(kernel, store, address, id, constraint) {
255
275
  // unmarked throw is still the caller's own (`markBodyThrow`, `kernel-foundations.mjs`).
256
276
  let outcome;
257
277
  const context = registrationContext(store, address, null);
278
+ // **⟶ AND THE BODY IS INVOKED WITH THE REGISTRATION'S COMMIT NAMED — `D-53`, 2026-10-28.** The clause this
279
+ // replaces read *"the body is invoked with nothing named … so a verb it calls OPENS ITS OWN COMMIT"*
280
+ // (`D-45`, `D-46` item `1`), and **`D-53`'s invariant reverses it for constraints: *"Constraints/Repairs should
281
+ // always use the same commit"*** — a constraint body is asked, and the check it is asked by is this commit.
282
+ // **THE FRAME IS PUSHED FOR EXACTLY THE INVOCATION AND POPPED IN A `finally`**, so a nested write appends to this
283
+ // registration's list and a throwable leaves no frame standing.
284
+ // **THE CONTEXT IS BUILT ABOVE THE INVOCATION** (`kernel-surface.md §2.3` item 1), so the store read the context
285
+ // owes is this verb's own act and not the body's.
286
+ enterCommitFrame(kernel, commitLog);
258
287
  try {
259
- // **⟶ AND THE BODY IS INVOKED WITH NOTHING NAMED, 2026-10-27 (`D-45`, `D-46` item `1`).** A constraint body
260
- // is EXTERNALLY-SUPPLIED FUNCTION code entering the surface **and it has no commit in hand to name** — its
261
- // five-member context carries none (`kernel-surface.md §2.3` item 1) — so **a verb it calls OPENS ITS OWN
262
- // COMMIT** rather than joining the registering verb's (*"It should be opening a new commit by default of
263
- // entering at the surface"*). **THE CONTEXT IS BUILT ABOVE THE INVOCATION**, so the store read the context
264
- // owes is this verb's own act and not the body's.
265
- outcome = externalCall(store, () => constraint(context));
288
+ outcome = constraintInCommit(store, commitRecord, () => constraint(context));
266
289
  } catch (body) {
267
- throw markBodyThrow(body, address, null);
290
+ // **AND THE MARK CARRIES THIS COMMIT'S LIST** (`markBodyThrow`'s second slot): a registration-time body that
291
+ // threw after its own verbs appended carries those entries to the crossing, where `[]` used to be.
292
+ throw markBodyThrow(body, address, commitLog);
293
+ } finally {
294
+ leaveCommitFrame(kernel);
268
295
  }
269
296
  // THE OUTCOME IS `PAR-C-5`'S CLOSED THREE-ARM SET, read as the landing pass reads it: `true` is the pass
270
297
  // arm; an object carrying `message` is the violation arm (its `repair`, where present, the repair the
@@ -277,9 +304,10 @@ function registrationEvaluation(kernel, store, address, id, constraint) {
277
304
  : null;
278
305
  // STATE 2 — THE STATE VIOLATES IT AND NO REPAIR IS REGISTERED. Nothing is attempted and no attempt is
279
306
  // opened; the refusal carries the store's code and the row's own slots.
280
- if (repair === null) return registrationRefusal(address, id, []);
281
- // STATE 3 — THE REPAIR IS ATTEMPTED, on the synthetic attempt `3-bis` (a) declares.
282
- return registrationAttempt(kernel, store, { address, id, repair });
307
+ if (repair === null) return registrationRefusal(address, id, commitLog);
308
+ // STATE 3 — THE REPAIR IS ATTEMPTED, on the synthetic attempt `3-bis` (a) declares — **the attempt of THIS
309
+ // commit, holding THIS list by reference** (`D-53`).
310
+ return registrationAttempt(kernel, store, { address, id, repair, log: commitLog, commit: commitRecord });
283
311
  }
284
312
 
285
313
  /// **THE FIVE-MEMBER EVALUATION CONTEXT** (`kernel-surface.md §2.3` item 1's clause of 2026-10-17), built
@@ -392,11 +420,14 @@ function registrationAttempt(kernel, store, pair) {
392
420
  tier: null,
393
421
  value: undefined,
394
422
  // `commit` — THE REGISTRATION ACT'S OWN `CommitRecord` (`3-bis` (a)): this kernel's four members, with
395
- // no write's op and no write's triggered set, because a registration lands no `K-2` of its own.
396
- commit: { attempt: null, op: null, triggered: [], pending: [] },
423
+ // no write's op and no write's triggered set, because a registration lands no `K-2` of its own. **⟶ IT IS THE
424
+ // RECORD THE EVALUATION ALREADY BUILT AND NAMED (`D-53`), not a second one**, so the commit the constraint
425
+ // body was asked inside and the commit its repair joins are ONE.
426
+ commit: pair.commit,
397
427
  write: null,
398
428
  ops: [],
399
- log: [],
429
+ // **AND THE LIST IS THAT COMMIT'S OWN, BY REFERENCE** (`D-53`).
430
+ log: pair.log,
400
431
  ran: [],
401
432
  appliedRepairs: [],
402
433
  rollback: [],
@@ -411,9 +442,12 @@ function registrationAttempt(kernel, store, pair) {
411
442
  // pair is appended to BOTH `ran` and `appliedRepairs` (`3-bis` (b): the pair JOINS both members — of the
412
443
  // SYNTHETIC attempt, and of no other). **THE PAIR ENTERS `ran` BEFORE THE REPAIR RUNS**, which is that
413
444
  // arm's own ordering and is what makes the repeat arms reachable at the re-run below.
414
- append(attempt, 'constraint-check', pair.id, null, `the evaluation of ${pair.id}`);
445
+ // **⟶ AND THE TWO ENTRIES ARE NAMED BY THE ITERATION — `D-53`'s first ruling.** This arm names no OP (`3-bis`
446
+ // (a)'s synthetic attempt has landed nothing when these are appended): the pair stands, the op is the declared
447
+ // `null` absence, and the `repair-run` entry's `opId` is rewritten below where the repair lands one.
448
+ append(attempt, 'constraint-check', { pair, op: null }, `the evaluation of ${pair.id}`);
415
449
  appendOwn(attempt.ran, key);
416
- append(attempt, 'repair-run', pair.id, null, 'the pair\'s repair was attempted at registration');
450
+ append(attempt, 'repair-run', { pair, op: null }, 'the pair\'s repair was attempted at registration');
417
451
 
418
452
  let result;
419
453
  const repairContext = registrationRepairContext(store, address, pair.id, attempt.commit);
@@ -428,6 +462,8 @@ function registrationAttempt(kernel, store, pair) {
428
462
  // that context at `write`'s fourth position names the same commit by its own hand, and the two spellings
429
463
  // cannot disagree (the entry's position wins where it is supplied, and the invocation's name is the fallback
430
464
  // where it is not — `state.mjs`'s `openCommit`).
465
+ // **AND THE FRAME STANDS FOR THIS INVOCATION TOO (`D-53`)**: the repair's verbs append to this commit's list.
466
+ enterCommitFrame(kernel, attempt.log);
431
467
  result = repairInCommit(store, attempt.commit, () => pair.repair(repairContext));
432
468
  } catch (body) {
433
469
  // A REPAIR WHOSE BODY THROWS IS A FAILED REPAIR AND THE REGISTRATION IS REFUSED — the caller's act is
@@ -437,6 +473,8 @@ function registrationAttempt(kernel, store, pair) {
437
473
  // that expected the body's own value out of `addConstraint` has read a route no clause declares.
438
474
  closeRegistrationAttempt(kernel, attempt);
439
475
  return registrationRefusal(address, pair.id, attempt.log);
476
+ } finally {
477
+ leaveCommitFrame(kernel);
440
478
  }
441
479
 
442
480
  const ok = result !== null && typeof result === 'object' && result.ok === true;
@@ -500,17 +538,20 @@ function registrationRerun(kernel, attempt, produced) {
500
538
  attempt: attempt.ref,
501
539
  op: produced,
502
540
  };
541
+ // **⟶ AND THE RE-RUN'S BODIES ARE OF THIS COMMIT TOO — `D-53`.** The clause this replaces read *"this body is
542
+ // invoked with nothing named too … the third of the three that invoke a caller-authored constraint body"*
543
+ // (`D-45`): **the three frames name the attempt's commit now, and the registration's re-run names the same
544
+ // one its repair joined.** The context is built ABOVE the invocation.
545
+ enterCommitFrame(kernel, attempt.log);
503
546
  try {
504
- // **⟶ AND THIS BODY IS INVOKED WITH NOTHING NAMED TOO, 2026-10-27 (`D-45`, `D-46` item `1`)** — the
505
- // re-run's frame is the third of the three that invoke a caller-authored constraint body, and it names no
506
- // commit exactly as the registration evaluation and the landing's pass do. The context is built ABOVE the
507
- // invocation.
508
- outcome = externalCall(attempt.store, () => pair.constraint(context));
547
+ outcome = constraintInCommit(attempt.store, attempt.commit, () => pair.constraint(context));
509
548
  } catch (body) {
510
549
  // **⟶ ADDED 2026-10-26 (`DEF-S`, the same ruling): a body thrown at the synthetic attempt's re-run is
511
550
  // marked at this frame too — the mark carries the attempt's own log and address, and the entry's
512
551
  // crossing converts a marked value only** (`markBodyThrow`, `kernel-foundations.mjs`).
513
552
  throw markBodyThrow(body, attempt.address, attempt.log);
553
+ } finally {
554
+ leaveCommitFrame(kernel);
514
555
  }
515
556
  if (outcome === true) continue;
516
557
  attempt.terminalArm = attempt.appliedRepairs.includes(key) ? 'KC-5' : 'KC-6';
@@ -49,14 +49,27 @@ export function terminal(kernel, attempt, status, message) {
49
49
  // THE COMPOSITION'S OWN FLOOR, TAKEN OFF THE MEMBER IT WAS DECIDED ON (`composition-and-states.md
50
50
  // §2.6` item 5 (b)): a still-owed op's call meeting either floor code makes the arm `KC-8` WHATEVER
51
51
  // the pass's own arm was, and nothing is restored (item 6 (a), (c)).
52
- const finalStatus = floor === null ? status : KC.rollbackFloor;
53
- const finalMessage = floor === null
54
- ? message
55
- : `the per-op rollback met the store's own ${floor.code} at ${floor.opId}`;
52
+ //
53
+ // **⟶ AND THE SAVE ARM'S OWN TOKEN IS NOT OVERWRITTEN BY IT — `D-53`'s THIRD RULING, 2026-10-28.** **THE FLOOR
54
+ // BELONGS TO THE COMPOSITION AND `KC-8` KEEPS ITS DECLARED MEANING; what it may not do is make a SAVE FAILURE
55
+ // and a CONSTRAINT REFUSAL end at the same token**, which is the contradiction `D-52` measured (*"`KC-8` crosses
56
+ // as both statuses"*). **SO THE PRECEDENCE IS ONE SENTENCE: THE FLOOR GOVERNS EVERY ARM BUT THE SAVE'S, and the
57
+ // save's arm keeps `KC-9`, its own message, and the floor's own sentence beside it** — so the caller can still
58
+ // read, from the ONE `outcome` entry, both what failed and what the composition then met.
59
+ const floorGoverns = floor !== null && status !== KC.saveFailed;
60
+ const finalStatus = floorGoverns ? KC.rollbackFloor : status;
61
+ const finalMessage = floorGoverns
62
+ ? `the per-op rollback met the store's own ${floor.code} at ${floor.opId}`
63
+ : floor === null
64
+ ? message
65
+ : `${message} — the per-op rollback then met the store's own ${floor.code} at ${floor.opId}`;
56
66
  // THE `outcome` ENTRY — appended ONCE, and `state` is THE ARM'S ONE TOKEN: the floor is answered IN THIS
57
67
  // SAME ENTRY where the attempt would also have had a failure arm, and NO SECOND ENTRY IS APPENDED
58
68
  // (`kernel-surface.md §2.5` item 1's kind table; `§2.6` item 6 (a), (b)).
59
- append(attempt, 'outcome', null, attempt.ops[attempt.ops.length - 1].opId, finalMessage, finalStatus);
69
+ // **⟶ AND ITS ITERATION NAMES NO PAIR AND THE ATTEMPT'S LAST OP — `D-53`'s first ruling**, so the two columns
70
+ // travel as one identity wherever an op exists and read `null` where one does not ("an `outcome` names them
71
+ // where they exist").
72
+ append(attempt, 'outcome', { pair: null, op: attempt.ops[attempt.ops.length - 1] }, finalMessage, finalStatus);
60
73
  // THE DISCARD RULE, AT ITS ONE OBSERVABLE (`attempt-lifecycle.md §2.4` item 8 (i) (5), read with
61
74
  // `kernel-surface.md §2.1` item 2's discarded-set clause; `A: docs/decisions.md` · `A112`): where the
62
75
  // attempt's own write FAILED the registered constraints at its landing and could not be repaired, its
@@ -85,6 +85,39 @@ export const SAVE_FAILED_AT_THE_TERMINAL_ARM = Symbol.for('provident-storeroom.s
85
85
  /// ACT BRANCHES ON IT** (`PAR-JS-19`'s answer cell, whose subject is the arm's return and not a member's).
86
86
  export const TERMINAL_ARM = Symbol.for('provident-storeroom.terminal-arm');
87
87
 
88
+ /// **⟶ ADDED 2026-10-28 — THE COMMIT'S OWN MESSAGE LOG, SPELLED HERE AS THE KERNEL SPELLS IT (`D-53`).**
89
+ ///
90
+ /// **THE RULING, VERBATIM: *"If it's part of the same commit, then it uses the same message log. If it opens a new
91
+ /// commit, it uses the new commit's message log"*** (`strata/decisions.md` · `D-53`). **SO THE LIST LIVES ON THE
92
+ /// COMMIT AND IT TRAVELS ON THE COMMIT'S OWN RECORD** — `kernel-commit.mjs` defines it on the `CommitRecord` it
93
+ /// builds, under this key, and this module reads it for the ONE site that needs the commit's verdict and not an
94
+ /// attempt's: **a failed `file`-tier save.** The save is the write's LAST constraint and it fails at the terminal
95
+ /// arm, so the crossing this module builds there carries the COMMIT's entries — `D-52`'s sentence *"a save failure
96
+ /// crosses as `FileCommitFailed` with `log: []`, and its verdict stands only in the attempt's log, which the
97
+ /// crossed value does not carry"* is the defect this member closes.
98
+ ///
99
+ /// **IT IS A `Symbol.for` KEY ON THE RECORD AND NO MEMBER IS ADDED** (`A117` (D6)): `Object.keys` and
100
+ /// `Object.getOwnPropertyNames` read past it, so `CommitRecord` keeps its FOUR declared members and every census of
101
+ /// them moves by nothing — the device `SETTLED_ON_THE_PASSING_ARM` and `SAVE_FAILED_AT_THE_TERMINAL_ARM` already
102
+ /// stand on. **A RECORD CARRYING NO SUCH KEY** — a caller's own record, and every record the kernel did not build —
103
+ /// answers `undefined`, and the crossing falls back to the declared `[]`.
104
+ export const COMMIT_LOG = Symbol.for('provident-storeroom.commit-log');
105
+
106
+ /// **⟶ AND THE ONE READ OF IT — a `[[GetOwnProperty]]` and never a `[[Get]]`, `D-35`'s READ HALF.**
107
+ ///
108
+ /// The record this reads is handed through `write`'s FOURTH POSITION, which a CALLER may fill: an ordinary member
109
+ /// read would run a getter the caller installed there — user code on a refusal path, before any verdict. **The
110
+ /// descriptor's own `value` is taken only where it IS one, so an accessor answers `undefined` exactly as an absent
111
+ /// key does, and `fail`'s declared `[]` is what crosses.** A record the KERNEL built carries the list as an own
112
+ /// non-enumerable data property (`kernel-commit.mjs` defines it with `Object.defineProperty`), so the lawful arm
113
+ /// reads it and every other arm reads nothing.
114
+ export function commitLogOf(record) {
115
+ if (record === null || typeof record !== 'object') return undefined;
116
+ const descriptor = Object.getOwnPropertyDescriptor(record, COMMIT_LOG);
117
+ if (descriptor === undefined || !Object.hasOwn(descriptor, 'value')) return undefined;
118
+ return descriptor.value;
119
+ }
120
+
88
121
  /// **⟶ ADDED — `K-6`'s READ-MODEL CHANNEL, under a symbol key for the same reason every symbol above is
89
122
  /// one** (`A117` (D6): *"a symbol-keyed own property is invisible to both"* `Object.keys` and
90
123
  /// `Object.getOwnPropertyNames`, so the product's declared fourteen own members are UNMOVED by it).
@@ -879,11 +912,16 @@ export function isStoreReceiver(candidate) {
879
912
  return state !== undefined && state.receiver === candidate;
880
913
  }
881
914
 
882
- export function carryRefusal(call, args) {
915
+ export function carryRefusal(call, args, log) {
883
916
  try {
884
917
  return call();
885
918
  } catch (refusal) {
886
- fail(notFoundRendering(refusal, listedSegments(args[0])));
919
+ // **⟶ AND THE THIRD POSITION IS THE COMMIT'S LIST WHERE THE CALLER HAS ONE (`D-53`).** Every verb that
920
+ // reaches here with no third position answers exactly what it answered before (`fail`'s declared `[]`); the
921
+ // FAILED SAVE's call is the one that passes a list, and it passes `commitLogOf(record)` — the commit's own
922
+ // entries, carried beside the store's two-member form and never re-shaped (`surface.md §2.5` item 10 (ii):
923
+ // *"nothing is converted, and a member is added"*).
924
+ fail(notFoundRendering(refusal, listedSegments(args[0])), log);
887
925
  }
888
926
  }
889
927
 
@@ -945,11 +983,16 @@ function listedSegments(arg) {
945
983
  /// `[A1 bindings/js/index.mjs:2439 · :2454 · :2429 · :2432; docs/specs/storeroom-json-crossing/contract.md §6
946
984
  /// row 4; docs/specs/storeroom-addon-surface/js-unwrap.md §3; docs/specs/storeroom-native/write-and-read.md
947
985
  /// §3.4 `F-1` | requirement]`
948
- export function writeRefusal(refusal, fault) {
986
+ export function writeRefusal(refusal, fault, record) {
949
987
  if (fault.found !== null && refusal !== null && typeof refusal === 'object' && refusal.status === 'ValueNotAdmitted') {
950
988
  valueReject(fault);
951
989
  }
952
- fail(refusal);
990
+ // **⟶ AND THIS IS THE WRITE VERB'S ONE LOG BYPASS, CLOSED — `D-53`, 2026-10-28.** The store's own refusal at the
991
+ // verb crossed here with `fail`'s declared `[]`, so a write refused inside a commit whose list already carried
992
+ // entries — a nested write refused by the store from inside a constraint body or a repair — told the caller
993
+ // NOTHING of the commit's verdict. **THE LIST IS THE COMMIT'S**, handed by the member that holds the record,
994
+ // and it stays `[]` where no commit stands (a caller's own write, whose commit is the one this verb is opening).
995
+ fail(refusal, commitLogOf(record));
953
996
  }
954
997
 
955
998
  /// `docs/specs/storeroom-js/contract.md §2.10`: `listen` mints a token, populates the store's home and wraps it for JS — and its `address` is