Get features shipped right with Claude Code.
PHARN takes one feature through spec → plan → grill → test → build → regress → verify → ship: the intent
you approve, a plan with the files the build may touch, a failing test for each acceptance criterion before
any code exists, and your project's gates run before the final decision is yours.
Every step stays in Git: the spec, the plan, the declared files, the checks that ran and the handoff at the shipping gate are committed Markdown, so months later you can answer "what was done and why?".
It is open-source methodology, not a black box: Claude Code commands, readable Markdown artifacts, deterministic hooks, stdlib-only floor checkers, grillers, and review lenses that live in your repository. PHARN is strict about one thing: claims backed by deterministic checks are named as such; model or human judgment remains advisory.
npx @pharn-dev/pharn@latest initStatus: Ready to install and use with Claude Code today. Active development continues; functionality that has not shipped yet is explicitly labeled.
- What PHARN is
- Why it exists
- Quick start
- What gets installed
- Commands
- Guaranteed vs advisory
- More documentation
- PHARN builds PHARN
- Design docs
- Contributing
- Security
- License
PHARN is an open-source workflow layer for teams using AI agents to change real code. It gives each increment a trail in Git:
SPEC.md— the human-readable intent PHARN asks you to approve before implementation (or, under the unattended/pharn-loop, approves for you and records as the model's approval).PLAN.md— the agent's implementation plan and declared write scope.GRILL.md— pre-build interrogation of the plan (a quick run'sGRILL.md—/pharn-ship --quickor/pharn-loop --quick— and every/pharn-looprun's, since 6.45.0, records its two floor stops, plus in a full loop the deterministic plan scans, and explicitly did not interrogate — see--quickunder Quick start).AC-TESTS.md,AC-TESTS.lock.json— which test covers each acceptance criterion, and the pinned evidence that each of those tests failed before the build.BUILD.md,REGRESSION.md,VERIFY.md,SHIP.md— what changed, what ran, what passed, what did not, and where the run stopped (a quick run writes noREGRESSION.md: it never runs/pharn-regress; an unattended loop writesLOOP.mdinstead ofSHIP.md, and a quick loop'sLOOP.mdrecordsmode: quick).cost.json,RUN-REPORT.md— what the run cost and what it did, written by/pharn-loopat every stop and by/pharn-shipat every exit that ends a run. The first is machine-readable token counts per stage, iteration and model; the second is the human-readable view over it. Tokens only — PHARN ships no price table, so money is your own list price applied to those counts. (A quick run keepscost.jsonbut writes noRUN-REPORT.md.)
The project is intentionally small-surface: prompts and contracts in Markdown, plus deterministic Node helpers. There is no hidden service in this repository and no proprietary rule engine needed to inspect what PHARN is doing.
PHARN is not a correctness oracle, a security guarantee, or a replacement for tests and code review. It preserves intent, narrows some agent write behavior, runs focused plan/code scrutiny, and labels the boundary between deterministic checks and judgment.
AI made code cheaper to produce. It did not make code cheaper to understand six weeks later.
The expensive questions moved upstream and downstream:
- What did we ask the agent to build?
- Which constraints shaped the plan?
- Which files was the agent supposed to edit?
- What did the workflow actually check?
- Which findings were deterministic, and which were model judgment?
- What should a reviewer, maintainer, or future incident responder trust?
PHARN puts those answers in the repo, where normal engineering tools can diff, review, and preserve them. The first goal is a feature that ships right; the second is that how it got there can be inspected later, not taken on trust.
PHARN runs on Claude Code. The @pharn-dev/pharn installer requires
Node 24.2 or newer (the installer refuses older). The deterministic floor checkers this repo ships (pharn/floor/*.mjs, invoked by the
/pharn-* stages) require Node 24.2 or newer: their CLI entry points gate on import.meta.main, which
Node added in 22.18 / 24.2. Since 6.50.0 every guarded tool refuses an older Node: it prints the required version
and exits 2 instead of exiting 0 without running its checks. CI and local contributor gates use Node 24. In your project root:
npx @pharn-dev/pharn@latest initinit installs the newest PHARN release: a tag v<SKILLS_VERSION> that this repository's release workflow
creates only after the CI checks main requires, those that run on a push, passed on that commit. The installer records the
choice ("ref": "latest"), so update keeps following releases. When no release can be resolved (none published,
api.github.com unreachable, or its unauthenticated limit of 60 requests an hour used up, which status --strict on a
shared CI address can hit), it exits 1 and names --ref main instead of falling back on its own. --ref main takes the tip of main: every merge, before its checks on
main have finished. That channel is for contributors who want each change as it lands. Releases are the default from
@pharn-dev/pharn 0.15.0, which @latest resolves to; an older installer takes the tip of main unless given --ref latest.
Then open Claude Code in the same project and run the full loop:
/pharn-loop implement password reset with a one-time token
/pharn-loop runs spec → plan → grill → test → build → regress → verify unattended. The model approves its
own spec, checks the plan with the grill's two floor stops and its deterministic plan scans (no interrogation and no
model-driven griller, since 6.45.0 — no stage would read them before the build), writes each acceptance criterion's test and shows it fails before any code exists, repeats build →
regress → verify until a deterministic stop — green, the --max-iter cap, or a
result it must not retry — commits a green result to a new local branch (never pushed or merged), and
ends with a summary of what was done. While the spec, plan and grill stages work, it runs your project's gates once
in the background on the tree it started from (6.42.0). If one is already red, the run stops before /pharn-test
writes anything (blocked: gates-red-at-entry). If none is red but one did not finish inside the per-gate timeout or
could not start, it stops there too, as blocked: gates-unmeasured-at-entry (6.75.0), since that gate was not measured. Pass
--allow-red-entry to go on past either, when fixing that gate is the feature.
/pharn-ship runs the same check and asks you whether to stop or continue. When it reaches a point that needs a human decision, it stops and
says what it needs instead of guessing. On any stop other than a committed green result it reverts its
own spec approval — a procedural step, so an aborted run can skip it. Start it outside plan mode: in plan mode it stops
at once and asks you nothing (6.58.0). It pre-approves its own commands for the run, but its stage agents run your
project's gates under your session's permission mode, so when nobody will be watching, use one that does not prompt
for them (auto mode, or allow rules). In the measured runs, an interactive claude --permission-mode auto session on
Claude Code 2.1.310 launched each stage agent in the background and the loop waited it out (one completed --quick
run, PHARN 7.1.1); a headless claude -p --permission-mode auto session ran them in the foreground on 6.73.2, which
sent run_in_background: false (headless under 7.1.1, which sends no such parameter, is not measured).
The test stage needs your project's test runner and its per-test results: a test script (and a test:e2e or
e2e script for end-to-end criteria) whose reporter writes JSON to the path PHARN passes
(Per-test results). Without them the run stops with blocked: no-test-runner and suggests a
setup increment to run first. The same split applies when a feature must change the runner itself: its config at
the project root, its test script, or its results format. PHARN pins those before the build, so plan that change as
its own setup increment first, run through /pharn-ship. /pharn-plan refuses a plan that names a root runner
config.
To approve the spec yourself and decide merge, fix, or abandon at the end, use the one-pass run with both human gates:
/pharn-ship implement password reset with a one-time token
For a small, well-scoped change, both commands take --quick (/pharn-ship --quick, 6.25.0;
/pharn-loop --quick, 6.28.0): a shorter spine that checks less. Quick mode says
what it skips and what still stops it.
Run an unattended loop in a container that holds none of your credentials. At entry it checks a fixed list of
container markers and credential files and env vars. When it finds no container marker, or finds a credential, it
refuses to start unless you pass --accept-host-execution. Since 7.3.0 the command does not pre-approve the line that
accepts it, so Claude Code's permission settings decide: in the default mode it asks you at the start of the run; in
auto mode or under an allow rule of yours it may run the line without asking, and nothing in PHARN can tell who chose.
When it cannot read your home directory or git config, it refuses even with that flag (7.1.0;
Running /pharn-loop in a container).
Use the full /pharn-loop for a feature that spans several files, has acceptance tests, or is likely to need more
than one iteration, and --quick for everything else. Two measured full-loop runs of one small feature in a user
project on 6.73.2 both stopped at the entry check, when that project's test suite ran past the 540 s gate timeout,
after about 19 minutes and 5.5–5.9 million processed context tokens each (input, cache-write and cache-read tokens,
plus about 31 thousand output tokens), with only the spec and plan done
(the measurement). A measured --quick loop of one small feature
in the same project on PHARN 7.1.1 took about 18 minutes and 11.2 million processed context tokens (10.7 million
cache-read, 0.47 million cache-write, 208 uncached input, plus about 50 thousand output tokens) and completed green in
one iteration; a full loop is still unmeasured
(the measurement).
Every stage is also available as its own command; see Commands.
Already installed? npx @pharn-dev/pharn status reports your installed skills version and drift.
update re-fetches the latest skills version, add and remove manage capabilities, and list prints
what is installed. init installs into the project rather than onto your PATH, so keep the npx
prefix unless you installed the CLI globally. An install made with --ref main, or before @pharn-dev/pharn 0.15.0
without --ref latest, follows main, and update keeps it there; npx @pharn-dev/pharn@latest update --ref latest
switches it to releases.
Two version numbers, on purpose. The pharn badge above tracks
SKILLS_VERSION — the content an install receives, and what status and
CHANGELOG.md are keyed to. The npm package @pharn-dev/pharn carries the installer's own version.
They move independently and are not meant to match.
The installer reads your project and selects the capabilities that apply before writing files. Detection
is JS/TS-shaped today: it reads package.json and scans for next.config.*, app/ route handlers,
.tsx/.jsx, migrations/, and .sql, resolving to one or more of ssr, backend, spa, and lib
— a project can match several rather than exactly one. (The installer's own documentation gives Next plus
Express as an example resolving to ssr and backend together.) A repo with none of those signals still
installs the universal capabilities.
You see the selected capability list, with a reason beside each entry, before the installer writes. A normal install adds:
- product commands in
.claude/commands/, - hooks in
.claude/hooks/: the two write guards, the scope setter they read, and aStopguard for/pharn-loop(see below), - the four trusted docs —
pharn/CONSTITUTION.mdandpharn/ARCHITECTURE.md, pluspharn/THREAT-MODEL.mdandpharn/LIMITS.md, - the deterministic floor, contracts, grillers, and review lenses under
pharn/, pharn.config.json, pinning the skills version and exact installed commit, and carrying themodels.stagesblock that sets each product command's model and effort — and, since 6.27.0, the model/pharn-shipand/pharn-looprequest for each stage they run as a subagent (see Current limitations for what that does and does not reach).
your-repo/
├── .claude/
│ ├── commands/pharn-*.md # the 11 product commands
│ ├── hooks/*.cjs # the write guards, their setter, the /pharn-loop Stop guard
│ └── settings.json # wires the hooks (see the caveat below)
├── pharn/
│ ├── CONSTITUTION.md # the four trusted docs (human-only)
│ ├── ARCHITECTURE.md
│ ├── THREAT-MODEL.md
│ ├── LIMITS.md
│ ├── floor/*.mjs # the deterministic checkers
│ ├── pharn-contracts/ # artifact shapes
│ ├── pharn-pipeline/grillers/ # plan interrogators
│ ├── pharn-review/ # code lenses
│ ├── memory-bank/ # your promoted lessons and patterns (/pharn-memory-promote) — commit these
│ ├── spec-template.md # optional: your own SPEC template, edited by hand
│ ├── features/<name>/ # per increment, written as you run the pipeline — commit these:
│ │ # SPEC PLAN GRILL AC-TESTS (+ .lock.json) BUILD REGRESSION VERIFY, then SHIP + BRIEFING
│ │ # (/pharn-ship) or LOOP (/pharn-loop), and RUN-REPORT + cost.json
│ ├── .run/ # runtime scratch — gitignore it
│ └── .backup/ # pharn-cli's backups from `pharn update` — gitignore it
└── pharn.config.json # skills version + installed commit + models.stages
Add these three lines to your .gitignore. The middle one is pharn-cli's lock file:
pharn/.run/
pharn/.lock*
pharn/.backup/Reconcile leaves pharn/.run/ out of its set whether or not you ignore it (7.2.0); the three lines keep run state,
the lock and backups out of your commits. Before 7.0.0 the scratch lived at .pharn/, and memory-bank/, the SPEC template
and two of the trusted docs sat at the project root (migration: CHANGELOG [7.0.0]).
The hooks enforce only after they are registered in Claude Code's settings — .claude/settings.json, or
.claude/settings.local.json, which is loaded too and can wire or override the same hooks. If your
project already has a .claude/settings.json, the installer preserves it and warns instead of
overwriting it. Until you copy the hook wiring over, any guarantee that depends on a PreToolUse hook is
not active.
require-loop-record.cjs is not a write guard. It is a Stop hook: while an unattended /pharn-loop run
in the session has written no LOOP.md, it refuses to let the turn end, a bounded number of times per run,
and it fails open. It does nothing unless your settings register it under Stop. As of 6.12.0 the
settings.json PHARN ships registers it (matcher-less, exec form). An existing install whose
settings.json the installer preserved still needs that entry copied by hand — pharn update never
edits it.
Copy the wiring as it ships, anchored on the project-directory placeholder and set to block on failure:
{
"type": "command",
"command": "node \"${CLAUDE_PROJECT_DIR}\"/.claude/hooks/protect-trusted-paths.cjs",
"onFailure": "block"
}Claude Code runs a hook in Claude's current directory, so a relative node .claude/hooks/… stops
starting after any cd into a subdirectory: node exits 1. Without onFailure, Claude Code treats that as a
non-blocking error, and both guards are then silently off. That was the shipped form until 6.1.0, and it is
measured, not theorised. Upgrading an older install is ordered: run pharn update first, then change
these two commands — the anchored wiring over pre-6.1.0 hooks regresses both guards, so roll back in the
reverse order. Each guard judges the git working tree that contains Claude's current directory;
pharn/LIMITS.md §7 states the bounds that remain.
Since 7.3.0 both guard entries also carry "onFailure": "block". On Claude Code 2.1.295 or later, a write
is then blocked when a guard cannot start (a wrong path, no node), exits with a code other than 0 or 2, or
times out. Without the field, that write goes through. The Stop hook does not carry the field: it fails open
by design, and the field has no effect on a Stop hook. The Claude Code docs do not say what a client older than
2.1.295 does with the key. On 2.1.312, claude doctor reports nothing for an unknown hook-handler key, so an older
client most likely ignores it and keeps the old fail-open behaviour. That has not been measured on an older client.
An existing install whose settings.json the installer preserved needs the field added by hand.
Two commands cover the normal path. Every other command except /pharn-review and /pharn-memory-promote is
a pipeline stage those two run, available on its own when you want to inspect or drive one step manually. Those
two are standalone: neither is a pipeline stage, and neither is invoked by /pharn-loop or /pharn-ship.
| Command | Use it when you want to... |
|---|---|
/pharn-loop |
Run the full workflow unattended: the model approves the spec, iterates build → regress → verify to a deterministic stop, commits a green result to a local branch, and reports. --quick (6.28.0): a quick SPEC, no base comparison. |
/pharn-ship |
Run the full workflow once, then present the ship record and briefing at the final human decision gate. --quick (6.25.0) runs a shorter spine for a small change and skips /pharn-regress's base comparison. |
/pharn-review |
Run code-review lenses in parallel over any code and merge their structured findings deterministically. This is standalone; it is not a pipeline stage. |
/pharn-spec |
Convert prose intent into a structured SPEC.md, surface gaps, and stop for approval before implementation. |
/pharn-plan |
Convert an approved SPEC.md into a PLAN.md with declared files and declared promoted lessons. |
/pharn-grill |
Challenge the plan before code exists and re-check the spec/plan hash chain. |
/pharn-test |
Write each acceptance criterion's test before the build, into the files /pharn-plan mapped in AC-TESTS.md, run them and require each to fail, and pin the evidence. Runs between /pharn-grill and /pharn-build in /pharn-ship and /pharn-loop. |
/pharn-build |
Implement the plan after setting the active write scope from PLAN.md. |
/pharn-regress |
Re-run existing project suites and record regressions outside the feature. |
/pharn-verify |
Check build artifacts and completeness signals, including declared concrete paths that were never created, and that every acceptance criterion's locked, once-red test passed. |
/pharn-memory-promote |
Promote one lesson into pharn/memory-bank/ through a gated provenance check. |
The command names are generated and drift-guarded in the inventory below; the one-line descriptions in this table are hand-written and are not.
A guarantee must reduce to a deterministic, non-LLM operation such as a hook decision, content-hash comparison, enum/set-membership check, regex scan, or filesystem check. Anything that depends on model judgment is advisory.
- Guaranteed — narrow claims, each backed by a named checker. For example: once the hooks are wired, Claude Code's Write/Edit/MultiEdit/NotebookEdit tools cannot edit the four trusted docs and are held to the active write scope; an approved spec is pinned, so later body drift is detectable; and the verify verdict is computed from a gate map written by the gate runner and validated for internal consistency, not one a model typed (a self-consistent fabricated stamp still passes — see the table's bounds). The full table names the check behind each guarantee and its bounds.
- Detected, not prevented. A write issued through
Bash, or through an MCP server's tool (anmcp__<server>__<tool>name, which is none of the four tools above), passes neither write guard./pharn-verifydetects a non-adversarial one that reached a path the active scope would have denied, with the exceptions that guarantee lists, and fails its verdict; it does not stop the write. - Advisory — everything a model judges: whether a plan is wise, whether a review finding is real, whether the code satisfies the product intent. A green PHARN floor means the named deterministic checks passed. It does not mean the code is correct.
What PHARN does not do today is in Current limitations; the full set of
bounds is in pharn/LIMITS.md.
docs/pipeline.md— how the workflow works, the stages and what binds them, quick mode, your own SPEC template, per-test results, excluding a gate, declaring the base install, acceptance-criteria tests, and the capability tour.docs/guarantees.md— every guarantee with the check behind it, its bounds, and why a hook is not the same as a line inCLAUDE.md.docs/limitations.md— what PHARN does not do today.docs/capabilities/— the generated catalog of grillers, lenses and skills.
PHARN is built with its own workflow, one increment at a time, and the resulting development artifacts are committed in this repository.
That means the repository contains real specs, plans, grill reports, reviews, regression reports, and verification records produced while building PHARN itself. You can inspect the process instead of taking the README's claims on trust.
The inventory below is generated from the live repository by npm run docs:generate and guarded
byte-for-byte by npm run docs:check, so it cannot quietly drift from what is actually built.
- Capabilities — 37 built, counted by the
role:frontmatter test (mirrorspharn/floor/validate.mjs): 13 grillers, 22 lenses, 2 skills (pharn/pharn-core/installed-skill-selection/,pharn/pharn-core/seam-resolver/), 0 validators, 0 verifiers, 0 auditors. Full list:docs/capabilities/README.md. - Contracts — 15 (
pharn/pharn-contracts/):ac-tests,cost-ledger,eval-format,finding-shape,gate-run-record,loop-record,reconciliation-record,regression-report,seam-config,ship-briefing,ship-record,spec-template,stage-exit,test-results-record,verify-report. - Product commands — 11 (
.claude/commands/):/pharn-build,/pharn-grill,/pharn-loop,/pharn-memory-promote,/pharn-plan,/pharn-regress,/pharn-review,/pharn-ship,/pharn-spec,/pharn-test,/pharn-verify. - Dev-apparatus commands — 9 (
.claude/commands/):/pharn-dev-build,/pharn-dev-eval,/pharn-dev-grill,/pharn-dev-memory-promote,/pharn-dev-plan,/pharn-dev-regress,/pharn-dev-review,/pharn-dev-ship,/pharn-dev-verify. - Hook scripts — 4 (
.claude/hooks/):enforce-writes-scope.cjs,protect-trusted-paths.cjs,require-loop-record.cjs,set-writes-scope.cjs. - Floor checkers — 148
.mjsfiles underpharn/floor/(tests excluded).
For concrete examples, browse .dev/features/. The development history includes
cases where PHARN's own review workflow raised defects in PHARN changes before those increments were
finished — see .dev/features/span-redos-linear/REVIEW.md,
where the review caught a false bound shipped by the very increment that was repairing a false bound.
The architecture is specified in four documents:
pharn/CONSTITUTION.md— the principles that govern commands, rules, guarantees, and PHARN's own development process.pharn/ARCHITECTURE.md— the floor, primitives, layer tree, and pipeline.pharn/THREAT-MODEL.md— the security foundation and attack surface.pharn/LIMITS.md— what PHARN does not guarantee.
These trusted docs are protected from edits through Claude Code's Write/Edit/MultiEdit/NotebookEdit tool
surface. Bash writes and writes through an MCP server's tools are outside that protection (see Current limitations). All
four are copied into an install, under pharn/ (before 7.0.0 the last two sat at the project root).
PHARN is small-surface on purpose: a rule or enforcer is added in response to a real failure, not merely because a hypothetical checker could exist.
See CONTRIBUTING.md for the read-first order, required gates, and development loop.
Conduct expectations live in CODE_OF_CONDUCT.md; release history is in
CHANGELOG.md.
The floor and hooks carry zero runtime dependencies beyond the Node standard library; ESLint, Prettier, and markdownlint are development-only.
Found a vulnerability? Please follow SECURITY.md rather than opening a public issue.