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.
- package/README.guide.md +122 -3
- package/README.md +24 -8
- package/RELEASE-NOTES.md +584 -0
- package/index.d.ts +38 -2
- package/index.mjs +11 -3
- package/package.json +7 -6
- package/src/entry/constraints.mjs +402 -69
- package/src/entry/delivery.mjs +8 -2
- package/src/entry/kernel-attempt.mjs +136 -19
- package/src/entry/kernel-commit.mjs +26 -2
- package/src/entry/kernel-foundations.mjs +100 -8
- package/src/entry/kernel-registration.mjs +63 -22
- package/src/entry/kernel-terminal.mjs +18 -5
- package/src/entry/state.mjs +47 -4
package/src/entry/delivery.mjs
CHANGED
|
@@ -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
|
-
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
391
|
-
//
|
|
392
|
-
//
|
|
393
|
-
//
|
|
394
|
-
//
|
|
395
|
-
//
|
|
396
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
625
|
-
opId
|
|
716
|
+
id,
|
|
717
|
+
opId,
|
|
626
718
|
message,
|
|
627
719
|
state: state === undefined ? null : state,
|
|
628
720
|
});
|