# Usage and recovery

For the concept and installation, start with the [README](../README.md). This guide covers operating State Flow; the [architecture](architecture.md) owns its internal contracts.

## Session behavior

### Active, passive and configured off

State Flow has three distinct model-facing states. **Active versus passive changes the agent's workflow, not the existence of disk memory.**

- **Active:** Both `read_state` and `patch_state` are available, subject to host restrictions and valid memory authority. Prompts require the agent to consolidate future-relevant results into state before ending an iteration. The final meaningful semantic patch preserves decisions, outcomes and continuation needed after the completed conversation is removed from model projection. The next iteration starts from accepted state and its new input, not the previous iteration's completed reasoning. Native trace remains inspectable; a clean model context does not mean deleting Pi history.
- **Passive:** Both memory tools remain available with the default passive configuration. Existing state is projected into context, and the agent may read or patch it as useful. Ordinary conversation continuity remains: State Flow does not impose the active iteration-ending context reset or the same mandatory consolidation pressure. Accepted patches still reach the same canonical store with the same validation and ownership guarantees.
- **Configured off:** Neither memory tool is exposed to the model, and State Flow injects no bootstrap/state context. This is distinct from passive mode, and does not mean deleting stored memory. With the current configuration, this means an inactive branch plus `passiveTools: false` and `passiveBootstrap: false`. `autoStart: false` only disables automatic activation of new sessions; it does not disable passive memory or override an already active resumed branch.

The two passive settings are independent: turning off only tools or only projection produces a deliberately partial integration, not the fully off state. Host restrictions and genuine unavailable/corrupt-memory fences are separate from these mode definitions.

**Completion is semantic, not ceremonial.** In active mode, necessary final state changes must be accepted before relying on state-only continuation. This does not require an empty `patch_state` when memory is already current, an extra model turn, or a special finalization tool. Each `patch_state` remains an inference barrier, not automatically the end of an iteration; the runtime separately persists the accepted ordinary answer as Session `response` and completes the run.

Here, “bootstrap from state” means constructing model context from durable memory. The implementation's `meta.bootstrap` is narrower: the one transition run that retains an existing conversation while active mode is first enabled and that conversation is compiled into state. It is not a flag that must be re-entered on every active iteration. Native compaction is also separate from model-context projection; its safety checks and usage threshold still apply.

Start/Stop change active/passive behavior and respect the configured passive capabilities. They do not choose a different storage algorithm or cancel creation of a fork's private memory. A fork receives its own initial Session state from the selected parent boundary, then lives independently in either mode. See [fork support](#fork-support-and-limits).

### Lifecycle operations

`/state-flow-start` enables the current Pi branch over validated current same-session memory, initializing absent storage when safe. No remote is required. Starting mid-conversation retains Pi's active context for one complete bootstrap run, during which the agent must compile future-relevant information into state. Repeating Start while already active leaves the in-progress run unchanged. On an attached branch, current-memory activation waits asynchronously for a coherent canonical cohort; pending repeats share the wait. Until acceptance, existing passive context, tools and any Stop fence remain in effect. Stop or selection can withdraw obsolete activation; an error after acceptance does not undo enabled memory.

