Skip to content
← All spec documents

Plan state

Version 1.0. Status: Stable. This document specifies the machine-readable plan state layer of the Deep Work Plan methodology. The keywords MUST, MUST NOT, SHOULD, SHOULD NOT, and MAY are to be interpreted as described in RFC 2119.

Two JSON artifacts — manifest.json (the plan’s static identity) and state.json (the live, per-task execution state including validation-gate results) — that every plan MAY carry alongside its markdown files, and that unattended execution (see Agent protocol) and non-git workspaces (see Archetypes §3) MUST carry.

The markdown plan remains the human-readable source of truth. The JSON layer is a derived projection: it is regenerated by the agent at defined protocol points, never hand-edited, and never allowed to silently disagree with the markdown. Its purpose is interoperability — linting, conformance checking, diffing, dashboards, registry discovery, and synchronization with external session infrastructure — none of which can be built reliably on prose.

Why this exists

Through v1.1, plans were prose markdown only. That kept them auditable and agent-agnostic, but left nothing a tool could validate, diff, or consume: no conformance gate, no desync detection between README.md and PROGRESS.md, no way for a daemon or cloud session to know a plan’s state without parsing prose. v1.2 adds the JSON projection without demoting markdown — the projection is derived from the markdown, in the same way a lockfile is derived from a manifest.

Placement

A plan using the state layer has this layout:

.dwp/plans/PLAN_{name}/
├── README.md            ← human source of truth (unchanged)
├── PROGRESS.md          ← narrative log (unchanged)
├── PROMPTS.md           ← unchanged
├── manifest.json        ← static identity (written at materialization)
├── state.json           ← live state (rewritten at protocol points)
├── analysis_results/
└── {N}.task_{...}.md

manifest.json MUST be written exactly once, when the create flow materializes the plan, and MUST NOT change afterward except for a spec-version migration recorded in PROGRESS.md.

state.json MUST be rewritten by the agent at each of these protocol points: plan materialization (all tasks pending), task start (in_progress), each validation-gate run (gate record appended or updated), and task completion (completed, as part of the task completion protocol in DWP specification).

Both files MUST be written atomically: write to a temporary file in the same directory, then rename over the target. A crashed write MUST NOT leave a truncated JSON file in place.

When the layer is required

  • For interactive execution in a git repository, the state layer is RECOMMENDED for new plans and OPTIONAL for pre-v1.2 plans. A plan without it remains conformant.
  • For unattended execution, the state layer is REQUIRED.
  • In an agent workspace without git, the state layer is REQUIRED: state.json carries the recovery information that the git log carries in a repository.

manifest.json — plan identity

{
  "schema": "https://deepworkplan.com/schema/plan-manifest/v1.json",
  "spec_version": "2.2.0",
  "name": "PLAN_payment_webhooks",
  "title": "Add payment webhook handling",
  "archetype": "individual",
  "rigor": "standard",
  "created_at": "2026-06-09T14:00:00Z",
  "created_by": { "agent": "claude-code", "model": "claude-fable-5" },
  "tags": ["backend", "payments"],
  "task_count": 7,
  "parent_plan": null
}

schema, spec_version, name, archetype, rigor, created_at, and task_count are REQUIRED.

archetype MUST be one of individual, orchestrator-hub, agent-workspace.

rigor MUST be one of micro, standard, deep (see Proportional rigor).

parent_plan links a child plan to its orchestrator plan ({repo}:{plan_name}, or null).

created_by SHOULD identify the creating agent and model. It MUST NOT contain secrets, tokens, or user identifiers beyond a display name.

state.json — live execution state

