Writing specs and plans

Specs and plans

Create them

phax artifact new spec <slug>                     # docs/specs/<YYMMDDHHMM>-<slug>.md
phax artifact new plan <slug> --spec <spec path>  # docs/plans/<YYMMDDHHMM>-<slug>-plan.md

phax names the file from the current UTC minute and the slug, with a Draft status, and refuses any other name (exit 12). A plan carries its spec's slug and names it as its source-spec; --spec can be left out when a plan has no spec. Fill the file in with your agent: the phax-spec and phax-planning skills hold the formats, and point at the right sections — requirements and acceptance criteria for a spec; for each plan phase, its instructions, the files it creates and edits, its gate and its commit. examples/hello-world/plan.md is a small worked plan.

Let phax write them

With --headless, phax runs the authoring session itself from a brief: it gives the agent the skill and the document's JSON Schema, accepts a valid document as the session's only output, renders it to Markdown, keeps the document as a .json sidecar beside it, and commits both. A plan written this way is already extracted, so phax run never extracts it again.

phax artifact new spec greet --headless --brief brief.md
phax artifact new plan greet --headless --brief brief.md --spec docs/specs/2610041200-greet.md
cat brief.md | phax artifact new spec greet --headless --brief -
authoring spec greet — claude-opus-5-5 / high
created docs/specs/2610041200-greet.md (Draft, headless)
sidecar docs/specs/2610041200-greet.json
commit a1b2c3d — docs(specs): draft greet
record authoring/2610041200-greet

--model and --effort choose the model, before authoring.spec|plan in phax.json and the catalog's default. The last line names the session's record. It exits 5 when the session's output is not a valid document, 8 on a provider rate or usage limit, and 12 when the slug, the brief or --spec is wrong. phax artifact schema spec|plan prints the document's JSON Schema. Headless authoring is experimental: the two document formats may still change.

If you edit the Markdown of a headless document, edit its sidecar too: phax artifact status tells you whether the body is still the sidecar's rendering, and approving a document whose body has diverged is refused.

Check a plan

phax plans lint docs/plans/2610041201-greet-plan.md

A read-only check, with no model: the plan's structure, its planned files against the working tree and against earlier phases, the commands it needs against phax.json, each phase's model and effort against the catalog, and the findings of your plan auditor if you have one. It exits 1 on any error; the auditor's findings are only warnings.

Move them through their lifecycle

CommandFromTo
phax artifact approve <path>Draft; Stale (plan); Approved again to record a revisionApproved
phax artifact stale <path>Approved (plan)Stale
phax artifact reopen <path>Stale (plan)Draft
phax artifact complete <path>Approved; Stale (plan)Completed
phax artifact abandon <path>Draft, Approved; Stale (plan)Abandoned
phax artifact status <path>any— prints the status and the legal transitions

Each transition rewrites the status in the file's frontmatter and commits it on its own, and refuses when the files it writes have uncommitted changes. Completed and Abandoned move the file, and its sidecar, into the folder's archive/. Approving records the approval in docs/specs/approvals.json or docs/plans/approvals.json, with the commit it was made against; approving a plan is refused while its spec's approval is missing or the spec has changed since. A run completes its own plan, and its spec where it can, on the run's branch.

Keep several plans in step

phax plans status                          # every Approved plan: fresh, or stale and why
phax plans status --apply                  # mark the stale ones Stale
phax plans overlap <plan> <plan>...        # which plans can run side by side without conflicts
phax plans overlap --landed <run> <plan>   # which plans a landed run's real diff touches
phax adjust-plan <plan> --landed <run>     # an agent session that updates a plan to what landed

plans status compares each plan with what its approval was made against: spec-changed when its spec changed, self-changed when the plan did, ground-changed when files it plans to touch changed since, missing-record when there is nothing to compare with. It reports and exits 0; --apply makes the change. plans overlap compares the files plans declare (file by file, not line by line); with --landed, it uses the files a finished run actually changed — run it before you archive that run. adjust-plan asks before it edits and commits anything.