Upgrading phax
Upgrade the way you installed phax:
With a downloaded binary, download the new release from GitHub Releases and replace the old one.
After every upgrade
Two commands keep what phax copied into your repository in step with the new binary. Run both in each repository that uses phax, and commit what they change:
Release by release
Only the releases that ask for something are listed. phax never asks for a step silently: a command that needs one refuses with exit 12 and names it.
0.35.0
- Commit trailers name no path of the machine. Phase, interactive and held review commits no longer carry
WorktreeorSession-Id, andGate-Logis relative to the run's folder (phase-02/checks-attempt-01.log), ornone. A stop whose commit was written by an older version still closes, and no record or run file changes. A script that read those trailers reads the phase folder instead:<run>/<phase>/agent-binding.jsonfor the session, thechecks-attempt-NN.logfiles for the gate. See The phase commit for history already written. - Records:
phax records syncaligns the local records branch. It creates the branch at the remote's tip when it is missing, fast-forwards it when it is behind, and refuses when it has diverged from the remote's or shares no commit with it.--replayrecommits the local-only records onto the remote's tip, keeping their trees, messages and authors, and prints the previous tip; a record the remote already holds file for file is left out, and a key the remote also holds with other content is replayed and named. phax never forces the branch. See When the records branch and its remote disagree. - Records:
phax records syncworks for the in-repo destination. It fetches the records branch frompublish.remoteand no longer saysnothing to sync, so it now needs that remote to answer: a script that runs it unconditionally can see it exit 1 on a network or authentication failure. - Records:
phax records sync --jsongains a requiredbranchoutcome (created,fast-forwarded,replayedorunchanged) on aclonedorfetchedanswer, which also gainsremoteName. Thereplayedoutcome carriesskippedandsharedKeys, and the answer gainsreplay. A refusal adds one error:RecordsBranchDivergedError(reason,localTip,remoteTip,localOnly,remedy). A script that matches the answer's keys exactly adds them. - Records:
phax records status --jsongains a requiredbranchobject (state,localTip,remoteTip), betweenremoteandpending. The text gains aRecords branch:line with its remedy; before anything is written or fetched it namesphax records syncinstead of saying every record is pushed. - Records: pending counts only local-only records. On a diverged or unrelated branch, the records the remote already holds are no longer counted.
- Records: a refused records push names the branch state and the fix. When the branch is behind, diverged or unrelated, the message gives the state and
phax records syncorphax records sync --replay, not a plaingit push. A record is also written on top of the remote's tip as phax last fetched it, so a lagging branch catches up with the write. - Records: a refused records push is always reported.
phax publishandphax runprint it as a warning, where it was shown only with--verbose. - Artifacts: the
--jsonrecord ofartifact new --headlessandartifact decidegains a requiredbranchNotebesidepushWarning: the note of a record kept on a diverged or unrelated records branch, or null.pushWarningstill names only a failed push.
Record format, manifests and versions are unchanged. No migration is needed, but records an earlier version left on a stranded branch (a fresh clone, a second machine) stay pending until you run phax records status, then phax records sync --replay when it names it.
0.34.0
- Gate steps accept
partial. An optionalpartialobject with afilesand/or adiffcommand, joined by pipes only, is shown in the phase prompt and allowed in secure mode; a malformed one is refused when the config loads, as is a quote, a backslash or a*in it.phax validateandphax run --dry-runlist the prefixes each profile's partial commands allow (the--jsonanswers gainpartialPrefixes). See Partial commands. Records readers:security.jsonagentCommands[].sourcegains the value"partial"; the file stays at version 3. No migration is needed. - Command line: a refused call exits 64. An unknown command, an unknown option or a bad argument (a missing argument or option value) now exits 64 instead of 1, with the same text. A group called alone, such as
phax artifact, writes its help on stderr and exits 64. A command answering through the runner refuses an operand it does not declare, which it ignored before:phax validate extraandphax ls extraexit 64. A script that checks for exit 1 on a mistyped call checks for 64. The page Machine answers describes--json, the message lines and--json-schema. - Command line: an unexpected crash exits 70. It writes
defect: …on stderr, where it wroteUnexpected error: …and exited 1. A command's own refusals keep their codes, and the phase guard still refuses with exit 1. - Command line:
phax --json-schemaprints the contracts of the commands answering through the runner, with the program's exit codes and the shapes of its message lines.--json-schemaafter a command that does not answer through the runner is refused with exit 64. - Run list:
phax ls --jsonrows gain the requiredpullRequest. It is the URL of the pull request phax opened for the run, ornullwhen it opened none or the run's publication record cannot be read.run-listis stamped 0.34.0; a list stamped 0.26.0 or 0.30.0 is still parsed by@lbdremy/phax-schemas(parseRunList), andtoLatestRunListgives each of its rowspullRequest: null. A script that matches the row's keys exactly adds the new one. - Schemas package:
artifact-status,plan-overviewandplan-lintare new formats at 0.34.0.@lbdremy/phax-schemasexportsparseArtifactStatus,parsePlanOverviewandparsePlanLint, with their schemas,toLatestfunctions and JSON Schemas, andparseDocumentidentifies each. They describe the answers ofphax artifact status --json,phax plans overview --jsonandphax plans lint --json, which phax never saves. - Plans:
phax plans overview --jsonandphax plans lint --jsonanswer versioned documents. Each prints aplan-overvieworplan-lintdocument whose first key is$schema, followed by the keys the unversioned object had, in the same order. A script that reads the object as it was ignores$schema, or reads it withparsePlanOverviewandparsePlanLint.plans lintstill exits 1 when a finding is an error, with or without--json. - Artifacts:
phax artifact status --jsonis new. It prints anartifact-statusdocument: the path, kind, status, legal transitions, approval, authoring and questions the text reports. Without--jsonthe text is unchanged. - Command line:
artifact status,plans overview,plans lintandlsanswer through the runner. Under--json, stderr carries only message lines, one JSON object each: a warning (an orphan approval record, a failed architecture provider) is anoteline, a failure anerrorline, a refused call arefusedline.--json-schemaafter each prints its contracts. Without--json, their text, warnings, error sentences and exit codes are unchanged. - Command line: the help of a command answering through the runner prints its long help before its options, wrapped at 80 columns; its usage line, options, help texts and examples are the same.
phax help <command>andphax <group> help <command>print it too. - Plans:
plans status,plans overlapandplans estimateanswer through the runner. Their--jsonobjects are unchanged exceptplans status --apply, which gainswrittenafterapplied, the flipped plans' records. Under--jsontheir warnings are note lines.phax plansalone and--json-schemafollow the runner. - Command line:
validate,session-info,path,brief,architecture,completionsandreportanswer through the runner. Under--jsoneach answers one JSON object and message lines on stderr, and--json-schemaafter each prints its contracts. Their text and codes are unchanged;phax pathprints the same bytes.phax completionsprints a script that embeds the regenerated usage spec (the exit-code block after the root help,--json-schema, each command's--json), so its bytes change while completion works as before. - Command line:
cost backfill,schema upgrade,security statusandskills installanswer--json. The writing ones list the files they wrote as written-file records (cost backfillthe cost-ledger.json files, by absolute path;schema upgradethe two schema files;skills installeach installed file), andskills installnames the files already installed when it fails part way. Without--jsoneach prints the same text with the same codes. Thecost,schema,securityandskillsgroups called alone are refused with exit 64 and their help; barephax security, which ran the status report and exited 0, now does the same, so callphax security status.--verboseand--traceonsecurityandsecurity statusare the program's options, accepted anywhere in the call as before. - Command line: the
recordscommands answer--json.records initprompts, sorecords init --jsonis refused with exit 64 before anything is asked, although its help lists--jsonas every command's does.records explain --jsoncarries the text of each artifact its flags ask for (--prompt,--diff,--transcript,--gates,--briefs),nullwhen the record does not carry it. Without--jsonthe text, error sentences and exit codes are unchanged, andphax recordsalone is refused with exit 64 and its help. - Artifacts: the transitions,
artifact schemaand the migrations answer through the runner. Under--json,artifact approve,reopen,stale,abandonandcompleteanswer an object (the status reached, the previous status, the path, the baseline, who approved or reopened, the approval record a reopen, complete or abandon deleted, the bound plans, the commit) and list the files they wrote as written-file records (a headless artifact's sidecar only when complete or abandon moves it);artifact migrate-approvalsandartifact migrate-documentsanswer their report with the record files, or the sidecars and bodies, they wrote.approve --jsonandreopen --jsonwithout--byare refused with exit 64 before anything is written, because a terminal standing for the operator is a person's gesture; the refusal readserror: --json: artifact approve prompts; pass --by.artifact schemaprints the same bytes in both modes. Under--json,migrate-approvals' orphan-record warnings are note lines on stderr. An unknownartifact schemakind now exits 64, with the runner's text. - Artifacts:
artifact decideandartifact new spec|plananswer through the runner.--verdicts <file|->and--brief <file|->are read before anything runs: a terminal on stdin, a file that cannot be read, and a verdicts file that does not decode or does not fit the artifact (another artifact's path, an unknown question or option, a question named twice, a missing note) are refused with exit 64 and a refused line naming the file or stdin and the field, such aserror: verdicts.json: verdicts[0].chosen is not an option of q-cadence; a file stamped with an older decide-verdicts release is refused the same way, at$schema. These sentences no longer list the question's options or the artifact's question ids, and a field the file's schema refuses is writtenverdicts.0.note. Before,decideanswered these with exit 12 andartifact newwaited on a terminal stdin.--briefgiven without--headlessis now read, and refused when unreadable.decide --jsonwithout--verdicts, or without--by, is refused with exit 64 before anything starts, where it exited 12 with--json needs --verdicts; the refusal readserror: --json: artifact decide prompts; pass --verdicts and --by.decide --verdicts --jsonprints its object two-space indented, not on one line, withwritten(the files its commit holds) andrecord(the decision record's outcome) after its twelve keys, and itsrecordline no longer reaches stderr.artifact new --jsonanswers the path, kind and lineage of a blank skeleton, or with--headlessthe path, sidecar, commit and authoring record. Under--jsonthe sessions' progress lines (authoring …, the decide session's lines) are note lines on stderr, and a failed commit's error line lists the files it left written.phax artifactandphax artifact newalone are refused with exit 64 and their help on stderr, asphax agentandphax agent setupare: the help puts a group's long help before its commands, listshelp [command]last, and ends withRun `phax --help` for usage..phax artifact archivekeeps its sentence but exits 64, not 1, follows it withRun `phax --help` for usage., and answers--helpwith that refusal, not with help. The help ofdecideandartifact new spec|planlists--verdictsand--briefafter the other options, beside astdin: JSONorstdin: textline. Without--jsonboth commands print the same text with the same codes otherwise. - Agent:
agent models,resolve,probe,setup providersandsetup mistral-vibeanswer through the runner. Each answers--jsonwith one indented object (modelsthe routing table and priority,probeeach provider's availability and whether config enables it, the setup commands what they did).agent resolve --jsonis unchanged in its bytes. The setup commands list the files they wrote (providers.json, ~/.vibe/config.toml, their backups, a scaffolded model-routing.json) as written-file records with absolute paths. Under--json, an invalid--effortor a failed setup is oneerrorline on stderr. Without--jsonthe text, error sentences and exit codes are unchanged.phax agentandphax agent setupalone are refused with exit 64 and their help. - Root commands:
init,unlock,archiveandpruneanswer--json.init --yes --jsonanswers the config and schema paths, whether the wizard ran interactively, and phax.json, phax.schema.json and phax.user.schema.json as written-file records;init --jsonwithout--yesis refused with exit 64 before anything is asked or written, even off a terminal, namingpass --yes.unlock --jsonanswers{ shortName, removed, qualifiedName },removedone ofstale(the holder's process is gone),expired(the lock outlived its lifetime),forcedornone.archive --jsonanswers{ qualifiedName, crossProject }.prune --jsonkeeps its object byte for byte, but its preview lines (Prune from namespace …) are now note lines on stderr, andprune --jsonwithout--yesor--dry-runis refused with exit 64 and one refused line,error: --json: prune prompts; pass --yes to proceed or --dry-run to preview only, where it printed the preview document and exited 1. A held lock, a refused archive and a failed prune are oneerrorline under--json. Without--jsonthe text, error sentences and exit codes are unchanged, andprunekeeps-ybeside--yes. - Review:
review-handoff,publish-pr,review-complianceandreview-codeanswer through the runner. Under--jsoneach answers one object: the run's qualified name and whether it is another project's, thenreviewHandoffandwrittenforreview-handoff,pullRequest(the URL, ornullwhen the branch was pushed without opening one) forpublish-pr,verdict,review,notice,warningsandwrittenforreview-compliance, and forreview-code --headlessfindings(a count and its summary),findingsPath,fixHint,notice,warningsandwritten.review-handofflists the reconciliation,review-handoff.mdandfinal-report.mdit regenerated,review-complianceits Markdown and JSON, andreview-code --headlesscode-review.jsonandcode-review-session.json, each as a written-file record. The records and push warnings are note lines on stderr under--json.review-code --jsonwithout--headlessis refused with exit 64 before any session starts, since it hands the terminal to a session; without--json, its exit is still the session's. Without--jsonthe text, streams and codes are the same. Inphax --help,publish-prandreview-complianceshow[options]in their usage, since they now take--json. - Sessions:
enter,enter-phase,shell,openandadjust-plananswer through the runner. Each hands the terminal to a session, a shell or an editor, so under--jsoneach is refused with exit 64 and onerefusedline before anything is read or started, naming the command that answers instead:phax session-info <run> --jsonforenterandenter-phase,phax path <run> --jsonforshellandopen, and foradjust-planthat it runs from a terminal without--json. Before,--jsonwas an unknown option. Without--jsoneach prints the same lines and exits with the code of the process it handed the terminal to, as before; their error sentences and codes are unchanged.shellstarts$SHELL(orbash) through the session port, andadjust-planreads its plan through the file system port. Inphax --help,enter,shellandopenshow[options]beside their name. - Runs:
run,resumeandreset-phaseanswer through the runner. Under--jsoneach answers one object naming its outcome:runandresumeanswer{ qualifiedName, crossProject, outcome }, the outcome adry-run(the report--dry-runprints), apreview(resume without--yes: the state, the next phase, the skipped phases, the owed step),already-in-review,review_open(the phase count, the pull request, the completed artifacts and the final report's path),stopped(exit 13, with the stop report) orpaused;reset-phaseanswers itspreviewor itsreset. Every progress line (Run: <run>,using cached extraction,would resume …, the extract warnings, the--verboseevents) is a note line on stderr under--json, written as it happens, so the order of stdout against stderr is no longer a contract there. A paused run (a rate or usage limit, a lost network, a handoff turn, gates exhausted or a refused gate step, a phase with no changes, a refused close) is an answer, not an error: the outcomepausednames its reason, phase, resume command, resume-instructions path,resetAtwhen known and its exit, and the call exits with that code, as before. A refusal or failure before the run starts or outside a pause is oneerrorline with today's code; a held run lock isLockConflictErrorwith exit 7 and fieldsrun(the short name) andpid.resumeandreset-phasewithout--yesanswer their preview under--jsonand take no lock. Every exit code stays.resume --skip-oraclekeeps its sentence but exits 64, not 1, and follows it withRun `phax --help` for usage..resumeandreset-phaseno longer list their own--verboseand--trace, in their--helpor in the usage spec: they are the program's options, accepted anywhere in the call as before. Under--json, auto-publish's--verboselines (Publishing branch to …,Pull request created: …) and the run-end reviews'[phax] …lines are note lines too, as arepublish-pr --verbose's. An interruptedrunorresume(SIGINT or SIGTERM) still saves the run and exits 130; under--jsonits sentence is note lines. A run of short flags is read as one word:resume x -yvis refused naming-yv, where it named-v. The help ofresumeno longer says it prompts: without--yesit prints where it would resume and exits 0. Without--jsonthe text, streams and codes are unchanged;-ystays beside--yes, andrun --holdkeeps its optional value. - Command line: every command answers through the runner.
phax --json-schemalists every command, and--json-schemaafter any command prints its contracts; it is no longer refused after a command.phax help <command>answers that command's help, andphax help <group>its group's. phax.usage.kdl is written in one style, from the command definitions, under a two-line header: its groups no longer declaresubcommand_required(a group called alone is still refused with exit 64 and its help), its program name is a bare word (name phax), and its global flags list--usageand--usage-formatbefore--verboseand--trace. A script that reads the file with theusageCLI sees the same commands, arguments and flags.--version,-Vand--usageprint as before when called alone. Root help gains a--json-schemaline under Options;path,brief,architectureandcompletionsshow[options]besideenter,shell,open,publish-prandreview-compliance, andsecurityno longer does.--helpand--versionare answered alone: with any other word (phax --help run,phax run --version,phax -V run) the call is refused with exit 64, where 0.33.0 printed the help or the version. Barephax, orphaxwith only program options (phax --verbose), writes the root help on stderr and exits 64, not 1, ending withRun `phax --help` for usage..phax <group> help [command]answers likephax <group> [command] --help. The phase guard refuses inside a phase worktree with today's sentence and exit 1, or onerefusedline under--json, while--help,-hand--json-schemastill answer; it now runs before the call is read, so a malformed call there (phax runwithout--plan) gets the guard's sentence rather than the missing option's, and a group alone gets its help with exit 64. - Claude Code: headless sessions load only the repository's harness. Phase sessions, handoff, authoring, decide's handback, review and structured completions no longer load your Claude Code user settings, skills, plugins, hooks, instructions or auto-memory; interactive sessions and the usage probe are unchanged. The repository's
CLAUDE.local.mdand.claude/settings.local.jsonare left out too. Your ownpermissions.denyrules and hooks no longer guard headless sessions: in unsafe mode nothing else blocks what they blocked, so move a guardrail every session must keep into the committed.claude/settings.jsonor managed settings. If you authenticate through user settings (anapiKeyHelper, anenvblock, Bedrock or Vertex set there), runclaude /loginor setANTHROPIC_API_KEYin the environment phax runs in, or, for Bedrock, Vertex or a gateway, exportCLAUDE_CODE_USE_BEDROCK,CLAUDE_CODE_USE_VERTEXorANTHROPIC_BASE_URLand its credentials there; otherwise the phase fails withnot-authenticatedand names the remedies. If another credential is available (aclaude /loginsession,ANTHROPIC_API_KEY), a headless session uses it silently instead of your settings' backend. A setting a headless session needs goes in the repository's committed.claude/settings.json. Phase records carry the requireduserConfig(excludedfor claude-code,loadedfor codex-cli and mistral-vibe), andphase-record-manifestanddecision-record-manifestare stamped 0.34.0; earlier records read asloaded, andexcludedcovers a phase's headless sessions only. See What a Claude Code session loads.
Later entries of this release follow.
0.33.0
- Plan documents, run plans and extraction answers:
plan-document,phax-planandphax-plan-extractionare stamped 0.33.0. A phase'soracle: { dischargedBy, files }key isacceptance: { passedBy, files }, a test line's looporacleisacceptance, the techniqueboundaryis accepted, and the criterion wordsacceptance,in-phaseandoracleare reserved: no criterion id may be one of them. - Records and schema readers:
run-status,phase-status,gate-attributionandphase-record-manifestare stamped 0.33.0. A run's hold selectionoracleis recordedacceptance, a corrected stop'soracleStepisacceptanceStep, a gate step's surfaceoracleand a record's verified surfaceoracleareacceptance, and a record's role isacceptancewithpassedByorpassingwithacceptancePhase, in place oforaclewithdischargedByordischargingwithoraclePhase. - Provider and run folders: the new
acceptance-requestreplacesoracle-request. The provider reads anacceptance-requeststamped 0.33.0 on stdin, with verdictauthoredorpassedand the keysphase,acceptancePhase,baseandfiles, plusacceptanceCommitonpassed. phax saves it aschecks-attempt-NN.acceptance-request.json.oracle-requestis frozen at 0.26.0 and read only: phax writes it nowhere. A provider you wrote readspassed,acceptancePhaseandacceptanceCommitin place ofdischarged,oraclePhaseandoracleCommit, and refuses any other document, as the example does: a provider that still reads anoracle-requestreceives none, and phax refuses a plan with an acceptance phase when noacceptanceprovider is configured. - The Vitest example moved from
examples/vitest-oracles/toexamples/vitest-acceptance/. If you copied it, or your phax.json points atnode examples/vitest-oracles/main.mjs, change the path tonode examples/vitest-acceptance/main.mjsunder the keyacceptance. It reads only anacceptance-request, names--skip-acceptancein its remedy, and reports findings whose ids start withacceptance-(acceptance-unmarked,acceptance-not-run,acceptance-marker-left,acceptance-changed,acceptance-unparsable). - Telemetry: the event
oracle.edits.unjudgedisacceptance.edits.unjudged. Its console line isphax·acceptance.edits.unjudged, and a failed step namedoraclein a span or an event is namedacceptance. A dashboard or an alert on the old name stops matching. - Overview and records:
acceptanceRoleand theacceptancerows.phax plans overview --jsongivesacceptanceRole, whose kind isacceptance(withpassedBy),passing(withacceptancePhase) ornone, in place oforacleRole. The overview printsacceptance roleandphax records explainprintsacceptance, where they printedoracle roleandoracle; the review handoff's table is## Acceptance pairs. A script that matches those names changes with them. - Skills: rerun
phax skills install. The bundled skills (phax-planning, phax-decide-plan, phax-decide-review, phax-review) teach acceptance phases, and a copy made by an earlier release teaches the old names. See After every upgrade. - Schema readers: renamed exports in
@lbdremy/phax-schemas.parseAcceptanceRequestreads anacceptance-requestand anoracle-requestat 0.26.0, andtoLatestAcceptanceRequestlifts either to the latest acceptance request (dischargedbecomespassed,oraclePhasebecomesacceptancePhase,oracleCommitbecomesacceptanceCommit).OracleRequestSchema,parseOracleRequest,toLatestOracleRequestand the typesOracleRequest,LatestOracleRequestandOracleRequestShapeare gone;OracleRequestV0_26_0Schemastays for the frozen shape. An older@lbdremy/phax-schemascannot read 0.33.0 documents. - Older records read under the new names. A plan sidecar, run plan, status file, gate attribution or record manifest written by an earlier release still reads, lifted to the acceptance names when phax or
@lbdremy/phax-schemasreads it.phax records explainlists a record's.acceptance-request.json, and.oracle-request.jsonfor a record written before. - Configuration: the
oracleskey isacceptance. It applies in phax.json, phax.local.json and ~/.phax/config.json. A layer that still carriesoraclesis refused with<file>: "oracles" is no longer read: write "acceptance". Runphax schema upgradeto refresh the editor schemas. - Command line:
--hold acceptanceand--skip-acceptance. They replace--hold oracleand--skip-oracle, refused with--hold 'oracle' is no longer read: write --hold acceptanceand--skip-oracle is no longer read: write --skip-acceptance. - Hand-written plans: the header lines and the loop.
**Oracle phase:** yes | discharged by phase-NNis**Acceptance phase:** yes | passed by phase-NN,**Oracle files:**is**Acceptance test files:**, and a test line's looporacleisacceptance.phax plans lintreports the old lines on itsacceptancecheck (formerlyoracles) with**Oracle phase:** is no longer read: write **Acceptance phase:**and**Oracle files:** is no longer read: write **Acceptance test files:**, andphax runandphax resumerefuse them with exit 11, "Acceptance preflight failed". The old loop is refused withtest line <n> uses the loop 'oracle', which is no longer read: write 'acceptance', before any extraction. - Run
phax artifact migrate-documentsonce. It restamps every live plan sidecar stamped below 0.33.0, rewrites its plan.md test lines from the looporacletoacceptanceand its header lines to**Acceptance phase:**and**Acceptance test files:**, and commits both files. Spec sidecars are already at their current stamp, 0.32.0, and stay as they are. It refuses a plan whose body was edited by hand since its sidecar was written, and a plan with a test line whose criterion isacceptance, naming the phase and the line: rename that criterion in the spec and the plan, or re-author the plan, then rerun. Until thenapprove,reopenanddeciderefuse, naming the command. - Runs in flight. A
phax-plan.jsonextracted by 0.32.0 or older is refused byphax resumeandphax validate --plan, which namephax archive <run> --force. Finish the run with the release that started it, or archive it and run its plan again.phax lsandphax session-infostill show it.
0.32.0
- Records and schema readers:
hand-decision-answeris two formats.spec-hand-decision-answerandplan-hand-decision-answerare each the old variant with a$schemaconst.hand-decision-answerstays readable at 0.25.0, and phax no longer reads or asks for it. - Schema readers:
plan-documentno longer pairssourceSpecandcompletesSpecin its schema. phax checks the pair. - Schema readers: every answer's
$schemais a const of its 0.32.0 stamp. That covers spec and plan sidecars, the two hand-decision formats,first-reading,interactive-handoff,code-reviewandphax-plan-extraction. An older@lbdremy/phax-schemascannot read them, and the package reads a const format only at its own stamps. - Schema readers:
phax-planis restamped 0.32.0, its decoding unchanged. An older@lbdremy/phax-schemascannot read a 0.32.0 run plan. - Schema readers: the
code-reviewanswer and file are one shape,$schemain place ofversion. - Schema readers: new
phax-plan-extractionformat, the answer of the plan extraction. - Headless authoring answers carry
$schemain place ofversion: 1. - Records and schema readers:
compliance-review.jsonis stamped 0.32.0.$schemais a const, and two ledgers are required:unplannedChanges(phaseId,path,note) andunmetPromises(phaseId,promise,note), each possibly empty. A 0.32.0 file without either is refused. - Records and schema readers: older compliance reviews read without ledgers. A 0.17.0 or pre-schema review still reads, never with empty ledgers;
@lbdremy/phax-schemasgives themUnknownledgers. - Records readers:
compliance-review.mdis rendered by phax from the answer, with fixed sections (summary, one per phase, unplanned changes, unmet promises, attention points, pointers). phax review-complianceno longer printsVerdict: unknown. A review refused twice fails with exit 1 and writes no file and no records entry; the corrective resume's transcript iscompliance-review-retry.session.jsonl.- The compliance session is read-only and writes nothing under
.phax-context/. - Run
phax artifact migrate-documentsonce. It restamps every live spec and plan sidecar below its current shape, their bodies unchanged. Until thenapprove,reopenanddeciderefuse, naming the command.
See Structured answers and providers for what each provider now checks.
0.31.0
- Records readers:
security.jsonis version 3. Every phase'ssecurity.jsoncarries a requiredprotectedPaths, betweenskillEditGrantsandproviderSkippedForSecurity:nullwhenphax.jsondeclares none, otherwise{ globs, enforcement }withenforcementone ofenforced,partialorcommit-check-only. phax reads version 1 and 2 files as version 3. - Records readers:
gate-attribution.jsonis stamped 0.31.0. It carries a requiredprotectedPaths:null, or{ result, changes }(apasswith no change, or afailwith at least one{ path, kind, entry }). 0.26.0 documents and older still read, asnull. An older@lbdremy/phax-schemascannot read 0.31.0 attributions.
0.30.0
- Exit 14: the provider could not be reached. A command whose agent cannot reach its provider (a network failure) now exits 14, which is resumable, instead of 5. Treat 14 like 8, but retry as soon as the network is back.
- Tools that read a phase folder: a failed session's files are archived. When a session starts in a folder that holds an
agent-error.log, phax renames itagent-error.failed-NN.log, and the transcript the new session would overwrite<name>.failed-NN.jsonl. See Troubleshooting a failed agent session. - Review UIs:
review-stop.jsonandphax ls --jsonare stamped 0.30.0. A stop'slastClosegains the resultnetwork({ result, at, exitCode: 14, detail }, nostep) when its closing turn could not reach the provider; the run staysawaiting_review. 0.26.0 documents still read. - Records readers: a failed decision record names its failure.
failure(the session's failure kind,answer-refused,artifact-changedorcommit-failed) is required on a failed decision record, stampeddecision-record-manifest0.30.0. Records written earlier still read, without it. - Mistral Vibe 3.0.3 or later is required. Run
uv tool upgrade mistral-vibe. phax checksvibe --versionbefore its first Vibe session and refuses an older Vibe, naming the installed version. - phax no longer reads sessions from
~/.vibe. A Vibe turn's answer, session id, usage and transcript come from its stream and from theexport.jsonVibe writes beside it.VIBE_HOMEis no longer a provider store root.phax agent setup mistral-vibe --install-model-aliasesstill appends the phax aliases to your Vibe config. - Secure mode runs Vibe. It is recorded as filesystem jail
strong, with command enforcementprefix, so asecurerun no longer falls back to Claude Code. Your ownconfig.tomlsettings (bypass,outside_workdir, tool allowlists) never reach a secure or read-only phax session. - Rename a custom
accept-editsagent. If~/.vibe/agents/accept-edits.toml(or$VIBE_HOME/agents/accept-edits.toml) exists, phax refuses every secure and read-only Vibe session (a stop's turns, decide, authoring), naming the file: it would replace the built-in agent phax runs. Rename it, for example tomy-accept-edits.toml, and pick it with--agent my-accept-editsin your own sessions. - Vibe config under an extra writable root. A secure Vibe session is refused when a root in
security.filesystem.allowWriteholds.vibe/or.agents/, naming it: Vibe loads hooks, agents and tools from such a root. Move that config out, or drop the root. Phase agents on Vibe can no longer write.vibe/or.agents/in the worktree either. - A turn that ran under another config is refused. After each secure or read-only turn, phax compares the config Vibe ran with (its
export.json) with what phax set, and refuses the result on a difference, naming the field inagent-error.log. - MCP on Vibe. MCP servers and connectors are off in a session whose
mcp.modeisdisabledorallowlist: Vibe cannot load only some servers. - Records. Vibe usage is
{ provider, inputTokens, cachedInputTokens, outputTokens, costUsd }, summed over the phase's turn, fix attempts and handoff. Older records still read; an older@lbdremy/phax-schemascannot read 0.30.0 records. - Two Mistral efforts,
offandhigh. The Mistral API serves onlynoneandhighreasoning for mistral-medium-3.5, sophax-mistral-medium-3.5-low,-mediumand-maxare gone from the catalog.phax plans lintandphax runrefuse a plan that names one: rewrite it tophax-mistral-medium-3.5-off(effortoff) orphax-mistral-medium-3.5-high(efforthigh). A Claude Sonnet 4.6 phase routed to Vibe runs thehighalias, recorded as anupgradefromlowormediumand adowngradefrommax. ~/.phax/providers.jsonthat still lists them. Every command that reads the provider catalog refuses it, naming each id. Runphax agent setup providers --writeto drop them: it also enables every provider whose CLI is installed, so setenabled: falseagain on any you do not want. Or delete the three entries by hand.- The three aliases in your Vibe config.
phax agent setup mistral-vibeinstalls only theoffandhighaliases, and names the old ones it finds in~/.vibe/config.tomlwithout touching them. Delete their[[models]]blocks if you like; nothing uses them.
0.28.0
- A spec or plan option named
other.otheris now a reserved id, implicit on every question. A live spec or plan that lists an option, or a recommendation, with the idotheris refused: an authored answer exits 5,phax artifact approveanddecideon the artifact exit 12, andphax artifact statusreports a §9 that does not parse. Rename that option (in the sidecar and in the Markdown alike) before upgrading. ADecision: other — …line, and a verdict that choosesother, are accepted. - Plan sidecars:
testStrategybecomes a list of test lines. Runphax artifact migrate-documentsonce in each repository with plans underdocs/. A sidecar whose every test strategy follows the line format is lifted and committed. One holding prose is refused, naming the phase and the line: rewrite it in the line format in the sidecar and in plan.md alike, or re-author the plan, then run the command again. Until then,phax artifact approve,reopenanddeciderefuse and name the command. Archived plans are not touched and still read. - Hand-written plans. Write each
### Test strategyentry in the line format:<guarantee> — <size> · <technique> · <oracle> · <loop>[ · <criterion>].phax plans lintreports a departing line as atestserror.phax run,phax plans overlap,phax adjust-planandphax plans statusrefuse such a plan before any model call, naming the line andphax plans lint, andphax plans overviewexits 1. A phase with no section, or with- (none), has no line. - Runs in flight. A
phax-plan.jsonextracted by an older phax is refused byphax resumeandphax validate --plan. Finish the run with the release that started it, or archive it withphax archive <run> --forceand run its plan again.phax lsandphax session-infostill show it. - New
testswarnings inphax plans lint. Nothing to do. An uncovered acceptance criterion, or a source spec without a readable sidecar, is a warning that never fails lint or refuses a run.
0.27.0
- Spec sidecars gain a
summaryon every surface element. Runphax artifact migrate-documentsonce. It restamps every live spec sidecar, writes a placeholder summary on each element, re-renders §6 and commits; the report counts the summaries left to write per spec. Then write each summary, one sentence in the consumer's words, in the sidecar and on its §6Summary:line.phax artifact approveandcompleterefuse (exit 12) a spec until its placeholders are gone, andapprove,reopenanddeciderefuse until the migration has run. Archived specs are not touched. - Hand-authored specs: add a
Summary:line under each §6 element heading if you want the same layout; phax does not check it. - A run whose plan lists an oracle file twice.
phax plans lintandphax runnow refuse a path repeated on an**Oracle files:**line, and so doesphax resume(exit 11, "Oracle preflight failed") for a run an older phax started from such a plan. The refusal names the run's copy of the plan,~/.phax/runs/<namespace>.<name>/plan.md: remove the repeated path there, check it withphax plans linton that file, and resume. Or archive the run withphax archive <run> --forceand run the fixed plan again. - A phase an older phax left
committed. Nothing to do. Before 0.27,phax resumewent past a phase interrupted between its handoff and its cleanup, or cut short in its handoff turn, and left itcommittedwhile later phases ran. phax 0.27 finishes such a phase only when it is the run's last started phase; one with later phases on disk stays as it is, without its record or cleanup, and its worktree stays until the run is archived.
0.26.0
-
Oracle phases need an
oraclesprovider:phax plans lintandphax runrefuse an oracle phase without one. Nothing to run; phase records written earlier read with no oracle role. -
Plan sidecars gain
assignmentReason,modeandoracle.modelReasonbecomesassignmentReason, and every phase gainsmodeandoracle. Runphax artifact migrate-documentsonce in each repository with plans underdocs/. It renames the key, writesmode: "headless"andoracle: nullon every phase, rewrites**Model reason:**lines as**Assignment reason:**, and commits the result. Until then,phax artifact approve,reopenanddeciderefuse and name the command. -
Hand-written plans. Rename each
**Model reason:**line to**Assignment reason:**.phax plans lintreports the old label as an error, andphax runandphax resumerefuse it with exit 11. -
Runs in flight. A run started by an older phax keeps a
phax-plan.jsonin the older shape, which this phax refuses to resume. Finish the run before upgrading, or archive it withphax archive <run> --forceand run its plan again. -
run-status.jsongains a requiredhold. Scripts that read it find{ selection: none }on every run not started with--hold, and runs written by older releases read asnone. Nothing to run. -
New
modelswarnings inphax plans lint. Nothing to do. Lint now compares every phase with the policy default (claude-sonnet-5-5atmedium) where no assignment bound governs it, so a phase at another model or effort with no**Assignment reason:**line gets a warning. Warnings never fail lint or refuse a run. Add a reason line to silence one, or declare assignment bounds. -
phax ls --jsonprints a run list. The bare array becomes onerun-listdocument: read.runs[]instead of.[].currentPhase,gateProfile,recordsPendingandarchivePathare null when unknown, where they used to be dropped or printed as-, and every run carriesstop, its review-stop document or null. Nothing to run. -
The record manifest gains
modeandstop. Records written by released phax read as headless phases that never stopped. Records written by 0.26.0 development builds before this change no longer read:phax records explainon a commit of such a run fails and names the malformed manifest, andphax records listskips the record. -
security.jsonmoves to version 2. It gains a requiredstop. A version-1 file reads as a phase that never stopped; a close rewrites it as version 2. A run started by a 0.26.0 development build with a closed stop or aninteractive-session.jsondoes not resume. -
Closing a stop needs a git identity (
user.nameanduser.email) in the main checkout; a close without one is refused with exit 1.
None of these four changes needs a command.
0.25.0
- Spec and plan sidecars move to their current shapes. Run
phax artifact migrate-documentsonce in each repository with specs or plans underdocs/. It rewrites every live sidecar below its current shape and commits the result. Until then,phax artifact approve,reopenanddeciderefuse and name the command. - Cost ledgers for older runs. Nothing to do.
phax plans estimate, the only reader of cost ledgers, writes one for every kept run that has none before it estimates.phax cost backfilldoes the same by hand.
0.19.0
- Approval ledgers become one record file per artifact. Run
phax artifact migrate-approvalsonce on a clean tree in each repository that still hasdocs/plans/approvals.jsonordocs/specs/approvals.json. It commits the split by itself. Until then, approve,plans status,artifact statusandphax runrefuse with exit 12 and name the command.