Recipe: Durable Local State Substrate¶
1. Scenario and Non-Scenario¶
Applicable: you need to persist an event log while guaranteeing order and integrity, rebuild or verify a derived snapshot, and inspect or recover a cooperative single-writer lock.
Not applicable: needs to define a workflow state machine in Foundation (forbidden); needs to resist same-privilege malicious processes (fencing does not promise this scenario).
2. Architecture Choice and Capability ID¶
Preferred capability: foundation.harness.state-store (append-only events, hash chain, snapshot, and lock inspect/recover). Event meaning and reducer transitions are owned by the caller.
3. Preconditions and Trust Boundary¶
STATE_GENESIS_DIGESTas the chain starting point.- Trust boundary: the event log is the sole authority, and the snapshot is a derived cache;
confirmOwnerTerminatedis an external trust anchor provided by the caller and does not promise resistance against same-privilege malicious processes.
4. Minimal Code¶
import { mkdtempSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import {
openStateStore,
appendEvent,
readEvents,
writeSnapshot,
readSnapshot,
inspectStateStoreLock,
recoverStateStoreLock,
} from "skill-family-harness-node";
const root = mkdtempSync(join(tmpdir(), "sf-state-"));
const store = openStateStore(root, { owner: "writer-a", expectedSchemas: [] });
appendEvent(store, { eventType: "demo", payload: { n: 1 } });
writeSnapshot(store, readEvents(store));
const snap = readSnapshot(store);
const lock = inspectStateStoreLock(root); // { owner, fencing, ageMs, recovering }
await recoverStateStoreLock(root, {
expectedOwner: lock.owner,
expectedFencing: lock.fencing,
confirmOwnerTerminated: true,
});
The code above shows the order of opening the store, appending events, writing a snapshot, inspecting the lock, and explicitly recovering.
5. Expected Output and Evidence¶
readEvents returns the appended events, readSnapshot returns the derived snapshot, and inspectStateStoreLock returns owner/fencing/ageMs/recovering. Evidence: packages/skill-family-harness-node/test/state-store.test.mjs (with 34 assertions).
6. Safety / Failure Negative Case¶
// Negative case: a second writer immediately receives store-locked
const other = openStateStore(root, { owner: "writer-b", expectedSchemas: [] });
// throws HarnessError: details.kind = "store-locked"
A broken hash chain, a mismatched lock fencing, or recovery missing confirmOwnerTerminated all fail-closed.
7. Copy-Paste Verification Command¶
node --test packages/skill-family-harness-node/test/state-store.test.mjs
8. Business Logic the Caller Continues to Own¶
- Event business meaning, reducer transitions.
- The
confirmOwnerTerminatedtrust anchor (confirm outside Foundation that the old writer has terminated).
9. Upgrade and Rollback Notes¶
- The state substrate provides only the mechanism; the business state machine / terminal state belongs to loop-agent.
- A crash-left lock requires external evidence-gathering before explicit recovery, with no silent lock-stealing; the system stays diagnosable as a lock-out rather than silently overwriting.