footprintjs 9.45.0 → 9.46.1
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/CLAUDE.md +10 -10
- package/dist/esm/lib/decide/evaluator.js +2 -2
- package/dist/esm/lib/decide/evidence.js +2 -2
- package/dist/esm/lib/engine/handlers/servedPause.js +2 -2
- package/dist/esm/lib/memory/RecordFrame.d.ts +232 -0
- package/dist/esm/lib/memory/RecordFrame.js +296 -0
- package/dist/esm/lib/memory/SharedMemory.d.ts +1 -1
- package/dist/esm/lib/memory/SharedMemory.js +2 -2
- package/dist/esm/lib/memory/StageContext.d.ts +71 -155
- package/dist/esm/lib/memory/StageContext.js +130 -285
- package/dist/esm/lib/memory/TransactionBuffer.d.ts +2 -2
- package/dist/esm/lib/memory/TransactionBuffer.js +3 -3
- package/dist/esm/lib/memory/borrowedMutation.d.ts +1 -1
- package/dist/esm/lib/memory/borrowedMutation.js +2 -2
- package/dist/esm/lib/memory/honesty.d.ts +4 -3
- package/dist/esm/lib/memory/honesty.js +5 -4
- package/dist/esm/lib/memory/index.d.ts +4 -2
- package/dist/esm/lib/memory/index.js +6 -4
- package/dist/esm/lib/memory/placeholders.d.ts +23 -28
- package/dist/esm/lib/memory/placeholders.js +24 -29
- package/dist/esm/lib/memory/recordCommit.d.ts +5 -3
- package/dist/esm/lib/memory/recordCommit.js +7 -5
- package/dist/esm/lib/memory/redaction.d.ts +78 -40
- package/dist/esm/lib/memory/redaction.js +130 -64
- package/dist/esm/lib/memory/runAddress.d.ts +20 -0
- package/dist/esm/lib/memory/runAddress.js +25 -0
- package/dist/esm/lib/memory/scrub.d.ts +51 -0
- package/dist/esm/lib/memory/scrub.js +72 -0
- package/dist/esm/lib/memory/utils.d.ts +3 -3
- package/dist/esm/lib/memory/utils.js +4 -4
- package/dist/esm/lib/reactive/structuralWrite.d.ts +1 -1
- package/dist/esm/lib/reactive/structuralWrite.js +2 -2
- package/dist/lib/decide/evaluator.js +3 -3
- package/dist/lib/decide/evidence.js +3 -3
- package/dist/lib/engine/handlers/servedPause.js +3 -3
- package/dist/lib/memory/RecordFrame.js +300 -0
- package/dist/lib/memory/SharedMemory.js +2 -2
- package/dist/lib/memory/StageContext.js +134 -289
- package/dist/lib/memory/TransactionBuffer.js +3 -3
- package/dist/lib/memory/borrowedMutation.js +2 -2
- package/dist/lib/memory/honesty.js +5 -4
- package/dist/lib/memory/index.js +6 -4
- package/dist/lib/memory/placeholders.js +25 -30
- package/dist/lib/memory/recordCommit.js +9 -7
- package/dist/lib/memory/redaction.js +144 -75
- package/dist/lib/memory/runAddress.js +29 -0
- package/dist/lib/memory/scrub.js +77 -0
- package/dist/lib/memory/utils.js +4 -4
- package/dist/lib/reactive/structuralWrite.js +2 -2
- package/dist/types/lib/memory/RecordFrame.d.ts +232 -0
- package/dist/types/lib/memory/SharedMemory.d.ts +1 -1
- package/dist/types/lib/memory/StageContext.d.ts +71 -155
- package/dist/types/lib/memory/TransactionBuffer.d.ts +2 -2
- package/dist/types/lib/memory/borrowedMutation.d.ts +1 -1
- package/dist/types/lib/memory/honesty.d.ts +4 -3
- package/dist/types/lib/memory/index.d.ts +4 -2
- package/dist/types/lib/memory/placeholders.d.ts +23 -28
- package/dist/types/lib/memory/recordCommit.d.ts +5 -3
- package/dist/types/lib/memory/redaction.d.ts +78 -40
- package/dist/types/lib/memory/runAddress.d.ts +20 -0
- package/dist/types/lib/memory/scrub.d.ts +51 -0
- package/dist/types/lib/memory/utils.d.ts +3 -3
- package/dist/types/lib/reactive/structuralWrite.d.ts +1 -1
- package/package.json +1 -1
|
@@ -2,35 +2,35 @@
|
|
|
2
2
|
* StageContext — Execution context for a single stage in a flowchart run
|
|
3
3
|
*
|
|
4
4
|
* Like a stack frame in a compiler/runtime:
|
|
5
|
-
* -
|
|
6
|
-
*
|
|
5
|
+
* - A RecordFrame — the record half (C3): the heap, the address, the
|
|
6
|
+
* first-touch base, the lazy transaction buffer and every write's scrub
|
|
7
|
+
* (C4), the readKeys list
|
|
7
8
|
* - Links to parent/child/next contexts (call stack frames)
|
|
9
|
+
* - What belongs to a stage inside a run: retention of reads and writes, the
|
|
10
|
+
* redaction decision per write (`redaction.ts · decideWrite`) and where the
|
|
11
|
+
* frame writes (`runAddress.ts`), the dev-mode warnings, the commit observer
|
|
8
12
|
* - DiagnosticCollector for logs, errors, metrics
|
|
9
13
|
*/
|
|
10
14
|
import { type DiagnosticChannel, DiagnosticCollector } from './DiagnosticCollector.js';
|
|
11
|
-
import { EventLog } from './EventLog.js';
|
|
15
|
+
import type { EventLog } from './EventLog.js';
|
|
12
16
|
import type { EmitSourcePosition } from './eventPosition.js';
|
|
13
17
|
import type { RedactionVerdict } from './redaction.js';
|
|
14
18
|
import { RedactionRule } from './redaction.js';
|
|
15
19
|
import type { RunPolicy } from './runPolicy.js';
|
|
16
|
-
import { SharedMemory } from './SharedMemory.js';
|
|
17
|
-
import { TransactionBuffer } from './TransactionBuffer.js';
|
|
20
|
+
import type { SharedMemory } from './SharedMemory.js';
|
|
18
21
|
import type { CommitPhase, FlowControlType, StageSnapshot, UntrackedSource } from './types.js';
|
|
19
22
|
export declare class StageContext {
|
|
20
|
-
private sharedMemory;
|
|
21
23
|
/**
|
|
22
|
-
*
|
|
23
|
-
* the
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
* — consumers share the redacted view externally without leaking raw PII
|
|
31
|
-
* through `sharedState`.
|
|
24
|
+
* The record half of this frame (`RecordFrame.ts`, L3, C3): the heap and,
|
|
25
|
+
* when the run keeps them, the redacted mirror and the log; the address;
|
|
26
|
+
* the first-touch base; the lazy transaction buffer; the readKeys list.
|
|
27
|
+
* Every state read of this frame goes through it, and so does every
|
|
28
|
+
* commit. The mirror is present **only** when the run's policy keeps a
|
|
29
|
+
* redacted view (a `RedactionPolicy` is configured) — read via
|
|
30
|
+
* `FlowChartExecutor.getSnapshot({ redact: true })`, the foundation for
|
|
31
|
+
* sharing a trace without leaking raw PII through `sharedState`.
|
|
32
32
|
*/
|
|
33
|
-
private
|
|
33
|
+
private readonly record;
|
|
34
34
|
/**
|
|
35
35
|
* The run's policy — the four dials, the redaction rule, the mirror flag
|
|
36
36
|
* (`runPolicy.ts`, F5). Held BY REFERENCE: one frozen object per run (and
|
|
@@ -40,15 +40,6 @@ export declare class StageContext {
|
|
|
40
40
|
* ({@link usePolicy}). Never edited; a change is a new object.
|
|
41
41
|
*/
|
|
42
42
|
private policy;
|
|
43
|
-
private buffer?;
|
|
44
|
-
/**
|
|
45
|
-
* Committed-state view captured at this stage's FIRST touch (first read OR
|
|
46
|
-
* first write) — held by REFERENCE, never cloned. See
|
|
47
|
-
* {@link firstTouchState} for the algorithm and the immutability invariant
|
|
48
|
-
* that makes a bare reference safe.
|
|
49
|
-
*/
|
|
50
|
-
private stateView?;
|
|
51
|
-
private eventLog?;
|
|
52
43
|
/** The emitting frame's logical leg, never the mutable executor's current leg. */
|
|
53
44
|
private emitRunId?;
|
|
54
45
|
stageName: string;
|
|
@@ -65,7 +56,13 @@ export declare class StageContext {
|
|
|
65
56
|
* none — the same once-per-execution law `_untrackedSources` keeps.
|
|
66
57
|
*/
|
|
67
58
|
tags?: readonly string[];
|
|
68
|
-
|
|
59
|
+
/**
|
|
60
|
+
* The run this frame belongs to — its identity on every event, and the run
|
|
61
|
+
* namespace it reads and writes in ({@link runAddress}), read once, when the
|
|
62
|
+
* frame is built (C3). Only {@link useAddressOf} moves the address after
|
|
63
|
+
* that, and only before the first write.
|
|
64
|
+
*/
|
|
65
|
+
readonly runId: string;
|
|
69
66
|
/**
|
|
70
67
|
* The run namespace this frame writes and reads in when that is not its own
|
|
71
68
|
* `runId` — set once, before the frame's first write, by
|
|
@@ -128,11 +125,6 @@ export declare class StageContext {
|
|
|
128
125
|
* tier (`ScopeFacade`, one per stage execution), not here.
|
|
129
126
|
*/
|
|
130
127
|
private _committed;
|
|
131
|
-
/** Lazily-allocated ordered registry of keys tracked-read in THIS stage —
|
|
132
|
-
* the source of the per-write prefix. Only allocated under the
|
|
133
|
-
* `'reads-prefix'` dial; insertion-ordered (a Set) and monotonic, which
|
|
134
|
-
* is what makes "last write's prefix == union" hold in delta mode. */
|
|
135
|
-
private _provenanceReads?;
|
|
136
128
|
/** Tracked reads of a selected value ({@link selectedReads}). */
|
|
137
129
|
private _selectedReads;
|
|
138
130
|
/** Each object this frame read under a selected name → that read's verdict (`stageWrite` hands it on). */
|
|
@@ -159,7 +151,7 @@ export declare class StageContext {
|
|
|
159
151
|
/**
|
|
160
152
|
* Install a parallel redacted mirror. Subsequent `commit()` calls apply
|
|
161
153
|
* the already-computed redacted patches — the ones the log records — to
|
|
162
|
-
* this mirror, beside the raw patches on
|
|
154
|
+
* this mirror, beside the raw patches on the heap (`recordCommit`).
|
|
163
155
|
* Child / next contexts inherit the mirror via `createNext` / `createChild`.
|
|
164
156
|
*
|
|
165
157
|
* Called on a runtime's root frame by `ExecutionRuntime` when the run's policy keeps a mirror.
|
|
@@ -198,6 +190,12 @@ export declare class StageContext {
|
|
|
198
190
|
* {@link createChild} both call it, and it names no dial.
|
|
199
191
|
*/
|
|
200
192
|
private inheritRun;
|
|
193
|
+
/**
|
|
194
|
+
* The ONE place this frame's policy changes: the frame holds it, and its
|
|
195
|
+
* record frame encodes under the same reference (`commitValues`,
|
|
196
|
+
* `writeProvenance` — `RecordFrame · useEncoding`), so the two never differ.
|
|
197
|
+
*/
|
|
198
|
+
private adoptPolicy;
|
|
201
199
|
/**
|
|
202
200
|
* Record a tracked user-level write, policy-gated (#13c-A) — the single
|
|
203
201
|
* bookkeeping path for {@link setObject} and {@link updateObject}. Holds
|
|
@@ -210,10 +208,10 @@ export declare class StageContext {
|
|
|
210
208
|
* and stores nothing under `'off'` (entry skipped entirely — nothing to
|
|
211
209
|
* leak). A field-level verdict scrubs a clone BEFORE the dial sees it, so
|
|
212
210
|
* a summary preview can never show the secret either. The verdict is the
|
|
213
|
-
* one `stageWrite`
|
|
214
|
-
*
|
|
215
|
-
*
|
|
216
|
-
* `
|
|
211
|
+
* one `stageWrite` decided BEFORE staging, carried to commit with the
|
|
212
|
+
* value. The staged write itself is unaffected — redaction of the
|
|
213
|
+
* committed payload is the record frame's scrub of that write
|
|
214
|
+
* (`RecordFrame · write`, then `scrub.ts` at commit).
|
|
217
215
|
*/
|
|
218
216
|
private trackWrite;
|
|
219
217
|
/**
|
|
@@ -247,80 +245,21 @@ export declare class StageContext {
|
|
|
247
245
|
/**
|
|
248
246
|
* THE ONE FUNNEL every staged write passes through — facade writes AND
|
|
249
247
|
* the paths that bypass the facade (subflow seed, `outputMapper`
|
|
250
|
-
* merge-back, resume re-seed).
|
|
251
|
-
*
|
|
252
|
-
*
|
|
253
|
-
*
|
|
254
|
-
*
|
|
255
|
-
*
|
|
256
|
-
*
|
|
257
|
-
*
|
|
248
|
+
* merge-back, resume re-seed). Two owners (C4): the run's rule DECIDES,
|
|
249
|
+
* the record frame WRITES the verdict's bytes, in four steps
|
|
250
|
+
* (`redaction.ts`, "The write decision"):
|
|
251
|
+
* 1. the verdict — `decideWrite`, under the rule active as the write begins;
|
|
252
|
+
* 2. identity inheritance — `inheritByIdentity`, against this frame's
|
|
253
|
+
* selected reads as they stand after step 1;
|
|
254
|
+
* 3. the bytes — `RecordFrame · write`, the op and its scrub (the whole
|
|
255
|
+
* value, or the fields inside it);
|
|
256
|
+
* 4. the marks — `markStagedWrite`, only once the write staged, so a
|
|
257
|
+
* write that fails to stage marks nothing.
|
|
258
|
+
* Each step reads the rule (and the selected reads) when it acts, as this
|
|
259
|
+
* funnel always did. Returns the verdict so the caller retains and reports
|
|
260
|
+
* under the same decision.
|
|
258
261
|
*/
|
|
259
262
|
private stageWrite;
|
|
260
|
-
/**
|
|
261
|
-
* ── The first-touch state view (#13) ────────────────────────────────────
|
|
262
|
-
*
|
|
263
|
-
* WHAT: returns the committed shared state as it was at this stage's FIRST
|
|
264
|
-
* touch (first read or first write), capturing the reference on first call.
|
|
265
|
-
* Serves two consumers: reads before the first write ({@link readState})
|
|
266
|
-
* and the transaction buffer's diff base ({@link getTransactionBuffer}).
|
|
267
|
-
*
|
|
268
|
-
* WHY A BARE REFERENCE IS SAFE — the invariant this rests on: committed
|
|
269
|
-
* state is immutable-after-swap. Every write to `SharedMemory` builds the
|
|
270
|
-
* NEXT generation and swaps it in (copy-on-write, 9.29.0: `applyPatch` via
|
|
271
|
-
* `nextGeneration`, and `setValue`/`updateValue` too) — it copies the root
|
|
272
|
-
* and the containers on each written path, shares the rest, and never
|
|
273
|
-
* edits a container of the generation a stage captured here. Holding the
|
|
274
|
-
* reference therefore gives this stage a stable snapshot at zero cost — no
|
|
275
|
-
* clone, which is the entire point of #13. The transaction buffer's
|
|
276
|
-
* net-change diff base rests on the same guarantee: it IS this view.
|
|
277
|
-
*
|
|
278
|
-
* WHY FIRST TOUCH, not first write: the pre-#13 eager engine cloned the
|
|
279
|
-
* state into the buffer at the stage's first ACCESS, anchoring both its
|
|
280
|
-
* snapshot reads and its commit baseline (the net-change diff base) there.
|
|
281
|
-
* #13's first cut anchored the lazy buffer at first WRITE — observably
|
|
282
|
-
* different when something else commits in the gap between this stage's
|
|
283
|
-
* first read and its first write. That gap is REACHABLE: fork siblings are
|
|
284
|
-
* namespace-isolated for run-scoped keys (each child writes under
|
|
285
|
-
* `runs/<childId>/`), but ROOT-level keys are shared — written via
|
|
286
|
-
* `setGlobal` from consumer scope code and, critically, by
|
|
287
|
-
* `SubflowInputMapper`'s output mapping (`parentContext.setGlobal`), which
|
|
288
|
-
* is exactly what runs when a subflow is a fork branch. A sibling's
|
|
289
|
-
* root-key commit landing in the gap would shift this stage's diff base,
|
|
290
|
-
* making its CommitBundle record a phantom change (or swallow a real one)
|
|
291
|
-
* relative to the eager engine. Anchoring the view at first touch restores
|
|
292
|
-
* the EXACT eager semantics — sequential AND parallel — at zero clone cost.
|
|
293
|
-
*
|
|
294
|
-
* Read visibility is two-tier, matching eager byte-for-byte: keys present
|
|
295
|
-
* in the view at first touch read repeatably from it; keys ABSENT from it
|
|
296
|
-
* fall back to LIVE state (the eager engine's exact fallback — a
|
|
297
|
-
* mid-flight sibling root-key write was always visible to reads, and
|
|
298
|
-
* stays visible; only the DIFF BASE is pinned).
|
|
299
|
-
*/
|
|
300
|
-
private firstTouchState;
|
|
301
|
-
/** Lazily creates the transaction buffer on the stage's FIRST WRITE (#13).
|
|
302
|
-
*
|
|
303
|
-
* Reads NEVER construct it: read-your-writes only matters once a staged
|
|
304
|
-
* write exists, so before that {@link getValue}/{@link getValueDirect}
|
|
305
|
-
* serve from the first-touch state view and {@link commit} records an
|
|
306
|
-
* empty bundle — all with ZERO `structuredClone`s of the shared state.
|
|
307
|
-
*
|
|
308
|
-
* The buffer's base is the FIRST-TOUCH view, NOT the live state at write
|
|
309
|
-
* time: under parallel forks a sibling may have committed between this
|
|
310
|
-
* stage's first read and this write, and the net-change diff base must
|
|
311
|
-
* stay anchored at first touch to match the eager engine — see
|
|
312
|
-
* {@link firstTouchState}. */
|
|
313
|
-
getTransactionBuffer(): TransactionBuffer;
|
|
314
|
-
/**
|
|
315
|
-
* Where this frame writes and reads: `['runs', <id>]`, or `[]` (the root) with
|
|
316
|
-
* no run id. Handed to the record layer as data (C2): the buffer's address,
|
|
317
|
-
* and where `SharedMemory · getValue` looks before the root.
|
|
318
|
-
*/
|
|
319
|
-
private get address();
|
|
320
|
-
/** Builds an absolute path inside the shared memory: this frame's {@link address}, then the path. */
|
|
321
|
-
private withNamespace;
|
|
322
|
-
/** The run namespace writes and reads go to: {@link addressRunId}, else `runId`. */
|
|
323
|
-
private get namespaceId();
|
|
324
263
|
/**
|
|
325
264
|
* Write and read at ANOTHER frame's address (its run namespace) while this
|
|
326
265
|
* frame keeps its own identity on the record — `stage`, `stageId`,
|
|
@@ -328,9 +267,13 @@ export declare class StageContext {
|
|
|
328
267
|
* (`SubflowExecutor · executeSubflow`, R13): the mount frame of a branch or
|
|
329
268
|
* fork child lands its values where its parent writes, as before, and its
|
|
330
269
|
* bundle now names the mount instead of the stage before it. Refused once
|
|
331
|
-
* the frame has staged anything — its buffer's address is fixed then.
|
|
270
|
+
* the frame has staged anything — its buffer's address is fixed then. The
|
|
271
|
+
* decision is the engine's (namespace ids, as since R13); the record frame
|
|
272
|
+
* takes the address it comes to (`RecordFrame · useAddress`).
|
|
332
273
|
*/
|
|
333
274
|
useAddressOf(frame: StageContext): void;
|
|
275
|
+
/** The run namespace writes and reads go to: {@link addressRunId}, else `runId`. */
|
|
276
|
+
private get namespaceId();
|
|
334
277
|
patch(path: string[], key: string, value: unknown, shouldRedact?: boolean): RedactionVerdict;
|
|
335
278
|
set(path: string[], key: string, value: unknown): void;
|
|
336
279
|
merge(path: string[], key: string, value: unknown, shouldRedact?: boolean): RedactionVerdict;
|
|
@@ -342,26 +285,6 @@ export declare class StageContext {
|
|
|
342
285
|
updateGlobalContext(key: string, value: unknown): void;
|
|
343
286
|
appendToArray(path: string[], key: string, items: unknown[], description?: string): void;
|
|
344
287
|
mergeObject(path: string[], key: string, obj: Record<string, unknown>, description?: string): void;
|
|
345
|
-
/** Buffer-aware read, mirroring the eager engine's read order byte-for-byte:
|
|
346
|
-
*
|
|
347
|
-
* 1. staged writes + first-touch snapshot — `buffer.get` over its
|
|
348
|
-
* workingCopy when the buffer exists, else `nativeGet` over the
|
|
349
|
-
* zero-clone state view (the buffer's base IS that view, so the two
|
|
350
|
-
* tiers agree on content);
|
|
351
|
-
* 2. LIVE state via `sharedMemory.getValue` for keys absent from the
|
|
352
|
-
* snapshot — including its run→global namespace fallback. The eager
|
|
353
|
-
* engine had this exact live fallback for snapshot-missing keys;
|
|
354
|
-
* byte-identity over purity. After the stage's first write such a
|
|
355
|
-
* value can be the very container the buffer's diff base holds at
|
|
356
|
-
* the path (the stage deleted or unset it, or replaced a container
|
|
357
|
-
* above it), so the base is detached there first
|
|
358
|
-
* (`TransactionBuffer · detachBase`): an in-place edit of the value
|
|
359
|
-
* (out of contract) written back is recorded, as on 9.28.0, whose
|
|
360
|
-
* base was a clone taken at the first write.
|
|
361
|
-
*
|
|
362
|
-
* Reads never construct the buffer (#13): a stage that never writes
|
|
363
|
-
* performs zero clones of the shared state. */
|
|
364
|
-
private readState;
|
|
365
288
|
/**
|
|
366
289
|
* Tracked read. The returned value is BORROWED — see the contract on
|
|
367
290
|
* `ScopeFacade.getValue`. Read-tracking cost is policy-gated (#14):
|
|
@@ -420,8 +343,8 @@ export declare class StageContext {
|
|
|
420
343
|
* those are engine reads (the subflow merge-back), not user reads.
|
|
421
344
|
*
|
|
422
345
|
* It also only looks at keys served from the PINNED source — the first-touch
|
|
423
|
-
* view or this stage's own buffer. A key absent from both was served by
|
|
424
|
-
*
|
|
346
|
+
* view or this stage's own buffer. A key absent from both was served by the
|
|
347
|
+
* read's live fallback (`RecordFrame · read`, tier 2), which a parallel sibling's root-key commit can
|
|
425
348
|
* legitimately move; accusing the stage there would be a false alarm. For
|
|
426
349
|
* the same reason it runs on the frame's FIRST commit only: the reads belong
|
|
427
350
|
* to that round, and by the engine's second round (a fork double-commit, a
|
|
@@ -453,21 +376,16 @@ export declare class StageContext {
|
|
|
453
376
|
* `outputMapper` (a lazy mount, every `parallelForEach` branch), whose exit
|
|
454
377
|
* is its only bundle, records that bundle as its own, tags and all.
|
|
455
378
|
*
|
|
456
|
-
* Commit is the stage's lifecycle end:
|
|
457
|
-
*
|
|
458
|
-
*
|
|
459
|
-
*
|
|
460
|
-
*
|
|
461
|
-
*
|
|
462
|
-
* O(N²) before copy-on-write (9.29.0), when each generation and each
|
|
463
|
-
* buffer was a whole-state clone: 563.8MB at N=200 on an agent-style
|
|
464
|
-
* chart; a 500-iteration agent OOMed a default Node heap (backlog #18).
|
|
465
|
-
* Generations now share every unchanged subtree, but a pinned one still
|
|
466
|
-
* keeps the containers later commits replaced alive.
|
|
379
|
+
* Commit is the stage's lifecycle end: the record frame's buffer and
|
|
380
|
+
* first-touch base are only needed DURING execution, as the read snapshot +
|
|
381
|
+
* net-change diff base, so the record frame releases them once the commit
|
|
382
|
+
* has landed and the observer has seen it (`RecordFrame · release` — why a
|
|
383
|
+
* frame that kept them would retain one state generation per executed
|
|
384
|
+
* stage, and why a re-used frame re-anchors on current state, is there).
|
|
467
385
|
*
|
|
468
|
-
* RE-USE AFTER COMMIT stays correct because both
|
|
469
|
-
* - a later READ re-anchors
|
|
470
|
-
*
|
|
386
|
+
* RE-USE AFTER COMMIT stays correct because both re-create lazily:
|
|
387
|
+
* - a later READ re-anchors on the CURRENT committed state (which includes
|
|
388
|
+
* this stage's own flushed writes);
|
|
471
389
|
* - a later WRITE constructs a fresh buffer on that re-anchored view, so a
|
|
472
390
|
* second commit diffs against post-first-commit state. The pre-release
|
|
473
391
|
* buffer behaved the same for VALUES (its `workingCopy` was reset on
|
|
@@ -498,20 +416,18 @@ export declare class StageContext {
|
|
|
498
416
|
* (M1: the transaction buffer holds writes until `commit()` flushes them).
|
|
499
417
|
*
|
|
500
418
|
* What is released, and why each one matters for the next attempt:
|
|
501
|
-
* -
|
|
502
|
-
* with nothing staged
|
|
503
|
-
*
|
|
504
|
-
*
|
|
505
|
-
*
|
|
419
|
+
* - the record frame's staged state (`RecordFrame · discard`) — the buffer
|
|
420
|
+
* (the next attempt starts with nothing staged), the
|
|
421
|
+
* first-touch base (it re-anchors on committed state as
|
|
422
|
+
* it stands NOW — a sibling fork branch may legitimately
|
|
423
|
+
* have committed in between) and the readKeys list (#P1:
|
|
424
|
+
* a discarded attempt's reads must never reach the next
|
|
425
|
+
* attempt's `TraceEntry.readKeys`);
|
|
506
426
|
* - `_stageWrites` / `_pendingWrites` / `_stageReads` — the snapshot
|
|
507
427
|
* payload; without the reset the execution tree would
|
|
508
428
|
* report writes that were discarded, which is the exact
|
|
509
429
|
* lie this feature exists to prevent (the pending map
|
|
510
430
|
* holds bare references — dropping it un-clones nothing);
|
|
511
|
-
* - `_provenanceReads` — the per-write read prefix (#P1); a discarded
|
|
512
|
-
* attempt's reads must never appear in the next attempt's
|
|
513
|
-
* `TraceEntry.readKeys`, or a backward slice would follow
|
|
514
|
-
* an edge that no committed write ever had;
|
|
515
431
|
* - `_untrackedSources` — the D2 honesty markers, released with the rest.
|
|
516
432
|
*
|
|
517
433
|
* What is deliberately KEPT: `debug` (logs, metrics, errors, flow messages).
|