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