---
name: state-discipline
description: Enforce structured, deterministic state management in long-running workflows. Use in any multi-step workflow that persists state across iterations. Prevents LLM-managed state, in-memory-only state, and unstructured file-based state — the three failure modes that cause autonomous loops to lose track of where they are.
user-invocable: false
model-invocable: true
side-effects: code-edits
permission-profile: normal
triggers:
  - build
  - plan
  - "*"
---

# State Discipline

## Iron Law

```
STATE LIVES IN SQLITE OR ON DISK AS STRUCTURED FILES.
NEVER IN THE LLM'S CONTEXT WINDOW.
NEVER IN MEMORY ACROSS STEPS.
```

Context-window state is lost on restart, summarization, and context compaction. In-memory state is lost on crash. Only SQLite and structured files survive restarts, crashes, and context rotation.

## Recognize Your Own Rationalizations

- "I'll track the progress in my context." → Context is summarized and truncated. Progress state in context is lost exactly when you need it most — after a crash or a long run.
- "I'll use a JSON object in a variable." → In-memory variables don't survive the tool call boundary. Each tool invocation is a fresh execution context.
- "It's simpler to just write to a text file." → Unstructured text files can't be queried, can't be joined, and produce parse errors under concurrent access. Use SQLite.
- "I'll write the state management after the feature works." → State management is not a feature — it is the foundation. Without it, you can't resume, can't retry, and can't verify.

## When to Run

- Before designing any multi-step workflow that must survive restart
- When a workflow has been running for more than 2 iterations
- When implementing retry logic that requires tracking attempts
- When implementing any lock, queue, or work-item pattern

## The Four State Rules

### Rule 1: SQLite for structured state

Use `.sf/sf.db` (or a task-scoped DB) for any state with schema, ordering, priority, joins, or queries.

**Use SQLite when:**
- Tracking work items (pending/in-progress/done)
- Recording retry counts
- Storing key-value configuration that persists across steps
- Any state that needs to be queried or filtered

**Use structured files when:**
- The state is a single document (a plan, a spec, an evidence file)
- The state is append-only and never queried (logs)
- The state must be human-readable and is the primary artifact

**Never use:**
- In-memory variables for state that crosses step boundaries
- Free-form text files for state that needs to be queried
- LLM context window for state that must survive restart

### Rule 2: Schema before data

Define the schema explicitly before inserting any rows. The schema is the contract:

```sql
CREATE TABLE IF NOT EXISTS workflow_units (
    id TEXT PRIMARY KEY,
    status TEXT NOT NULL DEFAULT 'pending'   -- pending | in_progress | done | blocked
        CHECK(status IN ('pending','in_progress','done','blocked')),
    created_at TEXT NOT NULL DEFAULT (datetime('now')),
    updated_at TEXT NOT NULL DEFAULT (datetime('now')),
    error TEXT                               -- last error if status = blocked
);
```

Never add rows to an undefined table. Never use a table whose schema you haven't verified.

### Rule 3: Atomic transitions

State transitions must be atomic. Use SQLite transactions for multi-step transitions:

```sql
BEGIN;
UPDATE workflow_units SET status = 'in_progress', updated_at = datetime('now')
    WHERE id = :id AND status = 'pending';   -- conditional: only if still pending
-- do the work
UPDATE workflow_units SET status = 'done', updated_at = datetime('now')
    WHERE id = :id;
COMMIT;
```

Never set status to 'in_progress' in one statement and 'done' in another without a transaction — a crash between the two leaves inconsistent state.

### Rule 4: Resume from state, not from memory

Every workflow step must be resumable from the DB alone:

```sql
-- Find the next pending unit (resumable from cold start)
SELECT * FROM workflow_units
WHERE status = 'pending'
    AND NOT EXISTS (
        SELECT 1 FROM workflow_units dep
        JOIN unit_deps d ON d.depends_on = dep.id
        WHERE d.unit_id = workflow_units.id AND dep.status != 'done'
    )
ORDER BY priority DESC, created_at ASC
LIMIT 1;
```

If you cannot reconstruct "where the workflow is" from a single SQL query, the state model is wrong.

## State Inventory Checklist

Before implementing a multi-step workflow, produce this inventory:

```
State item:      <what needs to be remembered>
Lifetime:        <step | iteration | session | permanent>
Schema:          <table + columns, or file path + format>
Read pattern:    <how it is queried>
Write pattern:   <when and how it is updated>
Conflict rule:   <what happens if two processes write simultaneously>
Recovery:        <how to detect and fix corrupt state>
```

## Completion Criteria

- [ ] All cross-step state is in SQLite or structured files
- [ ] Schema is defined before any data is written
- [ ] All state transitions are atomic (transactions for multi-step)
- [ ] The workflow is resumable from the DB alone after a cold restart
- [ ] No state stored only in context or in-memory variables
