The plan file format

The AI writes a JSON plan template; paqad-ai plan compile validates it, adds identity and a content hash, and stores plan.json in the evidence bundle. The reuse section is required. Acceptance criteria now live in the frozen spec.

  • Planning
  • Checked against paqad-ai 1.91.1
  • Reviewed 25 September 2026 by Eliyce

Two files: the template you write and the record paqad keeps

A plan exists in two forms. A schema here means the exact list of fields a file must have and the values each field may take.

  • The plan template is a JSON file the AI writes anywhere outside the evidence bundle. It holds only the content of the plan.
  • The plan record, plan.json, is written by paqad-ai plan compile <template.json> into the change's bundle at .paqad/ledger/feature-evidence/<change>/plan.json. paqad adds identity fields and a content hash, rejects any field it does not know, and then deletes the template.

Nobody should edit plan.json by hand. The content hash makes a hand edit detectable, and the AI is instructed never to write into a bundle folder.

Template fields

FieldRequiredContents
summaryYesA non-empty description of the approach.
titleNoA readable title. If the bundle still has a generic name, paqad renames it from the title.
stepsNoOrdered steps, each with id and description, and optionally module, classification and files.
modules_touchedNoModule names the change affects.
decisionsNoIDs of decision pauses (D-…) the plan depends on.
risksNoEach with a description and a mitigation.
reuseYes, in practiceWhat was checked before building. plan compile refuses a template without it.

Step fields in more detail:

  • classification is the verdict of the diff-minimizer skill: ac-satisfying (meets an acceptance criterion), necessary-setup, scaffolding or over-build.
  • files lists the files the step expects to touch, as project-relative paths. paqad uses it to tell a frontend change apart before any code exists.

The reuse section

The reuse object has three lists.

ListEach entry
consulted (at least one entry)source, query, hits, and optionally target (the package@version checked). source is one of existing-surface, index-query, reuse-catalog, module-doc, grep, framework-api, framework-docs.
reusing (may be empty)symbol and how, optionally file. A library symbol also sets package and version. paqad records a provenance value (asserted, unknown-dynamic or doc-derived) from its own index where it can.
new_constructs (may be empty)name and justification, optionally framework_checked with package, nearest and a verdict of reuse, extend, insufficient or absent.

An example template

{
  "title": "Relative dates on the invoice list",
  "summary": "Show 'due in 3 days' style dates using the existing date helpers.",
  "steps": [
    { "id": "S1", "description": "Extend formatIsoDate callers in the invoice list",
      "module": "invoices", "files": ["src/invoices/list.tsx"] },
    { "id": "S2", "description": "Add a new helper for relative dates",
      "module": "shared", "files": ["src/utils/dates.ts"] }
  ],
  "modules_touched": ["invoices", "shared"],
  "decisions": [],
  "risks": [
    { "description": "Time zones shift the day boundary",
      "mitigation": "Compute in the user's zone, cover with a test" }
  ],
  "reuse": {
    "consulted": [{ "source": "index-query", "query": "date format", "hits": 2 }],
    "reusing": [{ "symbol": "formatIsoDate", "file": "src/utils/dates.ts", "how": "call as-is" }],
    "new_constructs": [{ "name": "formatRelativeDate",
      "justification": "No existing helper handles the relative form" }]
  }
}

Step S2 says "add a new helper", so the template must declare a new construct. Leaving new_constructs empty would make plan compile refuse it.

What plan compile adds to the record

The stored plan.json contains everything above plus schema_version (1), doc_type (paqad.plan), issue, slug and ulid (taken from the bundle folder name, never from the AI), created_at, updated_at and content_hash.

What blocks a compile and what only warns

ResultCause
RefusedNo summary; no reuse section; empty consulted; a reused symbol not in the code index; a library claim without version or for a package not in your stack snapshot; a library symbol that is missing or deprecated at that version; a create step with no new construct; a new construct without a framework check when a framework is detected; any field outside the schema; no active change (run paqad-ai stage start planning first).
Warning onlyThe code index, stack snapshot or framework index has not been built, so a claim could not be verified.

All of these are machine checks. Whether the plan is a good plan remains the AI's judgement, and the reuse-or-create choices it cannot settle come to you as decision pauses (see Planning a change).

Where acceptance criteria and invariants live now

The plan says how the work will be approached. What the change must do lives in the frozen spec, specification.json, in the same bundle. Each acceptance criterion has an ID such as AC-1, given, when and then statements, and a proof_type of automated, manual or visual. Each invariant, a rule that must stay true, has an ID such as INV-1 and a confirmed flag. See From plan to frozen spec.

Earlier documentation described a YAML planning manifest with requirement_graph, execution_slices, verification_matrix, decision_log, doc_targets and regression_watch. Those types still exist inside the package, but in 1.91.1 no command or hook writes such a manifest.

Something on this page out of date or unclear? Open an issue on GitHub and name the page.