- **New session:** Passive durable memory is available by default without starting an episode. `autoStart` promotes genuinely new sessions into active State Flow; an active new session has its own empty session layer and inherits global/CWD state, never another session's private continuation.
- **Resume:** Restores the selected session's stored enablement, state, and lineage. Agent-level `autoStart` does not override a resumed branch.
- **Tree navigation:** Restores the selected retained private boundary over live shared scopes without checking out or resetting the shared store.
- **Abort inference:** Stops generation or an outstanding response-publication wait while already accepted patches remain durable for continued work and corrected direction in the same session. Cancellation before response acceptance keeps the previous response and unfinished run; cancellation after acceptance never rolls it back. It does not require immediate remote replication.
- **Native boundary continuation:** A companion may continue through Pi's `turn_end` or `agent_before_settle` boundary without a new user prompt. State Flow keeps projecting current memory and accepted response across those requests and subsequent patches; it does not restore the completed specification or request another turn itself.
- **Stop:** Ends active episode semantics and returns to the configured passive bootstrap/tool combination. For a proven pre-runtime branch, it records only the existing disabled checkpoint in Pi, without creating canonical files or publishing the passive view; later Start or passive patch remains available. For an accepted runtime, it adopts unrelated shared-state changes without semantic/provenance writes or step changes. Same-session conflicts refuse canonical publication but do not prevent local disablement. Its frozen handoff uses the accepted view. It does not change passive or automatic-start policy.
- **If Stop cannot persist:** Active mode still turns off. The last accepted cache remains readable and all available native conversation is retained. Pi's native session records the local Stop and a write fence, without a replacement semantic checkpoint. Tree/reload/resume stay passive and load validated current same-session memory read-only; they never replay an older selection over another writer. Unavailable or corrupt memory stays unavailable. Repeated Stop does not retry or clear the fence. Explicit Start revalidates current memory and clears the fence only after canonical acceptance. This fallback requires Pi's native trace to remain writable; it does not repair storage. The canonical config may still say enabled, but the native failed-Stop policy overrides it on this branch.
- **Continue after Stop:** The same physical session retains a frozen state handoff, any interrupted current request and tool trajectory (including late results), and post-stop conversation. Outside an unfinished bootstrap, a proven active boundary excludes completed earlier conversation; when native split-turn compaction removed the original request anchor, Stop instead preserves the available summary and tools without reconstructing discarded raw input. An unfinished bootstrap keeps all available native context, or the earlier passive boundary it received; repeated Start/Stop cannot move that boundary past uncompiled conversation. An interrupted run retains its captured anchor even after Pi becomes idle, falling back to all available context when that anchor is unknown. Other extensions' custom context survives. Only Stop after a completed idle run retains just later conversation plus foreign custom context. Reload/resume/tree preserve this projection; new/forked physical sessions do not inherit it. Mid-tool Start and repeated Start/Stop retain the first user event already observed while disabled, so subsequent Stop does not mistake that busy run for idle. Active restart uses the retained projection for one bootstrap run.
- **Completed-history compaction:** After an accepted run settles without queued input, State Flow asks Pi for a native compaction boundary only when public context usage reaches 24,000 tokens. No extra model summary is requested; Pi keeps the complete latest run—from its original request through steering, tools, foreign context and final answer—in active history and retains the complete append-only JSONL/tree. State Flow uses the native first-user anchor, independent of image-normalization hints or later steering; images and earlier tool results remain available to the model. Uncertain projection retains available context without changing that anchor. A missing or ambiguous native anchor skips compaction instead of choosing the last steering message. On resume, native `buildContextEntries()` and TUI rendering omit the older completed prefix. Unknown or smaller usage skips the request, and custom Pi retention settings may still decline it benignly. Foreign custom context in the removed prefix, bootstrap/abort/error, Stop and pending input prevent State Flow-owned shortening; ordinary manual/threshold/overflow compaction remains native and may preserve unfinished work not yet patched into memory.

State Flow does not undo tool effects. After interruption or returning to an older branch, check the relevant workspace or external system before repeating consequential operations. Restored memory is not restored reality.

### Fork support and limits

Native fork replacement copies the source session checkpoint, retained patch tail and matching provenance into the new session's own storage. Global/CWD values and provenance stay current; applying a smaller `historyLimit` may fold shared tails under CAS. An earlier fork selection copies that point's private state, not the parent's later private work. Parent-private data/history remain intact; selected enablement is retained, so a stopped source does not become enabled automatically.

The child starts at step zero and a new temporal origin. Its copied tail obeys the configured retention limit, but pre-origin records are not additional aligned causal boundaries addressable through `effective[n]` or scoped paths. The child's own transitions build its hot window; owned checkpoints support normal reload/resume. Parent Stop projection is not inherited, including after child reload.

Initial copying requires a native fork start event, a regular canonical direct-parent session file, matching CWD/identity, a readable temporal source and an unused child namespace. Missing/unsafe evidence or CAS conflicts leave the copy unavailable rather than importing unrelated or newer private state. Explicit Start can retry an unaccepted copy in the same loaded fork after the cause is corrected.

