跳转至

简体中文

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_DIGEST as the chain starting point.
  • Trust boundary: the event log is the sole authority, and the snapshot is a derived cache; confirmOwnerTerminated is 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 confirmOwnerTerminated trust 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.