Skip to content
Back to skills

Phax Planning

ASecurity

Write or review a plan.md that `phax run` extracts and `phax plans lint` checks — phase structure, required fields, model/effort, commit metadata.

  • 2 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added October 19, 2026
ai-agentstypescriptgoshelldockerterraformgitapisecurity

Works with

  • claude code
  • terminal
  • cli
  • api

Security analysis

A100/100

Scanned October 19, 2026

npx -y skills add lbdremy/phax --skill phax-planning --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Phax Planning?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Phax Planning
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/lbdremy-phax-planning/badge)](https://www.skillsdirectory.com/skills/lbdremy-phax-planning)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: phax-planning
description: Write or review a plan.md that `phax run` extracts and `phax plans lint` checks — phase structure, required fields, model/effort, commit metadata.
---

# phax planning skill

Use this skill when you are asked to write or review a `plan.md` that
`phax run` will extract and `phax plans lint` will check.

## What phax expects

`phax run` performs the extraction, turning your `plan.md` into `phax-plan.json`
in two stages:

1. **Deterministic first.** phax parses the Markdown structure directly (an
   mdast tree) and pulls out every required field. This is instant, free, and
   reproducible — no model call. It succeeds only when the plan follows the
   exact structure this skill describes: the `## phase-NN — <Title> {#anchor}`
   headings, the `**Recommended model:**` / `**Recommended effort:**` lines, the
   three planned-file sections, and the commit subsections. Authoring plans to
   this shape is what keeps extraction on the fast path.
2. **LLM fallback.** If the deterministic parse fails for any reason (a missing
   section, a malformed heading, prose where a list was expected), phax falls
   back to passing your `plan.md` to Claude Code with the `phax-plan.json` JSON
   Schema and asking it to return only structured JSON. A warning records why
   the deterministic path was skipped, so a plan that silently drops off the
   fast path is visible.

Either way, extraction succeeds only when every required field is present and
unambiguous, and it fails loudly when required data is missing — it never
guesses.

Each phase also declares the files it expects to touch. After a phase commits,
phax reconciles those declarations against the actual git diff and asks the
executing agent to explain any deviation in its handoff (see
[End-of-phase file reconciliation](#end-of-phase-file-reconciliation)).

## Lint before you run

`phax plans lint <plan>` reports every mechanical defect of a plan before a run
touches it — read-only, and model-free, so it never falls back to the extraction
model. Five checks: **structure** (every field the deterministic parse requires,
reported in full rather than stopping at the first, plus an error for a phase
number missing from phase-01 … phase-NN, such as phase-02 in a plan with phase-01
and phase-03), **files** (the planned
create/edit lists walked in phase order against the working tree — an edit of a
file no earlier phase creates, or a create of a file that already exists, is an
error), **commands** (the plan's required commands against `security.agentCommands`
and the gate profile), **models** (each phase's model/effort against the routing
catalog), and **advisory** (when `phax.json` registers a `planAuditor`, every
finding it returns about the plan's shape — one warning per phase it names). It
exits 1 when any finding is an error; advisory findings are always warnings, so
they never fail the lint — treat them as planning advice. With no auditor
registered, or on a plan the deterministic parser cannot read, there are no
advisory findings. A `Draft` plan lints like any other — run it while drafting,
not only before approval. The run preflight (`phax run`, `phax resume`) also
refuses, with exit 11, a phase whose section it cannot locate in plan.md; a phase
is located by its `## phase-NN` id heading or by its `{#anchor}`.

## Run title and short name

The plan's single top-level `# ` heading is the run title. The extraction
derives both extracted run-level fields from it:

- `run.title` — the heading text verbatim.
- `run.shortName` — the same text slugified (lowercase, hyphen-separated). The
  short name is the run's identity: it names the run folder and the Git branch
  (`phax/<shortName>`).

Write exactly one `# ` heading per plan and keep it short and descriptive — a
long title becomes a long branch slug. Do not add a separate name or namespace
field; the top-level heading is the sole source of the run's identity.

## Plan frontmatter block

Create a repo-tracked plan with
`phax artifact new plan <slug> --spec <spec path> --last|--not-last`
(omit all three when there is no source spec; both flags are refused without `--spec`): it names the file
`docs/plans/<YYMMDDHHMM>-<slug>-plan.md` from the clock — the slug must equal the
source spec's slug, which `phax plans lint` checks — and writes the frontmatter
block; you fill in the body. Never compute the stamp yourself, and refer to a plan
by its slug in prose, commits and pull requests, never by its stamp.

Every plan carries a YAML frontmatter block at offset 0, before the `# ` title:

```markdown
---
status: Approved
source-spec: docs/specs/<YYMMDDHHMM>-<slug>.md
completes-spec: false
---
```

- **`status`** — the plan's lifecycle status, one of `Draft`, `Approved`,
  `Stale`, `Abandoned`, `Completed`. phax **enforces the run gate: only an
  `Approved` plan can run** — `phax run` refuses any other status (naming the
  remedy) before extraction. Approve a repo-tracked plan with
  `phax artifact approve docs/plans/<YYMMDDHHMM>-<slug>-plan.md`; for a loose `plan.md`, set
  the key by hand. `approve` takes `--by operator` or `--by machine:<grant>`, required
  without a terminal. `phax artifact reopen` moves an `Approved` or `Stale` plan back to
  `Draft`. Extraction is never gated, so a `Draft` still extracts for preview.
- **`source-spec`** — the spec this plan implements
  (`docs/specs/<YYMMDDHHMM>-<slug>.md`), or `null` when there is no source spec. It is the
  lineage anchor for staleness tracking.
- **`completes-spec`** — `true` when this plan is its spec's last and its run completes
  the spec, `false` when more plans of the spec follow. Required when `source-spec` names
  a spec; absent (refused) when it is `null`. Every plan of a spec except the last says
  `false`. The chain gate still applies on top of `true`. The value is fingerprinted, so
  changing it on an Approved plan makes the plan stale. `--last` writes `true`,
  `--not-last` writes `false`.
- **`approved`** — optional; a mapping with `date` and `baseline` written by
  `phax artifact approve` when it stamps the approval. Absent on a plan that has
  never been approved; `reopen` clears it.

This block is frontmatter, not preamble prose. It is **not** extracted into
`phax-plan.json` — it governs the run gate and lineage, not phase structure — but
the extractor skips the block before parsing the plan body, so keep it present
and accurate on every plan.

## Per-phase field set

Each phase section must contain all of the following. Fields marked **extracted**
are pulled by the extraction into `phax-plan.json`; the rest are
informational for the executing agent.

| Field                    | Location in section                                     | Extracted? |
| ------------------------ | ------------------------------------------------------- | ---------- |
| `id`                     | Derived from the heading (`phase-01` → `"phase-01"`)    | yes        |
| `title`                  | Section heading after the dash                          | yes        |
| `model`                  | `**Recommended model:** <model-id>` line                | yes        |
| `effort`                 | `**Recommended effort:** low\|medium\|high` line        | yes        |
| `planMarkdownAnchor`     | `{#phase-NN-<slug>}` in the heading line                | yes        |
| `plannedFilesToCreate`   | `### Planned files to create` list                      | yes        |
| `plannedFilesToEdit`     | `### Planned files to edit` list                        | yes        |
| `optionalFilesToEdit`    | `### Optional files that may be edited` list            | yes        |
| `commit.subject`         | `### Commit subject` subsection                         | yes        |
| `commit.body`            | `### Commit body` subsection                            | yes        |
| Objective                | Opening paragraph of the phase section                  | no         |
| Detailed instructions    | `### Detailed instructions` bullet list                 | no         |
| Boundary contracts       | `### Boundary contracts` subsection                     | no         |
| Test strategy            | `### Test strategy` subsection                          | no         |
| Implementation order     | `### Implementation order` subsection                   | no         |
| Excluded scope           | `### Excluded scope` list                               | no         |
| Verification             | `### Verification` (names the `phax.json` gate profile) | no         |
| Expected handoff content | `### Expected handoff content`                          | no         |

The run-level field `requiredCommands` (extracted from `## Required commands` in
the plan preamble) is **not** per-phase — it applies to the whole run. See
[Required commands declaration](#required-commands-declaration).

The three planned-file arrays are **required**: the section must be present even
when it is empty (write `- (none)` for an empty list). The extraction pulls
all three into `phax-plan.json`, where they back the end-of-phase file
reconciliation.

## Phase section structure

```markdown
## phase-NN — <Title> {#phase-NN-<slug>}

**Recommended model:** <model-id>
**Recommended effort:** low | medium | high

<Objective — one short paragraph: the user- or system-visible capability this
phase adds.>

### Detailed instructions

- <what to implement, in enough detail for the chosen model/effort to succeed>

### Planned files to create

- <repo-relative path you expect to NOT exist before the phase>

### Planned files to edit

- <repo-relative path you expect to modify>

### Optional files that may be edited

- <repo-relative path that may need a touch depending on implementation>

### Boundary contracts

<consumer/producer contracts this phase crosses; omit if it crosses none>

### Test strategy

<what to test, at which layer, and which tests to write before implementation>

### Implementation order

<recommended order, usually core-to-surface>

### Excluded scope

- <what is explicitly NOT in this phase>

### Verification

<the phax.json gate profile that verifies this phase — do not invent commands>

### Expected handoff content

<what the agent must record in phase-handoff.md, including an explanation for
any file-plan deviation phax flags>

### Commit subject

<single-line conventional-commit subject>

### Commit body

<commit body explaining the change>
```

## Heading format

```
## phase-NN — <Title>  {#phase-NN-<slug>}
```

- `NN` is zero-padded to two digits.
- `<slug>` uses only lowercase letters and hyphens.
- The anchor must be on the same line as the heading.
- The extraction derives `id` as `"phase-NN"` (e.g. `"phase-01"`, matching
  `/^phase-\d{2}$/`) and `planMarkdownAnchor` from this line, and errors without
  the anchor.

## Planned files and end-of-phase reconciliation {#end-of-phase-file-reconciliation}

After a phase commits, phax compares the files it actually changed
(`git diff --name-status HEAD^ HEAD` in the phase worktree) against the phase's
`plannedFilesToCreate`, `plannedFilesToEdit`, and `optionalFilesToEdit`, and
writes a reconciliation report into the phase folder. phax computes this
deterministically — the agent does not author it. Touching a file from the
optional list is never flagged. Anything else that diverges — a planned file left
untouched, or an unplanned file created or edited — is a deviation the executing
agent must explain in `phase-handoff.md`.

Write the file lists to make this signal useful:

- List every file you genuinely expect the phase to create or edit. Accuracy
  beats optimism — an over-long list produces noise; an under-list produces
  false deviations.
- Use the optional list for files that may need a touch depending on
  implementation details (test factories, barrel/`index` files, route tables).
- Tests are files — list them in the create/edit sections too.
- Do not list files you only read. Reconciliation is about writes.
- Use repo-relative POSIX paths, exactly as they appear in `git`.

The planned files to create and edit (not the optional list) also leave phax as
facts. A gate step that declares `"input": "gate-request"` receives every phase's
planned files on stdin, and so does a brief provider. A report step can then
let a finding pass in this phase when a later phase plans to touch its file. A
phase's brief covers that phase's planned files. So, when a later phase is meant to
finish what an earlier one starts, list the shared file in the later phase's
create or edit section, not under optional. Otherwise a report-step gate sees
nothing still to come, and fails the earlier phase.

### Editing skill files

Claude Code protects `.claude/`, so a secure phase can write a `.claude/skills/...` file
only when that exact file is listed in the phase's planned-file sections. Declare every
skill file the phase writes, including new files such as `references/*.md`; undeclared
siblings, glob paths, and other `.claude/` paths stay denied. A plan that declares skill
files must be run with `phax run --allow-skill-edits`, or the preflight refuses it (see
`docs/security.md`).

## Boundary contracts (informational)

When a phase crosses an architectural boundary (page/cli/surface → view-model,
view-model → application command, command → port, …), describe the contract in
consumer/producer terms: who needs something, who provides it, and the stable
shape between them. Be strict on the semantic need, adaptable on the exact
interface shape. These are not extracted (human-readable only). Omit the section for a phase that crosses no boundary.

## Test strategy (informational)

Map tests to the layer they exercise and pick the cheapest reliable test for
that layer:

| Layer                     | Preferred test type                       |
| ------------------------- | ----------------------------------------- |
| Domain                    | Unit tests                                |
| Application command/query | Unit or integration tests with fake ports |
| Ports                     | Type/contract tests                       |
| Adapters                  | Integration tests                         |
| ViewModel                 | Integration tests                         |
| View                      | Component/unit tests                      |
| Page/route/CLI            | E2E or smoke tests                        |

Mark which tests to write **before** implementation. Do it for stable contracts
and critical behavior (domain invariants, application commands, ports, bug
fixes, regressions); skip strict test-first for exploratory UI and scaffolding.

## Planning doctrine

- **Plan outside-in.** Start from the user-visible need and trace the path it
  must take through the system (page → view-model → command → domain → port →
  adapter → storage). This is how you discover which files each phase touches.
- **Implement inside-out.** Order the work core-to-surface (domain → ports →
  command → adapter → view-model → view → route) so the surface never claims a
  behavior the core does not yet provide.
- **Verify outside-in.** End by checking the user-visible behavior works end to
  end, then let the gates confirm it.

## Technical arbitrations

Planning is where technical trade-offs get decided — contract shapes, phase
decomposition, which layer owns a behavior, model/effort per phase. The spec
deliberately leaves these open ("what, not how"); do not resolve them silently.

- **Surface real arbitrations as questions.** When two viable approaches differ
  in what they sacrifice, stop and ask the human **before writing the phases** —
  the same clarify gate the spec applies in its §9, applied at the plan layer.
  Bundle the questions in one pass; don't drip them one at a time. An arbitration
  is real when both options would pass the gates; a choice with only one viable
  option is not a question, just make it.
- **Frame every option by its dominant loss.** Do not weigh symmetric pros/cons
  lists — both columns fill endlessly and everything appears to weigh the same.
  Name, per option, the strongest thing you abandon by choosing it, and recommend
  the option whose loss is acceptable:

  ```markdown
  Question: <the arbitration>

  - Option A — abandons: <the strongest thing this choice gives up>
  - Option B — abandons: <the strongest thing this choice gives up>

  Recommendation: <option> — <why its loss is the acceptable one>.
  ```

  One line per option is enough. If you cannot name what an option abandons, you
  have not understood the option yet — dig further before asking.
- **Record the outcome in the plan.** Add a `## Technical arbitrations` section
  to the plan preamble (before the first phase heading) listing each resolved
  arbitration in one or two lines: the decision and the loss knowingly accepted.
  It is informational (not extracted), but it lets the executing agent and the
  reviewer read a phase without re-litigating the choice. Omit the section when
  the plan required no arbitration.
- **Questions in the preamble are optional.** A plan may also carry `## Open questions`,
  `## Arbitrated questions` and `## Decision log` sections in its preamble, before the first
  phase heading, each only when non-empty. A question follows the §9 format of the phax-spec
  skill: `### <id> — <question>`, option bullets `- <option id> — <label> — abandons: <loss>`,
  and `Recommendation: <option id> — <rationale>`; an arbitrated one also carries its
  `Decision:` block. `phax plans lint` and extraction accept these sections and read the same
  phases and required commands with or without them.
- **An arbitration you cannot settle stays an open question.** `phax artifact decide <plan>`
  answers the open questions with the operator, then writes each one, with its Decision block,
  into the arbitrated section. `phax artifact approve` refuses a plan while its open section is
  not empty, so a plan reaches Approved only once every question it raised is decided.

## Spike and discovery plans

phax is not only for feature work — it is a valid vehicle for spike / discovery /
feasibility plans. A discovery plan succeeds when it delivers **either the answers, or
the instrumentation to obtain them**. Hold the plan to that bar.

Such work fits awkwardly into the default model (autonomous agent + mechanical gate)
because a spike's value is in judgment, not in compiling code. Reconcile it like this:

- **Split deterministic scaffolding from real-world signal.** The agent authors what it
  can produce deterministically — harness scripts plus a findings-doc skeleton with
  empty `## Results` / `## Verdict` sections. The actual run (booting a VM, hitting a
  live API, observing a network block) happens out-of-band — a human or an e2e step —
  and its output is pasted into the findings doc. A closing synthesis phase
  rapatriates the judgment into a go/no-go.
- **Keep the spike project's gate profile light.** Spike artifacts (`spikes/`, docs) have
  no architecture/knip/build surface to protect, so gating them on heavy structural/product
  steps is a misleading "false green". Configure the spike's single gate profile with just
  the honest light steps (e.g. `typecheck` to cover any TypeScript scaffolding the spike
  produces). Gates passing trivially ("à vide") is acceptable for a spike — the real signal
  lives in the findings doc, not the gate.
- **State the execution-model caveat in the plan Overview.** A phase agent runs in a
  worktree and cannot reliably self-verify real-world effects (a microVM escape, a live
  egress block). Say so explicitly, so the synthesis reads as provisional until a real
  run fills the Results sections.

## Model catalog

Each entry below is a versioned model id from the provider catalog with its effort set and status. Plan phases reference concrete model ids (e.g. `claude-sonnet-4-6`); phax resolves them natively when the execution provider serves the same family, or translates through the Claude-hub equivalence table otherwise. The catalog is the source of truth — the run-start preflight rejects any phase whose id or effort is not listed here.

### Choosing a model and effort

- The default phase model is `claude-opus-5-5`: it is the cheapest Opus-tier entry and matches or beats `claude-fable-5-1` from `medium` effort up.
- Use `claude-sonnet-5-5` for mechanical phases (catalog/table edits, renames, doc sweeps).
- Use `claude-fable-5-1` only for a stated reason that Opus 5.5 cannot serve, and record that reason in Technical arbitrations.
- Use the lowest effort that succeeds. Opus 5.5 is weakest at `low` (below Fable 5.1), so use `medium` or above for phases that need real reasoning.
- A plan that changes the catalog itself must name only models already in the shipped catalog: the run-start preflight uses the running binary's catalog, not the one the plan adds.

<!-- BEGIN generated: model-catalog -->
| ID | Family | Status | Efforts |
| --- | --- | --- | --- |
| `claude-haiku-5-5` | `claude-haiku` | active | `low` \| `medium` \| `high` \| `xhigh` \| `max` \| `ultracode` |
| `claude-haiku-4-5-20251001` | `claude-haiku` | active | `none` |
| `claude-sonnet-5-5` | `claude-sonnet` | active | `low` \| `medium` \| `high` \| `xhigh` \| `max` \| `ultracode` |
| `claude-sonnet-5` | `claude-sonnet` | active | `low` \| `medium` \| `high` \| `xhigh` \| `max` \| `ultracode` |
| `claude-sonnet-4-6` | `claude-sonnet` | active | `low` \| `medium` \| `high` \| `max` |
| `claude-opus-5-5` | `claude-opus` | active | `low` \| `medium` \| `high` \| `xhigh` \| `max` \| `ultracode` |
| `claude-opus-5` | `claude-opus` | active | `low` \| `medium` \| `high` \| `xhigh` \| `max` \| `ultracode` |
| `claude-opus-4-8` | `claude-opus` | active | `low` \| `medium` \| `high` \| `xhigh` \| `max` \| `ultracode` |
| `claude-fable-5-1` | `claude-fable` | active | `low` \| `medium` \| `high` \| `xhigh` \| `max` \| `ultracode` |
| `claude-fable-5` | `claude-fable` | active | `low` \| `medium` \| `high` \| `xhigh` \| `max` \| `ultracode` |
| `phax-mistral-medium-3.5-off` | `mistral-medium` | active | `off` |
| `phax-mistral-medium-3.5-low` | `mistral-medium` | active | `low` |
| `phax-mistral-medium-3.5-medium` | `mistral-medium` | active | `medium` |
| `phax-mistral-medium-3.5-high` | `mistral-medium` | active | `high` |
| `phax-mistral-medium-3.5-max` | `mistral-medium` | active | `max` |
| `gpt-6-sol` | `openai-gpt` | active | `low` \| `medium` \| `high` \| `xhigh` \| `max` \| `ultra` |
| `gpt-6-luna` | `openai-gpt` | active | `low` \| `medium` \| `high` \| `xhigh` \| `max` |
| `gpt-6-astra` | `openai-gpt` | active | `low` \| `medium` \| `high` \| `xhigh` \| `max` \| `ultra` |
| `gpt-5.6-luna` | `openai-gpt` | deprecated | `low` \| `medium` \| `high` \| `xhigh` \| `max` |
| `gpt-5.6-terra` | `openai-gpt` | deprecated | `low` \| `medium` \| `high` \| `xhigh` \| `max` \| `ultra` |
| `gpt-5.6-sol` | `openai-gpt` | deprecated | `low` \| `medium` \| `high` \| `xhigh` \| `max` \| `ultra` |
| `gpt-5.5` | `openai-gpt` | active | `low` \| `medium` \| `high` \| `xhigh` |
<!-- END generated: model-catalog -->

## Required commands declaration

Every plan must include a top-level `## Required commands` section that lists the
shell commands the plan needs the agent to be able to run. The section must
appear in the plan before the first phase heading.

```markdown
## Required commands

- deno fmt
- deno lint
```

Write `- (none)` when the plan introduces no new commands. The extraction model
reads this section and emits `run.requiredCommands` in `phax-plan.json`. The
preflight check in `phax run` fails the run early — before any agent spawns —
if any required command is absent from the frozen effective set (security config
∪ gate commands).

### When to declare required commands

Declare a required command whenever a phase introduces a tool, runtime, package
manager, or CLI that was **not already in use** in the repository. Examples:
Deno, Bun, pnpm, Vitest, Playwright, ESLint, Biome, Cargo, Docker, `gh`, `az`,
`terraform`. If you are not sure whether the command is already allowed, declare
it — the preflight will confirm coverage.

Do **not** declare commands that are always available in the execution environment
(e.g. `git`, standard POSIX shell builtins) unless a phase actually calls them
as agent tasks.

### Broad vs narrow allowances

A _broad_ allowance is a single token (e.g. `deno`). It covers any sub-command
(`deno fmt`, `deno lint`, `deno run …`) via the token-prefix rule.

A _narrow_ allowance is a command with sub-commands or arguments
(e.g. `deno fmt`). It is more precise but may be degraded to `enforcement:
"none"` by providers that cannot enforce per-command precision (codex, vibe).
Degraded entries are recorded in `security.json` with a `command-precision` mark
but do **not** block the run.

Prefer narrow allowances when you want to constrain the agent precisely.
Prefer broad allowances when you need the agent to run many sub-commands of the
same tool.

### Required PHAX security configuration changes

When the plan declares new required commands, add a `## Required PHAX security
configuration changes` section in the plan so the developer knows to update
`phax.json` before running:

```markdown
## Required PHAX security configuration changes

This plan requires the following commands to be added to
`security.agentCommands` in `phax.json` before running:

- `deno fmt`
- `deno lint`

Without this configuration the preflight check will fail before any agent
spawns.
```

Omit this section if the plan's `## Required commands` is `(none)`.

### `requiredCommands` as a run-level extracted field

`requiredCommands` is a **run-level** field (not per-phase). It is extracted from
the `## Required commands` section into `phax-plan.json` as
`run.requiredCommands`. The preflight checks it against the full frozen set once,
before any phase begins. A command is "covered" by a configured allowance if an
exact normalised match exists or a configured broad allowance (single token)
is a token-prefix of the required command (`deno` covers `deno fmt`).

## Planning constraints

- **Sequential only.** Phases execute one at a time; phax has no parallel
  execution mode.
- **No invented repo commands.** Gates come from `phax.json`, not from
  `plan.md`. Never invent `pnpm` scripts or CLI commands that do not already
  exist in the project. `### Verification` names the configured gate profile; it
  does not introduce new commands.
- **Small, committable phases.** Each phase must produce a single coherent
  commit. If the diff would be hard to review, split the phase.
- **Gate-verifiable outcomes.** Every phase must have an outcome the configured
  gates can verify mechanically (type-check, tests, lint, build, etc.).
- **Accurate file lists.** The planned-file sections must match what the phase
  actually touches; they are reconciled against git at the end of the phase.
- **A phase reads its own section.** A phase's prompt carries the plan
  preamble, its own section, an outline of every phase (id, title, planned files
  to create and edit) and its phase definition; the full plan is at
  `.phax-context/plan.md` in the worktree. So:
  - put cross-phase decisions in the preamble, such as Technical arbitrations;
  - make each section self-contained, with no bare "as in phase-02";
  - rely on the planned file lists to show other phases' footprint;
  - give every phase a `## phase-NN — Title {#anchor}` heading, numbered without
    gaps.
- **Handoff-complete.** The handoff the executing agent writes must be enough
  for the next phase to proceed without re-reading earlier phases.
- **Comprehensive instructions.** Make sure each phase has enough detail for the
  model and effort you picked to execute it successfully.
- **No phax lifecycle commands in a phase.** A phase runs inside a phase worktree,
  where phax refuses every command except `phax brief` and the read-only ones
  (`plans lint`, `artifact status`, `records explain`, …). Never instruct a phase to
  approve, complete or abandon an artifact, or to run, resume, archive or publish.
  Those steps belong to the operator, in the main checkout. In a project whose tests
  start the phax CLI as a process, those tests also run inside the phase worktree,
  so give each one its own temporary repository as its working directory.
- **Briefs are already in the prompt.** When the project configures a `brief`
  provider, each phase's first prompt carries the brief for its planned files. Do not
  restate the project's standard in the instructions. A phase that works on paths
  outside its planned files can be told to run `phax brief <path>` before editing
  them.

## What makes a good phase boundary

- One clear outcome per phase (a file, a feature, a set of tests).
- Gates from `phax.json` must be able to verify the outcome mechanically.
- Phases that share state (e.g., a port defined in phase N used in phase N+1)
  must name the exact module path in the handoff expectations.

## Anti-patterns to avoid

- Phases with no mechanical verification step (gates must be able to pass/fail).
- Vague commit subjects — the subject is used in the git log; keep it precise.
- Scope creep — "and also clean up X" in a phase that has a different primary
  objective splits reviewer attention and risks the gate failing on unrelated work.
- Skipping the `{#phase-NN-<slug>}` anchor — the extraction uses it as the
  `planMarkdownAnchor` field and will error without it.
- Parallel or overlapping phase scope — each phase must be independently
  committable; assume the previous phase is done and merged.
- Inaccurate file lists — listing files you will not touch (noisy deviations),
  omitting files you will (false deviations), or listing files you only read.
- Putting "maybe" files in the create/edit lists instead of the optional list.
- Silently arbitrated trade-offs — picking between viable approaches without
  asking the human, or recording the decision without the loss accepted (see
  Technical arbitrations). Symmetric pros/cons lists are the same smell in
  disguise.

## Headless authoring

Everything above describes the Markdown plan you write in an interactive session. When the
session is spawned by `phax artifact new plan <slug> --headless --brief <file|->`, the
deliverable changes: you return the **plan document** — a JSON object — and phax renders it
deterministically to the phase structure above, writes the `.md` with its JSON sidecar
beside it, seeds the extraction cache so `phax run` never re-extracts it, and commits both.
The planning doctrine does not change; only the container does.

Skill files follow the same rule (see "Editing skill files"): the `plannedFilesToCreate` /
`plannedFilesToEdit` / `optionalFilesToEdit` arrays are the declaration.

- **The JSON object is your final message**, alone: no code fence, no commentary before or
  after it. The prompt carries the authoritative JSON Schema; `phax artifact schema plan`
  prints the same schema.
- **Write no files.** phax writes, renders and commits; a file you write is not the artifact.
- **Every key is required.** Unknown keys are rejected.
- **Lists are plain arrays.** An empty list is `[]`: phax renders it as `- (none)`. Paths,
  required commands and the commit subject are plain strings — phax renders them as code
  spans; do not add backticks.

Top-level keys:

- `version`: `1`; `kind`: `"plan"`.
- `sourceSpec`: the `--spec` path given to phax, or `null` without one (the prompt says
  which). phax sets it from `--spec` regardless, so frontmatter and sidecar agree.
- `completesSpec`: `true` or `false` beside a `sourceSpec` path, `null` without one. phax
  sets it from `--last`/`--not-last` regardless.
- `run`: `{ shortName, title, requiredCommands }` — the extracted run fields
  (`# <Title>`, `## Required commands`).
- `preamble`: `{ summary, requiredCommandsNote, technicalArbitrations, openQuestions,
  arbitratedQuestions }` — the prose under the title, the note after the required-commands
  list, and one string per arbitration (rendered as `## Technical arbitrations`, omitted when
  empty). `openQuestions` and `arbitratedQuestions` are required. Leave each arbitration you
  cannot settle in `openQuestions`, in the same shape as a spec's: `{ id, question, options:
  [{ id, label, abandons }], recommendation, rationale }`, with `recommendation` naming one
  option id. Settled arbitrations stay prose in `technicalArbitrations`. `arbitratedQuestions`
  is always `[]`: an authoring session arbitrates no question, and a non-empty one is refused
  as an invalid document. `phax artifact decide <plan>` answers the open questions, and
  approval waits until none is open.
- `phases`: a non-empty array, one object per phase.

Each phase carries the **extracted fields** — exactly what `phax run` reads, and exactly
the cache seed:

- `id` (`phase-NN`), `model`, `effort` (see the model catalog), `planMarkdownAnchor`
  (`#phase-NN-<slug>`, rendered as the heading's `{#phase-NN-<slug>}`).
- `plannedFilesToCreate`, `plannedFilesToEdit`, `optionalFilesToEdit`: string arrays.
- `commit`: `{ subject, body }`.

and the fields that only render the phase:

- `title` — the heading text after `phase-NN — `; phax derives the run's phase title from
  the rendered heading, as for any plan.md, so it is not part of the seed.
- `objective` — the paragraph under the model/effort lines.
- `detailedInstructions`, `implementationOrder` (rendered numbered), `excludedScope`:
  string arrays.
- `boundaryContracts`: a string, or `null` for a phase that crosses no boundary — the
  section is then omitted rather than filled.
- `testStrategy`, `verification`, `expectedHandoff`: strings.

## Example well-formed phase

```markdown
## phase-03 — Run folder model and atomic writes {#phase-03-run-folder}

**Recommended model:** claude-sonnet-4-6
**Recommended effort:** low

Introduce the run-folder model and atomic file writes so later phases have a
durable place to persist run state.

### Detailed instructions

- Add `createRunFolder(shortName, …)` returning the run path.
- Route all writes through an atomic write helper.

### Planned files to create

- `src/app/runFolder.ts`
- `tests/unit/runFolder.test.ts`

### Planned files to edit

- `src/app/index.ts`

### Optional files that may be edited

- (none)

### Excluded scope

- Phase-level worktree creation (phase-04).

### Verification

- The project's configured `standard` gate profile in `phax.json`.

### Expected handoff content

- The exact path `src/app/runFolder.ts` and the `createRunFolder` signature.
- Any deviation from the planned file lists, with the reason.

### Commit subject

feat(run): add run folder model and atomic writes

### Commit body

Add createRunFolder and an atomic write helper so later phases can persist run
state durably. Covered by a unit test exercising the folder layout.
```

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.