Selecting a copied parent checkpoint through the child's `/tree` does not make it child-owned: historical restoration stays disabled without resetting existing child data. Select a child-owned checkpoint, resume the original session, or explicitly Start from the validated current child-owned memory; Start does not copy newer parent data. Cold recovery before the first child checkpoint, startup paths lacking the fork event, in-memory parent locators and cross-CWD imports remain outside this slice. File-only copying requires an exact still-available source cohort. See the [contract](fork-contract.md) and [SDK compatibility boundary](compatibility.md#public-host-seams); do not rewrite UUIDs or delete pointers to force recovery.

## Configuration

Optional global `config.json` at the root of the State Flow repository, normally `~/.pi/agent/state-flow/config.json`:

```json
{
  "autoStart": false,
  "passiveBootstrap": true,
  "passiveTools": true,
  "logging": false,
  "showSuccessfulPatches": true,
  "historyLimit": 7
}
```

The canonical store is `state-flow/` beneath the agent directory. Keeping configuration inside that repository removes the separate agent-level `state-flow.json`; SDK embeddings may still provide an explicit repository override.
- `autoStart`: Defaults to `false`. When `true`, genuinely new sessions use the same initialization as explicit Start, including fresh CWDs.
- `passiveBootstrap`: Defaults to `true`. Projects existing effective durable memory into ordinary model context without creating scopes, publishing, or starting an episode.
- `passiveTools`: Defaults to `true`. Exposes `read_state` and `patch_state` outside active episodes. Reads remain side-effect free; the first explicit patch may initialize absent canonical storage but never converts predecessor formats or enables an episode or State Flow compaction.
- `logging`: Defaults to `false`. When enabled, records rejected patches, preparation failures and accepted-answer reconciliation failures locally at `tmp/state-flow/logs.jsonl` beneath the agent directory. Asynchronous Git push failures are recorded there even when this setting is off.
- `showSuccessfulPatches`: Defaults to `true`. In interactive Pi, successful `patch_state` rows show only the applied pretty-printed JSON arguments, with blank lines between adjacent memory sections; set it to `false` to keep only the compact summary. Rejected calls still use ordinary error rendering; State Flow adds no private validation turn.
- `historyLimit`: Defaults to `7` and accepts integers from `0` through `100`. It counts accepted semantic transitions, **not elapsed time or conversation length**. It bounds materialized-history and scope patch-history offsets. Lowering it on reload/restore/fork folds excess tails forward without losing current state; selected boundaries outside the new window are unavailable. Zero retains only current checkpoints. Raising the limit affects only future retention and cannot reconstruct discarded history.

Settings are read once at extension load. After editing, use `/reload` or restart Pi. A missing file uses defaults without creating a configuration file; malformed JSON, unknown keys, or invalid values fail loading rather than silently selecting another store.