{
  "schema": "https://deepworkplan.com/schema/plan-state/v1.json",
  "plan": "PLAN_payment_webhooks",
  "updated_at": "2026-06-09T16:42:10Z",
  "updated_by": { "agent": "claude-code", "model": "claude-fable-5" },
  "status": "in_progress",
  "completed_count": 2,
  "task_count": 7,
  "tasks": [
    {
      "id": 1,
      "file": "1.task_webhook_endpoint.md",
      "title": "Create webhook endpoint",
      "status": "completed",
      "started_at": "2026-06-09T14:10:00Z",
      "completed_at": "2026-06-09T15:02:33Z",
      "commit": "a1b2c3d",
      "gates": [
        {
          "command": "pnpm run test",
          "passes": true,
          "exit_code": 0,
          "last_run": "2026-06-09T15:01:50Z",
          "evidence": "42 passed, 0 failed"
        }
      ],
      "outcome": {
        "tried": ["raw body parsing via middleware"],
        "failed": ["initial signature check used wrong header"],
        "worked": "verify signature against X-Sig header before JSON parse",
        "notes": "stripe-style HMAC; see analysis_results/webhook_notes.md"
      }
    },
    {
      "id": 3,
      "file": "3.task_retry_queue.md",
      "title": "Add retry queue",
      "status": "in_progress",
      "started_at": "2026-06-09T16:30:00Z",
      "gates": []
    }
  ],
  "checkpoint": {
    "task": 3,
    "step": "instructions:4",
    "at": "2026-06-09T16:42:10Z",
    "note": "queue table migrated; worker loop not yet wired"
  },
  "blocked": null
}

Task entries

Every task file in the plan MUST have exactly one entry in tasks, keyed by its number (id) and filename (file).

status MUST be one of pending, in_progress, completed, blocked, skipped. skipped is valid only when the user explicitly removed the task from scope via refine; state.json MUST NOT be used to skip work silently.

A completed entry MUST carry completed_at and, where the plan commits, the short commit hash — this is the plan-to-code traceability link.

Gate records

Each run of a validation command SHOULD be recorded as a gate record: command, passes (boolean), exit_code, last_run, and a short human-readable evidence string (a summary line or a path under analysis_results/, never full command output).

A task MUST NOT be marked completed in state.json while any of its gate records has passes: false and no later passing run. Gate records are the machine equivalent of “never mark complete without evidence” — the pattern of a per-item passes flag guarding premature completion.

Outcome records as episodic memory

A completed task SHOULD carry an outcome record: what was tried, what failed, what worked, and free-form notes. Keep each entry to one line.

Outcome records make a finished plan retrievable episodic memory: an agent (or a memory-indexing platform) can later recall how a problem was solved, not just that it was. They feed the mandatory Skills & Agents Discovery task, which SHOULD read them when mining patterns. On platforms such as Hermes that index agent memory, outcome records in state.json make completed plans directly retrievable across future sessions.

Checkpoint and blocked state

checkpoint records the finest-grained resume point inside the current task: the task id, a free-form step locator, a timestamp, and a one-line note. An agent SHOULD update it whenever it pauses inside a task; it MUST update it before any planned interruption in unattended mode.

blocked is null or { "task": N, "reason": "...", "since": "...", "needs": "..." }. An unattended agent that hits a stop condition MUST populate blocked before halting — this is how a daemon’s next heartbeat, or a human, learns why the plan stopped.

Projection and reconciliation

The markdown MUST win every disagreement. If state.json says task 4 is completed but the plan README shows an unchecked box, the state file is stale.

A resuming agent MUST compare the README checkbox list against state.json before continuing. On desync it MUST regenerate state.json from the markdown (and the git log, where available), record the reconciliation in PROGRESS.md, and only then proceed.

The verify sub-skill MUST treat desync as a conformance finding: report which tasks disagree and in which direction.

Tools other than the executing agent MUST treat both JSON files as read-only.

Schema versioning

Both schemas are versioned by URL (/v1.json). Additive fields are allowed within a version; renaming or re-typing a field requires /v2.json and a migration note in the spec changelog. The spec_version field in the manifest pins the DWP spec version the plan was created under; an agent encountering a newer plan than its installed spec SHOULD say so rather than guess.