Operate the phax CLI — its concepts, run lifecycle, and the canonical init → plan → run → review → publish flow. Use when driving phax, explaining it to a developer, or scripting against it. Defers to `phax --usage` for the exact command/flag contract.
Installs into .claude/skills of the current project.
Are you the author of Phax Cli?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/lbdremy-phax-cli)
---
name: phax-cli
description: Operate the phax CLI — its concepts, run lifecycle, and the canonical init → plan → run → review → publish flow. Use when driving phax, explaining it to a developer, or scripting against it. Defers to `phax --usage` for the exact command/flag contract.
---
# phax CLI skill
This skill is the **map**, not the territory. For the exact, version-accurate
command/flag/argument contract — every command, its flags, args, examples, and
help — run:
```
phax --usage # KDL spec, source of truth, no external dependency
phax --usage-format json # same contract as JSON (needs the `usage` CLI)
```
`--usage` is generated from the CLI's own command tree, so it never drifts from
the installed binary. **Don't memorize or restate flags here — read `--usage`.**
This skill only covers what `--usage` can't express: the concepts, the run
lifecycle, and how commands fit together.
## What phax is
A deterministic, local CLI that drives an AI coding agent (Claude Code by
default; also Mistral Vibe and OpenAI Codex) through isolated, **gated phases**.
Each phase runs in its **own Git worktree**, executes the agent, then runs a
**gate profile** (typecheck, tests, lint, …) with a **same-session fix loop** —
the agent fixes gate failures in the same context before the phase may commit.
The **final phase worktree stays open** for human review. phax pushes the branch and
opens a PR only when `phax.json` sets `publish.auto`, or when you run `phax publish-pr`.
## Run lifecycle — the command you reach for depends on the state
Runs live in a local registry (`~/.phax/runs/`), are addressed by a
**short-name** (e.g. `usage-cli`), and move through:
```
created → running → review_open → archived → (prune) gone
↘ failed
```
- **created / running** — phases executing. After an interruption, continue
with `resume`.
- **failed** — a phase failed and the loop stopped. Inspect with `session-info`
(or `enter-phase` to attach to the uncommitted worktree), then `reset-phase`
to clear it and `resume` to re-run from there.
- **review_open** — all phases committed; the final worktree is open and waiting
for a human. This is where you `enter` / `shell` / `open` to review,
optionally `review-compliance`, then `publish-pr` and finally `archive`.
- **archived** — the run folder and worktrees are moved under `~/.phax/archive`, and the name stays held. `phax prune` deletes the run for real and frees its name and disk space. Archived is no longer terminal: prune is the step that removes it.
Most review commands have a `…-last` variant that targets the most recent
`review_open` run, so you can drop the short-name (see `--usage`).
## Canonical end-to-end flow
```
phax init # scaffold phax.json
# author plan.md → use the `phax-planning` skill for its format (status: Approved)
phax plans lint plan.md # check structure, planned files, run readiness
phax run my-feature --plan plan.md # extract plan + run every phase → review_open
phax enter my-feature # review/iterate in the kept-open agent session
phax publish-pr my-feature # push branch + open a PR (needs gh)
phax archive my-feature # finish
phax prune my-feature # optional: delete the archived run and free its name
```
`phax run` extracts the plan inline; `phax plans lint <plan>` checks a plan's
structure, planned files and run readiness without running it. Each phase requests a
`model` + `effort` that phax resolves to a concrete provider — inspect routing
under `phax agent` (see `--usage`).
Plans that touch `.claude/skills/` files need `phax run --allow-skill-edits`; without it
the preflight refuses (exit 11). `phax resume` inherits the consent, and the grant is
recorded per phase in `security.json` (`skillEditGrants`). See `--usage` for the flag
contract.
## Inside a phase worktree — the phase guard
A phase worktree is a linked Git worktree whose root holds `.phax-context/`. Run from
anywhere inside one, phax refuses every command that acts on phax state or on an
artifact's lifecycle, before the command runs, and names the main checkout as the place to
run it. That covers `run`, `resume`, `archive`, `publish-pr`, `review-*`,
`artifact approve`/`new`/`decide`/…, `plans status --apply`, and also `enter`, `shell` and `open`.
What still runs inside:
- `phax brief [path…]`;
- the read-only commands: `validate`, `ls`, `path`, `session-info`, `completions`,
`plans lint`, `plans status`, `artifact status`, `artifact schema`, `records explain`,
`records status`, `records list`, `security status`;
- `schema upgrade`, which writes only the working tree's local schemas.
The guard has no flag or environment variable to turn it off, and it catches people too:
in `phax shell` or the kept-open final phase, leave the worktree to run `review-compliance`
or `publish-pr`. It is accident-proofing, not a security boundary; the sandbox's write
scope is the boundary.
## Gate steps, briefs and records
These are `phax.json` keys rather than commands, so `--usage` does not show them. The
README (sections *Gate report steps*, *Gate request*, *Brief provider*, *Plan
auditor*) holds the exact formats.
- **`"output": "gate-report"`** on a gate step: the step prints a gate report on stdout,
and phax reads the verdict from it, not from the exit code. A checked report carries
findings (rule, location, message, related locations, and a guide file the agent reads)
and review notes; any finding fails the step, and the findings are what the agent is
asked to fix. Review notes fail nothing and go to the review handoff, never the agent. A
refused report (reason and remedy) stops the phase with exit 4 and no fix attempt;
`phax resume` runs the gate again. Each readable report is saved as
`checks-attempt-NN.report-SS.json`.
- **`"input": "gate-request"`** on a gate step: the step gets `{$schema, phase, base,
terminal, phases}` on stdin, the facts it needs to decide what is due now and what a
later phase is planned to bring. Each attempt's request is saved as
`checks-attempt-NN.request.json`; `phax records explain <commit> --gates` prints it after
its log.
- **`brief`** (`command` and `push`, both required): the provider answers with a brief
report, the rules over some paths and the findings there with their due. It is pushed
into a phase's first prompt and pulled with `phax brief [path…]`, which prints the whole
report, from inside a phase or from your own checkout. `push` chooses what the first
prompt lists: `"findings"` (the findings due in this phase) or `"findings-and-rules"`
(those findings, then the rules over the phase's planned files). A brief
never blocks; the gate still decides. In `secure` mode the phase agent is granted
`phax brief` (source `brief` in `security.json`). Each brief is recorded as
`brief-NN.json`; `phax records explain <commit> --briefs` prints them.
- **`planAuditor`** (a provider command): advisory findings for `phax plans lint`, never
during a run.
## Artifact lifecycle — specs and plans carry an enforced status
Specs (`docs/specs/`) and plans (`docs/plans/`) each carry a `status` key in
their YAML frontmatter block, drawn from a fixed per-kind set (specs: `Draft`,
`Approved`, `Abandoned`, `Completed`; plans add `Stale`). phax **enforces the one
gate that matters: only an `Approved` plan can run** — `phax run` refuses a
`Draft`, `Stale`, or retired (`Abandoned`/`Completed`) plan, naming the status and
the remedy, *before* extraction. Extraction itself stays ungated, so you can
still preview a draft's `phax-plan.json`.
The `phax artifact` command group inspects and moves lifecycle status through its
legal transitions (see `--usage` for the exact subcommands and flags):
- `phax artifact status <path>` — report an artifact's kind, current status, and
legal transitions. It also prints a `Questions:` line when the artifact has question
sections: the count, the open questions with their ids, and the arbitrated ones with how
many are lifted (`Questions: 4 — open 2 (Q3, Q4) · arbitrated 2 (1 lifted)`). A headless spec
with a valid sidecar always prints it, a plan only when it has questions; a hand-authored §9 that departs from the §9
format prints the first departing line instead, and status still exits 0. A headless
sidecar stamped below its format's current shape, or written before `$schema`, is not
compared: the `Authored:` line names its stamped shape and `phax artifact migrate-documents`.
- `phax artifact approve [--by <arbiter>] <path>` — the `Draft → Approved` gate; run it on a plan
under `docs/plans/` before `phax run`. Approving stamps `approved: { date, baseline }`
into the frontmatter and writes the artifact's own record file (`docs/plans/approvals/<plan file name>.json`
for plans, `docs/specs/approvals/<spec file name>.json` for specs). `--by operator` or
`--by machine:<grant>` (a kebab-case slug) says who approves; the record file's `approvedBy`
names them. From a terminal, `--by` defaults to the operator, named as git commits
(`GIT_COMMITTER_NAME`, else `user.name`); with no terminal and no `--by`, approve refuses
with exit 12, so **a loop or script must pass `--by machine:<grant>`**. An operator approval
with neither name set also refuses with exit 12 (`git config user.name`). A record written before attribution reads as
`unattributed` and is never rewritten by a read. Re-approving an already-`Approved` artifact
is legal — it re-records the approval against the current content, the correct way to log an
in-place revision (don't hand-edit the stamp). Approving a plan whose `Source-Spec` is an
Approved spec that is **unrecorded** or **edited since** its approval is refused with exit
12 — re-approve the spec first.
- `phax artifact stale` / `reopen` — mark a plan `Stale`, or reopen an `Approved` spec or
plan, or a `Stale` plan, back to `Draft`. Reopen clears the `approved` stamp, deletes the
record file, takes `--by` like approve (same terminal default and no-terminal refusal),
and writes a `Reopened-By: machine (<grant>)` / `operator (<name>)` trailer in its commit.
Reopening a spec names its bound plans and leaves them unchanged. A `Draft`, `Completed`
or `Abandoned` artifact is refused with exit 12.
- `phax artifact decide [--question <id>] [--doctrine <file>] [--model <m>] [--effort <e>] <path>` —
decide a Draft spec's or plan's open questions in an interactive Claude Code session. The agent
raises the framing questions F1–F5, then presents the open questions; the operator decides.
When the operator exits, phax asks the session for the revised document, checks it, prints the
body diff, and writes and commits the artifact and its sidecar only after the operator accepts
(a declined diff writes nothing and exits 0). `--question <id>` presents one question alone,
open or arbitrated; a new verdict keeps the old one in its history. The flow is
`status → decide → approve`, and to revisit a decision `reopen → decide --question <id>`.
decide needs a terminal (exit 12 without one), a Draft artifact (an Approved one names
`phax artifact reopen`), and a Claude model; it is refused inside a phase worktree, and never
runs inside a run. The doctrine is the `phax-decide-spec` or `phax-decide-plan` skill (a copy
under `.claude/skills/` wins), with `--doctrine` appended. **The approval lock:** `approve`
refuses with exit 12 while any question is open, naming the ids and `phax artifact decide`;
a loop cannot approve past an open question. See `docs/arbitration.md`.
- `phax artifact abandon` / `complete` — terminal transitions; phax moves the file
into the matching `archive/` directory and deletes its record file as part of the transition.
Reopen deletes the artifact's record file the same way. No transition touches another
artifact's record file.
- `phax artifact migrate-approvals` — the one-time upgrade from the old shared ledgers
(`docs/plans/approvals.json`, `docs/specs/approvals.json`) to per-artifact record files
under `docs/plans/approvals/` and `docs/specs/approvals/`, in one commit. Exits 0 when
migrated or when there is nothing to migrate; exits 12, writing nothing, on an unreadable
ledger, an entry that is not a live artifact path, an uncommitted change to a path it
would write, or an existing record file holding a different record. While a ledger
exists, every other approval command exits 12 and names this command.
- `phax artifact migrate-documents` — the one-time upgrade of headless sidecars. It rewrites
every live spec and plan sidecar (a `.json` directly under `docs/specs/` or `docs/plans/`
beside its `.md`) stamped below its format's current shape, or written before `$schema`,
and re-renders its body, keeping the frontmatter byte for byte. A spec question whose
rationale opens with `Decided by the author on <YYYY-MM-DD>` becomes arbitrated, lifted and
unattributed; every other question stays open. A plan stamped 0.20.0 lifts with empty
question arrays and keeps its body. It refuses (exit 12, nothing written) on an invalid
sidecar (a plan stamped 0.17.0–0.19.x stays refused), on a body that is not the sidecar's
own rendering (a hand edit would be overwritten), or on an uncommitted change to a path it
would write; the remedy is to re-author it headless, or delete its sidecar to demote it to
hand-authored. It names every Approved artifact it leaves edited since approval (with its
open question ids and `phax artifact reopen <path>` when it has any, else
`phax artifact approve <path>`) and every plan it leaves stale with `spec-changed`, then
commits exactly what it wrote in one commit. With nothing to migrate it says so and exits 0.
Until it has run, `approve` and `reopen` exit 12 and name it; `complete`, `abandon` and run
completion carry an older sidecar as before. Archived and hand-authored artifacts and
approval record files are never touched. The phase guard refuses it inside a phase worktree:
run it in the main checkout.
Transitions are validated: an illegal one is refused and names the legal set, and
a terminal status must live under `archive/` (status and location must agree). A
loose `plan.md` outside `docs/specs/`|`docs/plans/` is still gated on status, but
its status line is edited by hand — `phax artifact` targets repo-tracked
artifacts.
When unsure about any command, read its `long_help` and `example` in
`phax --usage`.