`PI_CODING_AGENT_DIR` changes both the default State Flow repository and its global configuration location. State Flow has no source-directory or Knowledge-root configuration. SDK repository overrides are documented under [embedding](architecture.md#embedding); an overridden repository owns its own root `config.json`.

### Diagnostic logging and privacy

Rejected-call records may contain exact attempted arguments and useful draft text, plus the error category and tool/call identity. Asynchronous push failures retain the available redacted Git error there regardless of `logging`; interactive warnings stay short and appear once per failure streak, then reset on success. If logging the push failure is unavailable, one warning exposes the available detail instead. Successful patches are not logged; reasoning bodies are excluded. Logs are not semantic state, scope metadata, Pi checkpoints, or publication input. If the log path overlaps a custom state repository, capture fails closed instead of committing it. A logging failure changes no accepted state and produces at most one local warning.

Logs remain local unless you move them; rotation/deletion is operator-owned. State Flow-authored errors and warnings display as one compact line: long operands are elided with `…` before prose so the operation, offending scope, target basename/suffix and actionable reason remain visible. Nested causes travel as text because Pi tool results need not retain `Error.cause`. Enabled diagnostic records keep the full target and rejected input for investigation. An explicit Start retry reports its final failure once rather than repeating its startup warning. Tool failures still keep the required blank line beneath their heading. Treat logs and state files as private. Removing a secret from current state does not erase older offsets, Git history, native sessions, or remote copies.

## Status and controls

`/state-flow-status` separates runtime configuration/metadata from semantic state. It reports:

- Selected CWD/session keys, internal step, independent scope revisions, temporal head, recovery failures, and available hot history.
- Per-scope retained patch tails and artifact counts, plus one JSON representation of effective global → CWD → session memory. Individual scope JSON is available through `read_state`, not duplicated in status.
- Already-known runtime hints or pending artifact invalidations; status does not discover or validate sources.
- Memory-bearing scopes. Promotion-shaped values receive no special interpretation.

Tail counts are not history depth: inherited records may predate the active origin. Failed inspection reports unavailable evidence, not invented empty state. Status is observational: it does not read source files, calculate fingerprints, create invalidations, or mutate semantic state.

The terminal indicator is `state-flow G15/C8/S31` only in active mode. Global, CWD and Session own independent semantic revisions; one atomic transition advances each materially changed scope once, including session-only response reconciliation. Effective has no scalar counter and uses the `G#/C#/S#` revision vector. When `pi-telegram` is available, its main-menu section shows that vector only while active and `State Flow: off` while passive. Requested owner-scope Rich snapshots show `#revision`; Effective shows the vector. During a failed-Stop write fence, inspection uses the accepted cache without a potentially conflicting refresh. Otherwise, Telegram inspection waits cancelably to load or refresh one coherent shared view, even when passive model tools are disabled. It does not initialize, publish, or advance storage. Data and displayed revisions belong to the same observation; absent/invalid memory stays unavailable. Stop, session/tree changes and shutdown cancel obsolete observations. The button acknowledges immediately, and a late failure appears in the menu instead of an expired callback popup. Start requested during a run waits for settlement; Stop currently applies immediately. The adapter is optional and the Pi commands remain available without it. Open implementation work is tracked in [BACKLOG.md](../BACKLOG.md).

## Storage and recovery

Use a dedicated directory. State storage and registered artifact sources have separate responsibilities:

```text
<agentDir>/state-flow/   global config, accepted state, and runtime metadata
<any exact registered path>   optional external source owned outside State Flow
```

Each scope materializes an anchored semantic-only `checkpoint.json` plus semantic-only lines in `patches.jsonl`. Scope `meta.json` holds temporal boundaries, CWD ownership where applicable, and runtime-owned artifact evidence. The session additionally uses `config.json` for behavior and `runtime.json` for branch/run recovery metadata; a full prompt is retained there only while its run is unfinished. CWD/session directories mirror Pi's native naming while validating canonical identities separately. See the [storage contract](architecture.md#storage-and-identity) for the exact layout.

### Missing, partial, and malformed storage

A checkpoint and tail are one semantic pair. If both live files for a global or CWD scope disappear, State Flow treats that complete absence as current empty shared reality. An authored `patch_state` applies to that empty basis under exclusion; normal file-cohort CAS creates the accepted pair without resurrecting cold values or orphaned compilation evidence. Raw precomputed replay still refuses a removed selected target rather than replaying stale normalized changes.

Exactly one surviving pair member is corruption and fails closed. Present malformed JSON, incomplete predecessor envelopes, semantic/metadata boundary mismatches, identity contradictions, and partial session runtime evidence also remain fail-closed and are not replaced. A missing or expired private retained boundary is unavailable; State Flow does not substitute Git history or newer private files.

After a selected-boundary failure, configured passive access may still expose current global/CWD memory, but it never grants access to the unavailable historical session layer or permission to publish an empty replacement. Historical session reads and every `patch_state` refuse without changing canonical files or appending substitute checkpoints. Stop remains available and records the native policy/write fence described above; a subsequent reload may expose validated current memory read-only, not the unavailable selected history. Status distinguishes a write fence from unavailable materialization.

**Explicit Start uses current memory, not unavailable history.** It independently validates the current same-session canonical cohort, preserving private memory, artifact provenance, revisions, step and available aligned history. Expired active/passive pointers and unfinished runtime work no longer block activation. A pre-runtime selection also keeps current accepted same-session memory rather than resetting it. Start bootstraps the conversation available on the selected Pi branch without resurrecting an old unfinished specification or claiming that expired historical private state was restored. Independently advanced shared scopes may require a new temporal origin; unavailable history is never invented. Exact-cohort CAS rejects a concurrent writer, and incomplete/corrupt storage remains untouched. An unaccepted native fork still retries its exact source rather than inventing child memory. When evidence is missing, repair it, select a retained boundary, or open a genuinely new Pi session.

Missing artifact provenance inside an otherwise complete scope `meta.json` means compilation evidence is unavailable while semantic state remains usable; removing the whole metadata file also removes temporal authority and fails closed. An unavailable registered source path does not prove that durable artifact routing was deleted, and external files are never created. State Flow has no durable push queue or publication-worker lease; failed replication is attempted again only after a later accepted turn. See the complete [filesystem recovery contract](filesystem-recovery.md).

### Canonical files and optional Git backup

Canonical scope/runtime files own current materialization and retained hot history regardless of Git availability. Pi checkpoints identify a retained semantic boundary, not a Git commit or arbitrary historical snapshot. Restart and branch restoration fail closed when the selected boundary has expired rather than substituting newer files as the selected past.

After an accepted turn has reconciled its response, Pi 0.87's final actionable `agent_before_settle` boundary may create one best-effort backup commit when Git is available. The local attempt completes before settlement continues. Its capture waits cancelably when Pi supplies an operation signal; on Pi 0.87 that signal is absent at settlement, so an occupied backup/storage mutex explicitly defers backup until a later accepted turn instead of trapping Abort. Deferral neither changes memory nor starts a push. Shutdown cancels and drains pending local attempts. If the attached branch has an explicitly configured remote/ref, State Flow starts a non-interactive asynchronous push of the exact current commit without force. Settlement does not wait for the network. Within one Pi process, an in-flight push per repository skips overlapping attempts; a later accepted turn retries the latest backup without a durable queue. Session shutdown waits for that repository's in-flight push to close or time out, suppressing push-failure reporting after shutdown begins. Commit or push failure is diagnostic-only; repeated push failures warn once per failure streak and remain locally diagnosable. Git availability never changes semantic authority, step, or retained lineage.

### Moving a store and the 0.17 format boundary

An SDK `repositoryRoot` override or a different `PI_CODING_AGENT_DIR` selects a location; it does not relocate existing state or retained history. Copy the complete canonical store while all writers are quiescent, or use a genuinely new Pi session for an independent store. Copying only current checkpoints without their tails and metadata cannot preserve retained boundaries.

Starting with 0.17, State Flow accepts only its canonical checkpoint/tail, temporal metadata, and separate session config/runtime contract; that boundary still applies to current versions. A canonical 0.17 scope written before independent revisions remains readable: its initial counter uses only the retained semantic tail and is persisted in metadata version 2 on the next owned write, without inventing folded ancestry. Version 1 remains readable; older writers refuse version 2 through the existing provenance-version fence, so cooperating instances should upgrade together. Predecessor checkpoint envelopes, combined session metadata, pre-intents checkpoints, `state.json`, hashed layouts, and semantic Pi checkpoint envelopes fail as unsupported without rewriting existing bytes. State Flow does not provide an in-place converter; external conversion or a fresh store is operator-owned.

### Conflicts and interrupted publication

`patch_state` asynchronously waits for a live cooperating writer, with native cancellation and no ordinary-contention deadline. It then applies authored Global/CWD operations to current canonical values: unmentioned fields survive, overlapping assignments follow successful acceptance order, and correct repeats return `State already current.` without another semantic revision. Session remains private; invalid/unavailable evidence and an independently changed private cohort still fail closed. Do not repeat external actions while memory publication waits.

Final-answer reconciliation also waits cancelably, then saves the response and completed-run lifecycle together over current shared memory. Stop, session/tree changes, shutdown and a superseding answer cancel obsolete waits without modifying newer work. A failed or canceled publication leaves the prior response and unfinished run intact; it never requests a repair inference.

Run preparation and missing-artifact maintenance wait cancelably before the first enabled inference, accepting current shared memory and lifecycle together. If preparation fails, State Flow aborts that native operation rather than sending a rejected or stale draft to the provider; previously accepted memory remains available. Cancellation can leave the new request without a specification checkpoint, so its native conversation is conservatively preserved through idle Stop/reload. Boundary continuation never replays a completed specification.

Telegram shared inspection also awaits exclusion read-only. Backup capture also uses the awaited API, with the no-signal settlement exception described above. Accepted-runtime Stop switches local policy and passive projection immediately, then awaits runtime-only persistence; pending repeats share that operation. Selection/shutdown or a successful Start cancel obsolete Stop work. An available host operation signal can cancel persistence without re-enabling mode; idle commands do not necessarily have that signal. Current-head Start similarly waits before enabling mode, with Stop/selection/shutdown cancellation and any available native operation signal. Startup, tree, auto-start, fork and failed-Stop reload restoration, including Start's initial attachment and fork retry, also await exclusion; until acceptance, mode stays passive and private memory reports the pending selection. Interrupted, malformed or unreadable locks are never stolen. Reconcile the owner rather than deleting locks or state directories merely because an operation is slow. File-cohort exclusion, exact prepared bytes and compare-and-swap checks are not kernel-atomic multi-file transactions against nonparticipating writers.

Fatal process termination can leave an incomplete canonical file cohort. Before repair, quiesce all store writers and preserve the complete store plus selected Pi retained-boundary references. Reconcile exact files against the last complete checkpoint/tail/metadata cohort; no automatic crash repair or power-loss durability is promised. Git backup has no semantic recovery authority.

Independently valid shared streams may require a fresh composed origin without inventing cross-writer history. Authored patches use that current basis; raw precomputed replay still refuses an advanced target instead of applying stale normalized changes. Rollback restores only bytes still matching that publisher's output and preserves detected external changes. See [performance evidence](performance.md) for measured contention and the [acceptance map](temporal-acceptance.md) for the tested boundaries; the [backlog](../BACKLOG.md) owns open implementation work.

## Lazy navigation and historical reading

Lazy bodies require explicit reads. Automatic state and recent-transition projections omit them, including lazy deletions; bounded `lazy_navigation` can still show the current layer's presence, path and key types. Explicit `session.lazy.releasePlan` reads the current value, while `session[3].lazy.releasePlan` reads the exact older value only if that causal boundary remains available. Filtering automatic visibility does not renumber history or erase already communicated native/user/tool/response text.

A missing path or runtime hint does not by itself require historical search. If the old value is unnecessary, continue without searching. If it can help the current task, the agent may choose a targeted historical read without separate user permission. Treat any found value as historical evidence, not automatically as current memory; do not restore deleted data without an independent reason. Do not scan all offsets or use repair inference or automatic hydration.

A dangling-reference hint accompanies only an unresolved single value read with verified current reference sources. Its `paths` are the owners of those references, not verified new locations of the requested data. It does not prove that the target existed, remains retained or was moved. The bounded lookup searches current state only and emits no lazy bodies. A proven stale reference may be repaired within touched work without resurrecting its target. Without a match, ordinary missing-path errors remain; keys, patch and batch projections retain their existing contracts.

Artifact freshness/invalidations and optional Skill acquisition hints keep their exact source/scope targets. Hints expose possibilities and diagnostic evidence; the current task determines whether action is necessary.

## Memory and source acquisition

The agent should use sufficient materialized knowledge before rereading files. Read for a concrete gap, exact-source/edit operation, evidenced invalidation, contradiction/failure, explicit request, or bounded maintenance—not simply because a new session began.

Artifact maintenance inspects only exact paths already registered in global, CWD, or session state, using `size + mtimeNs` without directory traversal or generic content hashing. Proven-missing paths are pruned from their exact owning scopes; unavailable, relative, directory, and symlink paths are preserved. Changed sources keep their artifact value and receive a runtime-only model `hint` until a stable read and same-path compilation updates hidden provenance. Only exact registered Pi Skill reads enter the separate hash protocol. Public Pi source provenance maps user Skills to global, project Skills to CWD and temporary Skills to session; matching compiled hashes require no update, and an uncompiled read remains volatile without blocking unrelated patches. Model patches cannot author or delete runtime provenance or hints.

The packaged `state-flow-guide` Skill answers concrete operational questions about reads, patches, inheritance, acquisition, completion, and recovery without initiating cleanup. The separate `state-flow-memory` Skill handles explicitly requested bounded curation and externally verified transfers. Feature/release/project boundaries may motivate recommending cleanup, not starting an audit. Ordinary handoffs reconcile touched state; neither Skill is a background maintenance loop. External transfers use the destination's native receipt and preserve the source whenever acceptance is uncertain. Artifact/compiler details and model-tool contracts belong in the [architecture](architecture.md#artifact-routing).
