@llblab/pi-kit 0.28.0 → 0.29.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/BACKLOG.md +2 -1
- package/CHANGELOG.md +4 -0
- package/README.md +3 -3
- package/node_modules/@llblab/pi-state-flow/AGENTS.md +2 -2
- package/node_modules/@llblab/pi-state-flow/BACKLOG.md +5 -2
- package/node_modules/@llblab/pi-state-flow/CHANGELOG.md +7 -0
- package/node_modules/@llblab/pi-state-flow/README.md +7 -8
- package/node_modules/@llblab/pi-state-flow/dist/lib/extension.js +94 -47
- package/node_modules/@llblab/pi-state-flow/dist/lib/recovery.d.ts +2 -2
- package/node_modules/@llblab/pi-state-flow/dist/lib/runtime.d.ts +8 -27
- package/node_modules/@llblab/pi-state-flow/dist/lib/runtime.js +16 -151
- package/node_modules/@llblab/pi-state-flow/dist/lib/snapshot.d.ts +3 -4
- package/node_modules/@llblab/pi-state-flow/dist/lib/snapshot.js +4 -3
- package/node_modules/@llblab/pi-state-flow/dist/lib/status.d.ts +1 -1
- package/node_modules/@llblab/pi-state-flow/dist/lib/status.js +4 -1
- package/node_modules/@llblab/pi-state-flow/dist/lib/telegram.d.ts +2 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/telegram.js +5 -1
- package/node_modules/@llblab/pi-state-flow/dist/lib/temporal.d.ts +0 -4
- package/node_modules/@llblab/pi-state-flow/dist/lib/temporal.js +0 -36
- package/node_modules/@llblab/pi-state-flow/dist/package.json +1 -1
- package/node_modules/@llblab/pi-state-flow/docs/architecture.md +20 -31
- package/node_modules/@llblab/pi-state-flow/docs/compatibility.md +8 -9
- package/node_modules/@llblab/pi-state-flow/docs/filesystem-recovery.md +4 -4
- package/node_modules/@llblab/pi-state-flow/docs/fork-contract.md +18 -23
- package/node_modules/@llblab/pi-state-flow/docs/temporal-acceptance.md +19 -21
- package/node_modules/@llblab/pi-state-flow/docs/usage.md +24 -25
- package/node_modules/@llblab/pi-state-flow/lib/extension.ts +89 -48
- package/node_modules/@llblab/pi-state-flow/lib/recovery.ts +2 -2
- package/node_modules/@llblab/pi-state-flow/lib/runtime.ts +22 -146
- package/node_modules/@llblab/pi-state-flow/lib/snapshot.ts +6 -3
- package/node_modules/@llblab/pi-state-flow/lib/status.ts +4 -2
- package/node_modules/@llblab/pi-state-flow/lib/telegram.ts +7 -1
- package/node_modules/@llblab/pi-state-flow/lib/temporal.ts +0 -36
- package/node_modules/@llblab/pi-state-flow/package.json +1 -1
- package/package.json +2 -2
|
@@ -6,8 +6,8 @@ import { pruneArtifactProvenance } from "./artifact.js";
|
|
|
6
6
|
import { assertTemporalFileBase, captureTemporalFileBase, initializeFileStore, publishTemporalStateToFiles, withStorageTransaction } from "./storage.js";
|
|
7
7
|
import { DEFAULT_HISTORY_LIMIT, MAX_HISTORY_LIMIT } from "./history.js";
|
|
8
8
|
import { hashJson, sameJson } from "./json.js";
|
|
9
|
-
import {
|
|
10
|
-
import { adoptTemporalStreams, advanceTemporalState, constrainTemporalState, createTemporalState, readTemporalScopes, readTemporalState, readTemporalView,
|
|
9
|
+
import { RevisionUnavailableError, createSessionRuntime, parseSessionRuntime, preRuntimeCheckpoint, retainedBoundaryCheckpoint } from "./snapshot.js";
|
|
10
|
+
import { adoptTemporalStreams, advanceTemporalState, constrainTemporalState, createTemporalState, readTemporalScopes, readTemporalState, readTemporalView, validateScopeLineage } from "./temporal.js";
|
|
11
11
|
const SCOPES = ["global", "cwd", "session"];
|
|
12
12
|
const SHARED_SCOPES = ["global", "cwd"];
|
|
13
13
|
function emptyProvenance() {
|
|
@@ -50,7 +50,6 @@ export class TemporalRuntime {
|
|
|
50
50
|
semanticRevision;
|
|
51
51
|
savedRuntime;
|
|
52
52
|
transaction;
|
|
53
|
-
restoredOriginPending = false;
|
|
54
53
|
provenanceByScope = emptyProvenance();
|
|
55
54
|
/** Shared scopes whose wholly absent live basis was accepted after one stale-target refusal. */
|
|
56
55
|
absentSharedScopes = new Set();
|
|
@@ -101,7 +100,6 @@ export class TemporalRuntime {
|
|
|
101
100
|
this.provenanceByScope = provenance;
|
|
102
101
|
this.semanticRevision = undefined;
|
|
103
102
|
this.savedRuntime = undefined;
|
|
104
|
-
this.restoredOriginPending = false;
|
|
105
103
|
this.absentSharedScopes.clear();
|
|
106
104
|
return true;
|
|
107
105
|
}
|
|
@@ -152,83 +150,6 @@ export class TemporalRuntime {
|
|
|
152
150
|
}
|
|
153
151
|
return result;
|
|
154
152
|
}
|
|
155
|
-
/** Prepare a retained session boundary from current canonical files; shared scopes remain live. */
|
|
156
|
-
prepareBoundaryRestore(checkpoint) {
|
|
157
|
-
if (!lstatSync(this.root, { throwIfNoEntry: false }))
|
|
158
|
-
throw new RevisionUnavailableError("Selected State Flow boundary storage is unavailable");
|
|
159
|
-
return this.prepareBoundaryRestoreBase(checkpoint, captureTemporalFileBase(this.cwd, this.sessionId, this.root, this.sessionKey));
|
|
160
|
-
}
|
|
161
|
-
prepareBoundaryRestoreBase(checkpoint, base) {
|
|
162
|
-
const files = new Map(base.files.map((file) => [file.path, file.content]));
|
|
163
|
-
const scopes = Object.fromEntries(SCOPES.map((scope) => {
|
|
164
|
-
const paths = temporalScopePaths(this.cwd, this.sessionId, scope, this.root, this.sessionKey);
|
|
165
|
-
const stream = parseScopeStream(files.get(paths.checkpoint), files.get(paths.patches), scope, scope === "cwd" ? this.cwd : undefined, files.get(paths.meta));
|
|
166
|
-
if (!stream)
|
|
167
|
-
throw new RevisionUnavailableError(`State Flow ${scopeLabel(scope)} scope is unavailable for retained-boundary restoration`);
|
|
168
|
-
return [scope, stream];
|
|
169
|
-
}));
|
|
170
|
-
const runtimePaths = sessionRuntimePaths(this.cwd, this.sessionId, this.root, this.sessionKey);
|
|
171
|
-
const document = parseSessionRuntime(files.get(runtimePaths.config), files.get(runtimePaths.runtime), this.cwd, this.sessionId);
|
|
172
|
-
if (!document)
|
|
173
|
-
throw new RevisionUnavailableError("State Flow session runtime is unavailable for retained-boundary restoration");
|
|
174
|
-
// The codecs validate persisted retention against the format maximum, not the new operator limit.
|
|
175
|
-
validateScopeLineage(scopes.session, "session", document.meta.lineage, MAX_HISTORY_LIMIT);
|
|
176
|
-
const boundary = document.meta.lineage.slice(-(this.historyLimit + 1)).find(({ id }) => id === checkpoint.boundary);
|
|
177
|
-
if (!boundary)
|
|
178
|
-
throw new HistoryBoundaryExpiredError("Selected State Flow history boundary is outside the retained temporal window");
|
|
179
|
-
const selectedSession = selectScopeStreamAtBoundary(scopes.session, "session", boundary, MAX_HISTORY_LIMIT);
|
|
180
|
-
const view = adoptTemporalStreams({
|
|
181
|
-
global: scopes.global,
|
|
182
|
-
cwd: scopes.cwd,
|
|
183
|
-
session: selectedSession,
|
|
184
|
-
}, `restore:${checkpoint.boundary}:${randomUUID()}`, this.historyLimit);
|
|
185
|
-
const snapshot = {
|
|
186
|
-
config: { mode: checkpoint.mode },
|
|
187
|
-
meta: {
|
|
188
|
-
step: checkpoint.step,
|
|
189
|
-
...(checkpoint.bootstrap === true ? { bootstrap: true } : {}),
|
|
190
|
-
...(checkpoint.specification === undefined ? {} : { specification: checkpoint.specification }),
|
|
191
|
-
},
|
|
192
|
-
};
|
|
193
|
-
const provenance = Object.fromEntries(SCOPES.map((scope) => {
|
|
194
|
-
const paths = temporalScopePaths(this.cwd, this.sessionId, scope, this.root, this.sessionKey);
|
|
195
|
-
const parsed = parseScopeProvenance(files.get(paths.meta), paths.meta);
|
|
196
|
-
const retained = pruneArtifactProvenance(parsed, readTemporalState(view, 0, scope, this.historyLimit).artifacts);
|
|
197
|
-
if (scope === "session") {
|
|
198
|
-
// Live evidence cannot prove an earlier artifact version, even after a change-away-and-back.
|
|
199
|
-
for (const record of scopes.session.patches) {
|
|
200
|
-
if (record.transition.position <= boundary.position)
|
|
201
|
-
continue;
|
|
202
|
-
for (const path of Object.keys(record.patch.artifacts === null ? retained : record.patch.artifacts ?? {}))
|
|
203
|
-
delete retained[path];
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
return [scope, retained];
|
|
207
|
-
}));
|
|
208
|
-
let consumed = false;
|
|
209
|
-
return {
|
|
210
|
-
snapshot: structuredClone(snapshot),
|
|
211
|
-
restore: () => {
|
|
212
|
-
if (consumed)
|
|
213
|
-
throw new Error("Prepared State Flow restore was already consumed");
|
|
214
|
-
consumed = true;
|
|
215
|
-
this.view = view;
|
|
216
|
-
this.base = base;
|
|
217
|
-
this.provenanceByScope = provenance;
|
|
218
|
-
this.semanticRevision = undefined;
|
|
219
|
-
this.savedRuntime = hashJson(createSessionRuntime(snapshot, this.cwd, this.sessionId, view.lineage, provenance.session));
|
|
220
|
-
this.restoredOriginPending = true;
|
|
221
|
-
this.absentSharedScopes.clear();
|
|
222
|
-
return structuredClone(snapshot);
|
|
223
|
-
},
|
|
224
|
-
};
|
|
225
|
-
}
|
|
226
|
-
/** Restore and canonically accept one retained boundary as a single lifecycle operation. */
|
|
227
|
-
restoreBoundary(checkpoint) {
|
|
228
|
-
const prepared = this.prepareBoundaryRestore(checkpoint);
|
|
229
|
-
const snapshot = prepared.restore();
|
|
230
|
-
return { snapshot, publication: this.acceptRestoredOrigin(snapshot) };
|
|
231
|
-
}
|
|
232
153
|
/** Await a coherent read-only recovery view; this neither activates policy nor accepts publication authority. */
|
|
233
154
|
async refreshCurrentMemory(signal) {
|
|
234
155
|
signal?.throwIfAborted();
|
|
@@ -280,44 +201,9 @@ export class TemporalRuntime {
|
|
|
280
201
|
this.provenanceByScope = provenance;
|
|
281
202
|
this.semanticRevision = undefined;
|
|
282
203
|
this.savedRuntime = undefined;
|
|
283
|
-
this.restoredOriginPending = false;
|
|
284
204
|
this.absentSharedScopes.clear();
|
|
285
205
|
return snapshot;
|
|
286
206
|
}
|
|
287
|
-
/** Copy one retained source-session boundary over the child's current shared scopes. */
|
|
288
|
-
prepareBoundaryFork(source, checkpoint) {
|
|
289
|
-
const parent = Object.freeze({ ...source });
|
|
290
|
-
if (parent.id === this.sessionId || parent.key === this.sessionKey)
|
|
291
|
-
throw new Error("State Flow fork requires a distinct session identity and key");
|
|
292
|
-
const sourceRuntime = new TemporalRuntime(this.cwd, parent, this.root, undefined, this.historyLimit);
|
|
293
|
-
const prepared = sourceRuntime.prepareBoundaryRestore(checkpoint);
|
|
294
|
-
prepared.restore();
|
|
295
|
-
const snapshot = {
|
|
296
|
-
config: structuredClone(prepared.snapshot.config),
|
|
297
|
-
meta: {
|
|
298
|
-
step: 0,
|
|
299
|
-
...(prepared.snapshot.meta.bootstrap === undefined ? {} : { bootstrap: prepared.snapshot.meta.bootstrap }),
|
|
300
|
-
},
|
|
301
|
-
};
|
|
302
|
-
let consumed = false;
|
|
303
|
-
return {
|
|
304
|
-
snapshot: structuredClone(snapshot),
|
|
305
|
-
fork: () => {
|
|
306
|
-
if (consumed)
|
|
307
|
-
throw new Error("Prepared State Flow fork was already consumed");
|
|
308
|
-
consumed = true;
|
|
309
|
-
if (this.view)
|
|
310
|
-
throw new Error("State Flow fork target already has session storage");
|
|
311
|
-
const publication = this.initializeOrigin(snapshot, { allowCreateCwd: false, copy: {
|
|
312
|
-
stream: sourceRuntime.view.scopes.session,
|
|
313
|
-
provenance: sourceRuntime.artifactProvenance("session"),
|
|
314
|
-
} });
|
|
315
|
-
if (!publication?.revision)
|
|
316
|
-
throw new Error("State Flow fork requires existing shared scope storage");
|
|
317
|
-
return { snapshot: structuredClone(snapshot), publication };
|
|
318
|
-
},
|
|
319
|
-
};
|
|
320
|
-
}
|
|
321
207
|
initialize(snapshot, allowCreateCwd, expectedShared, newSessionOrigin = false) {
|
|
322
208
|
return this.initializeOrigin(snapshot, { allowCreateCwd, expectedShared, newSessionOrigin });
|
|
323
209
|
}
|
|
@@ -493,8 +379,6 @@ export class TemporalRuntime {
|
|
|
493
379
|
}
|
|
494
380
|
/** Prepare current shared state while retaining the exact accepted private publication basis. */
|
|
495
381
|
publicationCandidate(storage, current) {
|
|
496
|
-
if (this.restoredOriginPending)
|
|
497
|
-
throw new RevisionUnavailableError("State Flow restored origin must be accepted before patching memory");
|
|
498
382
|
current ??= storage.capture(this.cwd, this.sessionId, this.root, this.sessionKey);
|
|
499
383
|
const candidate = new TemporalRuntime(this.cwd, this.session, this.root, undefined, this.historyLimit);
|
|
500
384
|
candidate.transaction = storage;
|
|
@@ -546,20 +430,16 @@ export class TemporalRuntime {
|
|
|
546
430
|
async withStartTransaction(action, signal, allowCreateOrigin = false) {
|
|
547
431
|
return this.withPublicationTransaction((_candidate, publish, current) => action(current, (snapshot) => publish(snapshot)), { kind: "start", allowCreateOrigin }, signal);
|
|
548
432
|
}
|
|
549
|
-
/**
|
|
550
|
-
async
|
|
433
|
+
/** Copy the parent's current session memory into an unoccupied child; the caller rechecks native selection after waiting. */
|
|
434
|
+
async withForkTransaction(source, lifecycle, action, signal) {
|
|
551
435
|
signal?.throwIfAborted();
|
|
552
|
-
return this.withPublicationTransaction((_candidate, publish, selected) => action(selected, (snapshot) => publish(snapshot)), { kind: "
|
|
436
|
+
return this.withPublicationTransaction((_candidate, publish, selected) => action(selected, (snapshot) => publish(snapshot)), { kind: "fork", source: { ...source }, lifecycle: { mode: lifecycle.mode, ...(lifecycle.bootstrap ? { bootstrap: true } : {}) } }, signal);
|
|
553
437
|
}
|
|
554
|
-
|
|
555
|
-
async withForkTransaction(source, checkpoint, action, signal) {
|
|
556
|
-
signal?.throwIfAborted();
|
|
557
|
-
return this.withPublicationTransaction((_candidate, publish, selected) => action(selected, (snapshot) => publish(snapshot)), { kind: "fork", source: { ...source }, checkpoint: structuredClone(checkpoint) }, signal);
|
|
558
|
-
}
|
|
559
|
-
prepareForkCandidate(storage, source, checkpoint) {
|
|
438
|
+
prepareForkCandidate(storage, source, lifecycle) {
|
|
560
439
|
const sourceBase = storage.capture(this.cwd, source.id, this.root, source.key);
|
|
561
440
|
const parent = new TemporalRuntime(this.cwd, source, this.root, undefined, this.historyLimit);
|
|
562
|
-
|
|
441
|
+
if (!parent.loadCurrentMemoryBase(sourceBase))
|
|
442
|
+
throw new RevisionUnavailableError("State Flow fork parent memory is unavailable");
|
|
563
443
|
const base = storage.capture(this.cwd, this.sessionId, this.root, this.sessionKey);
|
|
564
444
|
const files = new Map(base.files.map((file) => [file.path, file.content]));
|
|
565
445
|
const paths = temporalScopePaths(this.cwd, this.sessionId, "session", this.root, this.sessionKey);
|
|
@@ -580,7 +460,7 @@ export class TemporalRuntime {
|
|
|
580
460
|
}
|
|
581
461
|
return {
|
|
582
462
|
candidate,
|
|
583
|
-
current: { config:
|
|
463
|
+
current: { config: { mode: lifecycle.mode }, meta: { step: 0, ...(lifecycle.bootstrap ? { bootstrap: true } : {}) } },
|
|
584
464
|
assertSource: () => assertTemporalFileBase(sourceBase, storage.capture(this.cwd, source.id, this.root, source.key)),
|
|
585
465
|
};
|
|
586
466
|
}
|
|
@@ -599,7 +479,7 @@ export class TemporalRuntime {
|
|
|
599
479
|
if (this.view)
|
|
600
480
|
throw new Error("State Flow fork target already has session storage");
|
|
601
481
|
}
|
|
602
|
-
if (!semantic && (!this.semanticRevision || !this.view || !this.base
|
|
482
|
+
if (!semantic && (!this.semanticRevision || !this.view || !this.base)) {
|
|
603
483
|
throw new Error("State Flow lifecycle transaction requires an accepted runtime");
|
|
604
484
|
}
|
|
605
485
|
};
|
|
@@ -607,8 +487,8 @@ export class TemporalRuntime {
|
|
|
607
487
|
assertAuthority();
|
|
608
488
|
if (kind === "patch" || allowCreateOrigin)
|
|
609
489
|
initializeFileStore(this.root);
|
|
610
|
-
if ((kind === "start" || kind === "
|
|
611
|
-
throw new RevisionUnavailableError(kind
|
|
490
|
+
if ((kind === "start" || kind === "fork") && !lstatSync(this.root, { throwIfNoEntry: false })) {
|
|
491
|
+
throw new RevisionUnavailableError(kind === "fork" ? "State Flow fork parent storage is unavailable" : "Current State Flow session storage is unavailable");
|
|
612
492
|
}
|
|
613
493
|
return withStorageTransaction(this.root, (storage) => {
|
|
614
494
|
assertAuthority();
|
|
@@ -616,15 +496,13 @@ export class TemporalRuntime {
|
|
|
616
496
|
let candidate;
|
|
617
497
|
let assertSource;
|
|
618
498
|
if (selection.kind === "fork") {
|
|
619
|
-
({ candidate, current, assertSource } = this.prepareForkCandidate(storage, selection.source, selection.
|
|
499
|
+
({ candidate, current, assertSource } = this.prepareForkCandidate(storage, selection.source, selection.lifecycle));
|
|
620
500
|
}
|
|
621
|
-
else if (selection.kind === "
|
|
501
|
+
else if (selection.kind === "start") {
|
|
622
502
|
candidate = new TemporalRuntime(this.cwd, this.session, this.root, undefined, this.historyLimit);
|
|
623
503
|
candidate.transaction = storage;
|
|
624
504
|
const base = storage.capture(this.cwd, this.sessionId, this.root, this.sessionKey);
|
|
625
|
-
current =
|
|
626
|
-
? candidate.prepareBoundaryRestoreBase(selection.checkpoint, base).restore()
|
|
627
|
-
: candidate.loadCurrentMemoryBase(base);
|
|
505
|
+
current = candidate.loadCurrentMemoryBase(base);
|
|
628
506
|
if (!current) {
|
|
629
507
|
if (!allowCreateOrigin)
|
|
630
508
|
throw new RevisionUnavailableError("Current State Flow session memory is unavailable");
|
|
@@ -646,7 +524,7 @@ export class TemporalRuntime {
|
|
|
646
524
|
assertSource?.();
|
|
647
525
|
// Patch preparation without semantic work preserves complete accepted cohorts; selection accepts origins with configured folding.
|
|
648
526
|
// New authority or wholly absent shared pairs still need normal atomic initialization; partial evidence already failed.
|
|
649
|
-
const writeSemantic = semantic && (kind === "start" || kind === "
|
|
527
|
+
const writeSemantic = semantic && (kind === "start" || kind === "fork" || accepted !== undefined || !this.semanticRevision
|
|
650
528
|
|| SCOPES.some((scope) => Object.keys(provenance?.[scope] ?? {}).length > 0)
|
|
651
529
|
|| candidate.base.files.some((file) => file.content === undefined));
|
|
652
530
|
// Every capability acceptance validates its captured cohort, including semantic no-ops.
|
|
@@ -658,7 +536,6 @@ export class TemporalRuntime {
|
|
|
658
536
|
this.provenanceByScope = candidate.provenanceByScope;
|
|
659
537
|
this.semanticRevision = candidate.semanticRevision;
|
|
660
538
|
this.savedRuntime = candidate.savedRuntime;
|
|
661
|
-
this.restoredOriginPending = false;
|
|
662
539
|
this.absentSharedScopes.clear();
|
|
663
540
|
published = true;
|
|
664
541
|
return publication;
|
|
@@ -668,21 +545,9 @@ export class TemporalRuntime {
|
|
|
668
545
|
return result;
|
|
669
546
|
}, signal);
|
|
670
547
|
}
|
|
671
|
-
/** Canonically accept a prepared retained-boundary origin before lifecycle-only persistence. */
|
|
672
|
-
acceptRestoredOrigin(snapshot) {
|
|
673
|
-
if (!this.restoredOriginPending)
|
|
674
|
-
throw new Error("State Flow has no prepared restored origin to accept");
|
|
675
|
-
const publication = this.publish(snapshot, true);
|
|
676
|
-
if (!publication)
|
|
677
|
-
throw new Error("State Flow restored origin produced no canonical publication");
|
|
678
|
-
this.restoredOriginPending = false;
|
|
679
|
-
return publication;
|
|
680
|
-
}
|
|
681
548
|
publish(snapshot, semantic = false, accepted, options = {}) {
|
|
682
549
|
if (!this.view || !this.base)
|
|
683
550
|
throw new Error("State Flow temporal publication is unavailable; restore or initialize before accepting a transition");
|
|
684
|
-
if (this.restoredOriginPending && !semantic)
|
|
685
|
-
throw new Error("State Flow restored origin must be accepted before runtime-only persistence");
|
|
686
551
|
// Explicit evidence updates still own a scope CAS, even if they equal the stale cache.
|
|
687
552
|
const provenanceScopes = SCOPES.filter((scope) => Object.keys(options.provenance?.[scope] ?? {}).length > 0);
|
|
688
553
|
const runtimeOnly = !semantic && accepted === undefined && provenanceScopes.length === 0;
|
|
@@ -4,9 +4,6 @@ import { type TransitionBoundary } from "./temporal.ts";
|
|
|
4
4
|
/** Missing operational capability is not evidence that a checkpoint target is invalid. */
|
|
5
5
|
export declare class RevisionUnavailableError extends Error {
|
|
6
6
|
}
|
|
7
|
-
/** Expired history cannot be restored, but explicit activation may use validated current memory. */
|
|
8
|
-
export declare class HistoryBoundaryExpiredError extends RevisionUnavailableError {
|
|
9
|
-
}
|
|
10
7
|
export type StateFlowMode = "active" | "passive" | "off";
|
|
11
8
|
export type InactiveMode = Exclude<StateFlowMode, "active">;
|
|
12
9
|
export declare function isStateFlowMode(value: unknown): value is StateFlowMode;
|
|
@@ -38,6 +35,8 @@ export interface StateFlowSnapshot {
|
|
|
38
35
|
meta: SnapshotMeta;
|
|
39
36
|
}
|
|
40
37
|
export type Snapshot = StateFlowSnapshot;
|
|
38
|
+
/** Failed Active selection retains its policy and fences inference until explicit recovery or an inactive choice. */
|
|
39
|
+
export declare function isActiveRestorationBlocked(snapshot: Snapshot): boolean;
|
|
41
40
|
export interface SessionRuntime {
|
|
42
41
|
config: SnapshotConfig;
|
|
43
42
|
meta: SnapshotMeta & {
|
|
@@ -85,5 +84,5 @@ export declare function retainedBoundaryCheckpoint(snapshot: Snapshot, boundary:
|
|
|
85
84
|
export declare function preRuntimeCheckpoint(mode: StateFlowMode): PreRuntimeCheckpoint;
|
|
86
85
|
/** Decode the retained-window checkpoint contract; legacy `enabled`/`{disabled:true}` markers map through `inactiveMode`. */
|
|
87
86
|
export declare function parseRetainedPiCheckpoint(value: unknown, inactiveMode?: InactiveMode): RetainedPiCheckpoint;
|
|
88
|
-
export declare function migrationFailure(data: JsonObject, error: string, mode?:
|
|
87
|
+
export declare function migrationFailure(data: JsonObject, error: string, mode?: StateFlowMode): Snapshot;
|
|
89
88
|
export {};
|
|
@@ -8,9 +8,6 @@ const MAX_LEGACY_VALIDATION_ATTEMPT = 7;
|
|
|
8
8
|
/** Missing operational capability is not evidence that a checkpoint target is invalid. */
|
|
9
9
|
export class RevisionUnavailableError extends Error {
|
|
10
10
|
}
|
|
11
|
-
/** Expired history cannot be restored, but explicit activation may use validated current memory. */
|
|
12
|
-
export class HistoryBoundaryExpiredError extends RevisionUnavailableError {
|
|
13
|
-
}
|
|
14
11
|
export function isStateFlowMode(value) {
|
|
15
12
|
return value === "active" || value === "passive" || value === "off";
|
|
16
13
|
}
|
|
@@ -26,6 +23,10 @@ export function readCheckpointMode(value, inactiveMode = "passive") {
|
|
|
26
23
|
return Object.hasOwn(value, "enabled") || !isStateFlowMode(value.mode) ? undefined : value.mode;
|
|
27
24
|
return typeof value.enabled === "boolean" ? value.enabled ? "active" : inactiveMode : undefined;
|
|
28
25
|
}
|
|
26
|
+
/** Failed Active selection retains its policy and fences inference until explicit recovery or an inactive choice. */
|
|
27
|
+
export function isActiveRestorationBlocked(snapshot) {
|
|
28
|
+
return snapshot.config.mode === "active" && snapshot.meta.validation?.attempt === 0;
|
|
29
|
+
}
|
|
29
30
|
export function validateSessionRuntime(value, cwd, sessionId) {
|
|
30
31
|
if (!isJsonValue(value) || !isObject(value) || Object.keys(value).sort().join(",") !== "config,meta"
|
|
31
32
|
|| !isObject(value.config) || !isObject(value.meta))
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ArtifactInvalidationReason } from "./artifact.ts";
|
|
2
2
|
import type { RecentTransitionWindow } from "./history.ts";
|
|
3
|
-
import type
|
|
3
|
+
import { type Snapshot } from "./snapshot.ts";
|
|
4
4
|
import { type SemanticState, type ScopedStates, type StateScope } from "./state.ts";
|
|
5
5
|
import type { ScopeRevisions, TransitionBoundary } from "./temporal.ts";
|
|
6
6
|
export declare const STATUS_KEY = "state-flow";
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { ownedTopLevelKeys } from "./ownership.js";
|
|
2
2
|
import { conciseDiagnostic } from "./protocol.js";
|
|
3
|
+
import { isActiveRestorationBlocked } from "./snapshot.js";
|
|
3
4
|
import { overlayStates, projectSemanticState } from "./state.js";
|
|
4
5
|
export const STATUS_KEY = "state-flow";
|
|
5
6
|
export function formatScopeRevisionVector(revisions) {
|
|
@@ -9,7 +10,7 @@ export function compactStatus(snapshot, _revisions, colorize) {
|
|
|
9
10
|
const { mode } = snapshot.config;
|
|
10
11
|
if (mode === "off")
|
|
11
12
|
return undefined;
|
|
12
|
-
return `${colorize("accent", "state-flow")} ${colorize("dim", mode)}`;
|
|
13
|
+
return `${colorize("accent", "state-flow")} ${colorize("dim", isActiveRestorationBlocked(snapshot) ? "active (blocked)" : mode)}`;
|
|
13
14
|
}
|
|
14
15
|
function countArtifacts(states, scope) {
|
|
15
16
|
return Object.keys(states[scope].artifacts).length;
|
|
@@ -62,6 +63,8 @@ export function detailedStatus(snapshot, diagnostics) {
|
|
|
62
63
|
const artifacts = (scope) => countArtifacts(diagnostics.scopeStates, scope);
|
|
63
64
|
const hasArtifacts = available && ["global", "cwd", "session"].some((scope) => artifacts(scope) > 0);
|
|
64
65
|
return [
|
|
66
|
+
`Mode: ${snapshot.config.mode}`,
|
|
67
|
+
...(isActiveRestorationBlocked(snapshot) ? ["Inference blocked: Active memory restoration failed. Use /state-flow-active for current memory or select Passive/Off for native context."] : []),
|
|
65
68
|
`Repository: ${diagnostics.repositoryRoot}`,
|
|
66
69
|
`Scope keys: CWD ${diagnostics.cwdScopeKey}; session ${diagnostics.sessionScopeKey}`,
|
|
67
70
|
...temporalLines,
|
|
@@ -6,6 +6,8 @@ export declare function stateFlowTelegramSectionSpecifiers(moduleUrl?: string):
|
|
|
6
6
|
export interface StateFlowTelegramSnapshot {
|
|
7
7
|
/** The current session's selected mode. */
|
|
8
8
|
mode: StateFlowMode;
|
|
9
|
+
/** Failed Active restoration fences inference without silently selecting an inactive mode. */
|
|
10
|
+
inferenceBlocked?: boolean;
|
|
9
11
|
/** Legacy branch step retained for existing adapter ports; current runtime ports also supply owner revisions. */
|
|
10
12
|
step: number;
|
|
11
13
|
revisions?: ScopeRevisions;
|
|
@@ -22,7 +22,7 @@ export function stateFlowTelegramSectionSpecifiers(moduleUrl = import.meta.url)
|
|
|
22
22
|
}
|
|
23
23
|
/** Main-menu section label shows only the current session mode. */
|
|
24
24
|
export function formatStateFlowSectionLabel(snapshot) {
|
|
25
|
-
return `🌀 State Flow: ${snapshot.mode}`;
|
|
25
|
+
return `🌀 State Flow: ${snapshot.mode}${snapshot.mode === "active" && snapshot.inferenceBlocked ? " (blocked)" : ""}`;
|
|
26
26
|
}
|
|
27
27
|
export const STATE_FLOW_MODES = ["off", "passive", "active"];
|
|
28
28
|
const STATE_FLOW_MODE_LABELS = { off: "Off", passive: "Passive", active: "Active" };
|
|
@@ -44,6 +44,10 @@ export function buildStateFlowSectionView(snapshot, callbackData) {
|
|
|
44
44
|
return {
|
|
45
45
|
text: [
|
|
46
46
|
`<b>🌀 State Flow:</b> <code>${snapshot.mode}</code>`,
|
|
47
|
+
...(snapshot.mode === "active" && snapshot.inferenceBlocked ? [
|
|
48
|
+
"",
|
|
49
|
+
"<b>Inference blocked:</b> Active memory restoration failed. Select Active to accept current session memory, or Passive/Off to permit native context.",
|
|
50
|
+
] : []),
|
|
47
51
|
"",
|
|
48
52
|
"<b>Mode</b> — choose a workflow (switching modes never erases stored memory):",
|
|
49
53
|
"",
|
|
@@ -40,10 +40,6 @@ export declare function adoptTemporalStreams(scopes: Record<StateScope, ScopeStr
|
|
|
40
40
|
export declare function createTemporalState(states: ScopedSemanticStates, id: string, historyLimit?: number): TemporalState;
|
|
41
41
|
/** Fold retained tails to a lower configured limit without inventing history. */
|
|
42
42
|
export declare function constrainTemporalState(view: TemporalState, historyLimit: number): TemporalState;
|
|
43
|
-
/** Select one scope at a proven retained boundary from its owning runtime lineage. */
|
|
44
|
-
export declare function selectScopeStreamAtBoundary(stream: ScopeStream, scope: StateScope, boundary: TransitionBoundary, historyLimit?: number): ScopeStream;
|
|
45
|
-
/** Select one still-retained causal boundary without consulting an external history store. */
|
|
46
|
-
export declare function selectTemporalStateBoundary(view: TemporalState, boundaryId: string, historyLimit?: number): TemporalState;
|
|
47
43
|
/** Current independent scope revisions; Effective uses this vector rather than inventing a scalar owner. */
|
|
48
44
|
export declare function temporalScopeRevisions(view: TemporalState): ScopeRevisions;
|
|
49
45
|
/** Exact scope semantics for authored staging; defaults must never become implicit writes. */
|
|
@@ -200,42 +200,6 @@ export function constrainTemporalState(view, historyLimit) {
|
|
|
200
200
|
validateTemporalState(next, historyLimit);
|
|
201
201
|
return next;
|
|
202
202
|
}
|
|
203
|
-
/** Select one scope at a proven retained boundary from its owning runtime lineage. */
|
|
204
|
-
export function selectScopeStreamAtBoundary(stream, scope, boundary, historyLimit = DEFAULT_HISTORY_LIMIT) {
|
|
205
|
-
validateHistoryLimit(historyLimit);
|
|
206
|
-
validateBoundary(boundary);
|
|
207
|
-
validateScopeStream(stream, scope, historyLimit);
|
|
208
|
-
if (boundary.position < stream.checkpoint.through.position) {
|
|
209
|
-
throw new Error("Selected State Flow history boundary predates the retained scope checkpoint");
|
|
210
|
-
}
|
|
211
|
-
const selected = structuredClone(stream);
|
|
212
|
-
const retained = selected.patches.filter(({ transition }) => transition.position <= boundary.position);
|
|
213
|
-
selected.revision -= selected.patches.length - retained.length;
|
|
214
|
-
selected.patches = retained;
|
|
215
|
-
validateScopeStream(selected, scope, historyLimit);
|
|
216
|
-
return selected;
|
|
217
|
-
}
|
|
218
|
-
/** Select one still-retained causal boundary without consulting an external history store. */
|
|
219
|
-
export function selectTemporalStateBoundary(view, boundaryId, historyLimit = DEFAULT_HISTORY_LIMIT) {
|
|
220
|
-
validateHistoryLimit(historyLimit);
|
|
221
|
-
validateTemporalState(view, historyLimit);
|
|
222
|
-
if (typeof boundaryId !== "string" || boundaryId.trim().length === 0)
|
|
223
|
-
throw new Error("State Flow temporal boundary identity must be non-empty");
|
|
224
|
-
const index = view.lineage.findIndex(({ id }) => id === boundaryId);
|
|
225
|
-
if (index < 0)
|
|
226
|
-
throw new Error("Selected State Flow history boundary is outside the retained temporal window");
|
|
227
|
-
const target = view.lineage[index];
|
|
228
|
-
const selected = structuredClone(view);
|
|
229
|
-
selected.lineage = selected.lineage.slice(0, index + 1);
|
|
230
|
-
for (const scope of SCOPES) {
|
|
231
|
-
const stream = selected.scopes[scope];
|
|
232
|
-
const retained = stream.patches.filter(({ transition }) => transition.position <= target.position);
|
|
233
|
-
stream.revision -= stream.patches.length - retained.length;
|
|
234
|
-
stream.patches = retained;
|
|
235
|
-
}
|
|
236
|
-
validateTemporalState(selected, historyLimit);
|
|
237
|
-
return selected;
|
|
238
|
-
}
|
|
239
203
|
/** Current independent scope revisions; Effective uses this vector rather than inventing a scalar owner. */
|
|
240
204
|
export function temporalScopeRevisions(view) {
|
|
241
205
|
const revisions = {
|
|
@@ -236,7 +236,7 @@ Explicit Start independently prepares the validated current same-session canonic
|
|
|
236
236
|
|
|
237
237
|
- Incompatible but independently valid live shared streams require a fresh origin, not invented cross-writer history.
|
|
238
238
|
- An empty session origin is allowed only with wholly absent private authority and initialization-safe branch provenance; incomplete or contradictory evidence stays closed.
|
|
239
|
-
- Initial physical fork copying
|
|
239
|
+
- Initial physical fork copying copies the parent's current memory; an already accepted child activates its own current memory without recopying its parent.
|
|
240
240
|
- Repeated Start while already active changes neither the current run nor pending response reconciliation.
|
|
241
241
|
|
|
242
242
|
### Passive persistence
|
|
@@ -259,7 +259,7 @@ Off, session/tree selection, shutdown or an accepted Start revoke obsolete Passi
|
|
|
259
259
|
|
|
260
260
|
**If persistence fails.** Accepted cached memory and all available native context are kept. The existing native passive-stop marker records `owner`, the selected inactive `mode`, `persistenceError` and `preserveContext: true`.
|
|
261
261
|
|
|
262
|
-
- This is a same-session policy override and write fence, not semantic authority or a substitute checkpoint. No canonical failure is repaired by resetting memory.
|
|
262
|
+
- This is a same-session policy override and write fence, not semantic authority or a substitute checkpoint. No canonical failure is repaired by resetting memory. A later explicit Passive joins pending read-only recovery, then retries the shared current-memory acceptance used by Start; only accepted publication and its newer checkpoint clear the fence.
|
|
263
263
|
- Memory-enabled tree/reload/resume with that pending marker use `TemporalRuntime.refreshCurrentMemory()` to validate and read current memory, without historical restoration or publication; unavailable evidence stays unavailable.
|
|
264
264
|
- A later accepted checkpoint supersedes the fence while keeping the context boundary for bootstrap.
|
|
265
265
|
- Repeating the same degraded choice is inert; another inactive choice updates only native policy and keeps the fence.
|
|
@@ -433,22 +433,16 @@ meta.json
|
|
|
433
433
|
- Start uses ordinary origin acceptance, including configured retention folding, not lifecycle-only semantic-byte preservation.
|
|
434
434
|
- The synchronous prepared Start API keeps its selected-cohort CAS contract but has no production caller.
|
|
435
435
|
|
|
436
|
-
|
|
436
|
+
Native startup, reload, resume and tree attachment use `withStartTransaction` over current same-session memory, applying the branch checkpoint's mode and run lifecycle. There is no API that rewinds memory to an older boundary.
|
|
437
437
|
|
|
438
|
-
|
|
439
|
-
- The candidate stays detached until publication. Expiry, incomplete evidence, cancellation and CAS failure never substitute current-head or empty memory.
|
|
440
|
-
- Origin acceptance applies configured retention and causal provenance pruning, as the synchronous restore does.
|
|
441
|
-
- Accepted memory is installed before control returns to caller checkpointing; a later failure cannot roll it back.
|
|
442
|
-
- Native startup/tree restoration uses it through the extension's owned restoration lifetime.
|
|
438
|
+
**`TemporalRuntime.withForkTransaction(source, lifecycle, action, signal?)`** pins the parent identity before waiting; `lifecycle` supplies only the child's mode and bootstrap.
|
|
443
439
|
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
- Under one exclusion it selects exact retained parent authority and captures an unoccupied child target.
|
|
440
|
+
- Under one exclusion it loads the parent's current memory and captures an unoccupied child target, so the copy never depends on the native fork step.
|
|
447
441
|
- The callback receives the child lifecycle (step zero, selected mode/bootstrap, no unfinished specification), rechecks native selection/policy, then publishes synchronously once.
|
|
448
442
|
- Parent evidence is revalidated immediately before child publication, and the child's exact file cohort is CAS-protected. Only acceptance installs child memory.
|
|
449
443
|
- Parent-private files stay untouched, shared provenance stays live, and configured folding still applies.
|
|
450
|
-
-
|
|
451
|
-
- Native fork adoption and Start's
|
|
444
|
+
- Missing, malformed or occupied evidence never becomes an empty or foreign copy.
|
|
445
|
+
- Native fork adoption and Start's fork retry use it.
|
|
452
446
|
|
|
453
447
|
### Run preparation and answer reconciliation
|
|
454
448
|
|
|
@@ -499,18 +493,17 @@ Without removals on a complete accepted cohort, publication preserves semantic/p
|
|
|
499
493
|
|
|
500
494
|
- Off attaches without constructing a temporal runtime, resolving semantic boundaries, reading memory or emitting recovery warnings; native mode/write-fence bookkeeping stays available.
|
|
501
495
|
- Deferred fork ownership is recorded in a child-owned `forkPending` native marker that survives extension recreation; accepted child initialization resets it.
|
|
502
|
-
- Explicit Passive
|
|
503
|
-
- Policy decoding is never proof that a historical boundary is valid.
|
|
496
|
+
- Explicit Passive or Active accepts validated current same-session memory (a pending fork copies the parent's current memory first); a write fence clears only after that acceptance.
|
|
504
497
|
- Automatic callbacks stay memory-inert in Off; installed-client acceptance gates are tracked separately in [BACKLOG](../BACKLOG.md).
|
|
505
498
|
|
|
506
499
|
**Memory-enabled restoration.** Memory-enabled native `session_start` and `session_tree`, and explicit acquisition after an Off attachment, await one extension-owned branch restoration.
|
|
507
500
|
|
|
508
|
-
1. **Synchronous prelude.** It revokes older selection work and pins the session id, file, header timestamp and CWD. It
|
|
501
|
+
1. **Synchronous prelude.** It revokes older selection work and pins the session id, file, header timestamp and CWD. It reads the branch's newest supported checkpoint through the recovery domain's pure `selectRetainedCheckpoint` only for mode and run lifecycle (unfinished specification, bootstrap); its boundary is never a memory revision to rewind to. Revision pointers and unsupported envelopes fail closed.
|
|
509
502
|
2. **While waiting,** the mode is the selected inactive policy, and private reads/publication report the pending selection.
|
|
510
|
-
3. **Acceptance.**
|
|
503
|
+
3. **Acceptance.** Same-session attachment accepts current memory through `withStartTransaction` under the checkpoint's mode; a fork copies the parent's current memory; a truly new auto-start origin uses `withStartTransaction` with creation authority (rechecking branch evidence after waiting); failed-Stop reload uses read-only recovery. Bootstrap and policy are derived after waiting and published in that single acceptance.
|
|
511
504
|
4. **Installation.** Only the current lifetime installs memory, checkpoint, continuation, tools and UI before yielding; a later native-write failure only warns.
|
|
512
505
|
|
|
513
|
-
**Failed Active selection.**
|
|
506
|
+
**Failed Active selection.** Unreadable current memory (missing, corrupt, partial or contradictory files), a failed fork copy or automatic Start failure retains the selected Active mode with unavailable-memory validation. The inference fence is derived from that mode and validation, not an independent flag under an inactive fallback. No Active lifecycle work or fabricated projection runs before validated acceptance; shared reads may remain available. Terminal and Telegram show `active (blocked)` with explicit recovery guidance. The live `context` hook calls public `ctx.abort()` before returning; merely throwing would let Pi continue with native history. Reloading while current memory stays unreadable reconstructs the fence. Accepted Start or accepted explicit Passive releases it; explicit Off permits native context but grants no memory authority. Signal-less inspection does not cancel or clear the fence.
|
|
514
507
|
|
|
515
508
|
**Interaction with mode choices:**
|
|
516
509
|
|
|
@@ -518,25 +511,21 @@ Without removals on a complete accepted cohort, publication preserves semantic/p
|
|
|
518
511
|
- Passive keeps independently owned restoration, new-session initialization and fork copying, including attachment requested by a now-cancelled Active waiter. Off cancels those owned waits and keeps only native policy/source bookkeeping for later explicit acquisition.
|
|
519
512
|
- The pending publisher applies the latest inactive policy inside its single acceptance. Passive can then patch memory without an artificial error fence, while Off exposes no model access. Repeated selections are inert with respect to that acceptance.
|
|
520
513
|
- Start joins retained Passive acquisition. After Off, non-fork Active goes directly through one cancellable current-head Start transaction, keeping the deferred native selection and local Off policy until publication.
|
|
521
|
-
- A superseding Passive cancels that activation and acquires
|
|
522
|
-
- Accepted activation reconstructs any native continuation from its proven current memory.
|
|
514
|
+
- A superseding Passive cancels that activation and acquires current memory itself. Native Abort silently withdraws Active, without inventing a write fence or losing later acquisition choices.
|
|
515
|
+
- Accepted activation reconstructs any native continuation from its proven current memory. Fork acquisition keeps its independent restoration owner.
|
|
523
516
|
- Selection changes, shutdown and native operation cancellation still revoke obsolete work. Real validation/publication failures stay unavailable rather than becoming empty memory.
|
|
524
517
|
- Start joins pending restoration, owns initial attachment and fork retry without cancelling itself, and is inert when the result is already active. A cancelled Start withdraws its join without cancelling independently owned restoration or Stop persistence.
|
|
525
518
|
- Read-only recovery uses detached candidates and rechecks cancellation and physical identity before host installation. Passive attachment cannot overwrite a newer cache established by an intervening patch or inspection.
|
|
526
519
|
|
|
527
|
-
|
|
520
|
+
Two synchronous runtime methods (`loadPassive`, `initialize`) remain supported for library consumers and local tests/benchmarks; production lifecycle wiring uses the awaited APIs. Their contracts and cancellation boundaries are documented in [library API compatibility](compatibility.md#state-flow-library-api-compatibility).
|
|
528
521
|
|
|
529
522
|
**What stays outside critical sections.** The advisory [continuation-candidate reader](#session-continuation) also awaits coherent capture, and current-head activation on an attached branch uses the awaited Start transaction. Raw precomputed replay keeps its selected-target guard, unlike current-head authored patch staging. Model inference, source acquisition and Git commands do not belong inside a canonical critical section; artifact freshness validation stays part of publication validation.
|
|
530
523
|
|
|
531
524
|
## Optional Git backup
|
|
532
525
|
|
|
533
|
-
Canonical files always own semantic persistence, current materialization and retained hot history. Installing Git beside the store does not change authority or enable cold semantic restoration.
|
|
534
|
-
|
|
535
|
-
**Artifact provenance across restore/fork.** Scope artifact provenance records only current evidence.
|
|
526
|
+
Canonical files always own semantic persistence, current materialization and retained hot history. Installing Git beside the store does not change authority or enable cold semantic restoration. Branch navigation attaches current memory, so artifacts keep their current semantics and current evidence together.
|
|
536
527
|
|
|
537
|
-
|
|
538
|
-
- Selected artifact semantics stay intact with unavailable evidence until a stable explicit read and compilation.
|
|
539
|
-
- Untouched paths, provenance-only refreshes of unchanged semantics, and live shared provenance stay usable.
|
|
528
|
+
**Artifact provenance across attachment and fork.** Scope artifact provenance records only current evidence. Attachment keeps current semantics with their current evidence, and a fork copies the parent's current session provenance, so no historical pruning is needed.
|
|
540
529
|
|
|
541
530
|
### Local backup commit
|
|
542
531
|
|
|
@@ -812,9 +801,9 @@ See the [fork contract](fork-contract.md) and [operating limits](usage.md#fork-s
|
|
|
812
801
|
|
|
813
802
|
Status is a projection of the selected runtime and semantic view, not a second store.
|
|
814
803
|
|
|
815
|
-
- Compact terminal status uses accent `state-flow` with dim `active` or `passive`, and is hidden in Off.
|
|
816
|
-
- Telegram main-menu status uses `State Flow: active`, `State Flow: passive` or `State Flow: off
|
|
817
|
-
- `/state-flow-status` keeps the `g#c#s#` vector beside concise diagnostics and blank-line-separated top-level semantic JSON.
|
|
804
|
+
- Compact terminal status uses accent `state-flow` with dim `active` or `passive`, and is hidden in Off. Failed Active restoration stays visible as `active (blocked)`.
|
|
805
|
+
- Telegram main-menu status uses `State Flow: active`, `State Flow: passive` or `State Flow: off`, with the same blocked qualifier for failed Active restoration and an actionable submenu notice.
|
|
806
|
+
- `/state-flow-status` reports selected mode and any inference fence explicitly, then keeps the `g#c#s#` vector beside concise diagnostics and blank-line-separated top-level semantic JSON.
|
|
818
807
|
- Mode is read directly from the session's selected enum, never derived from a pending patch or separate passive flags.
|
|
819
808
|
- Requested Global, CWD and Session Rich snapshots show their independent `#revision`, while Effective shows the vector. Global/CWD Rich views omit the empty structural response placeholder; Session and Effective expose the Session-owned response.
|
|
820
809
|
- Missing evidence stays unavailable instead of appearing empty.
|
|
@@ -832,7 +821,7 @@ Status is a projection of the selected runtime and semantic view, not a second s
|
|
|
832
821
|
- Memory-enabled inspection keeps its accepted-private/shared-refresh and failed-Passive cache rules.
|
|
833
822
|
- Off inspection uses a fresh disposable `TemporalRuntime`. Global/CWD use coherent shared reads; Session/Effective use `refreshCurrentMemory` and reject absent, incomplete or malformed same-session private evidence, rather than manufacturing an empty layer/revision or borrowing foreign private data.
|
|
834
823
|
- No reader installs the extension runtime/cache, changes native policy or canonical bytes, clears a carried write fence, acquires an unaccepted fork parent or replaces the selected historical target.
|
|
835
|
-
-
|
|
824
|
+
- Branch steps never select a past memory revision; inspection and acquisition always use current stored memory.
|
|
836
825
|
- The awaited `StateFlowTelegramInspectionPort` returns a `StateFlowTelegramInspection` containing state and matching revisions, plus an optional revocation signal. A result checks its revocation lifetime and physical owner immediately before presentation.
|
|
837
826
|
- Stop, selection changes and shutdown cancel obsolete reads without altering newer memory or clearing write fences.
|
|
838
827
|
|