For AI agents: the complete documentation index is available at https://docs.phax.run/llms.txt, the full documentation bundle is available at https://docs.phax.run/llms-full.txt, and this page is available as Markdown at https://docs.phax.run/releases/0-30-0.md.

phax 0.30.0

Changes since 0.29.0: v0.29.0...v0.30.0.

Highlights

  • Mistral Vibe 3 — phax reads a Vibe session only from what its turns hand over, and confines it per invocation, never through the person's configuration.
  • Provider failures — The provider's own verdict on its session is the cause phax reports, first and in full; nothing the session read or wrote is ever taken for it.

Upgrading

  • 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 it agent-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.json and phax ls --json are stamped 0.30.0. A stop's lastClose gains the result network ({ result, at, exitCode: 14, detail }, no step) when its closing turn could not reach the provider; the run stays awaiting_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-changed or commit-failed) is required on a failed decision record, stamped decision-record-manifest 0.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 checks vibe --version before 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 the export.json Vibe writes beside it. VIBE_HOME is no longer a provider store root. phax agent setup mistral-vibe --install-model-aliases still appends the phax aliases to your Vibe config.
  • Secure mode runs Vibe. It is recorded as filesystem jail strong, with command enforcement prefix, so a secure run no longer falls back to Claude Code. Your own config.toml settings (bypass, outside_workdir, tool allowlists) never reach a secure or read-only phax session.
  • Rename a custom accept-edits agent. 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 to my-accept-edits.toml, and pick it with --agent my-accept-edits in your own sessions.
  • Vibe config under an extra writable root. A secure Vibe session is refused when a root in security.filesystem.allowWrite holds .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 in agent-error.log.
  • MCP on Vibe. MCP servers and connectors are off in a session whose mcp.mode is disabled or allowlist: 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-schemas cannot read 0.30.0 records.
  • Two Mistral efforts, off and high. The Mistral API serves only none and high reasoning for mistral-medium-3.5, so phax-mistral-medium-3.5-low, -medium and -max are gone from the catalog. phax plans lint and phax run refuse a plan that names one: rewrite it to phax-mistral-medium-3.5-off (effort off) or phax-mistral-medium-3.5-high (effort high). A Claude Sonnet 4.6 phase routed to Vibe runs the high alias, recorded as an upgrade from low or medium and a downgrade from max.
  • ~/.phax/providers.json that still lists them. Every command that reads the provider catalog refuses it, naming each id. Run phax agent setup providers --write to drop them: it also enables every provider whose CLI is installed, so set enabled: false again on any you do not want. Or delete the three entries by hand.
  • The three aliases in your Vibe config. phax agent setup mistral-vibe installs only the off and high aliases, and names the old ones it finds in ~/.vibe/config.toml without touching them. Delete their [[models]] blocks if you like; nothing uses them.

What's new

Mistral Vibe 3

phax reads a Vibe session only from what its turns hand over, and confines it per invocation, never through the person's configuration.

  • A phase record now shows a Vibe session's input, cached input and output tokens and its cost, summed over all its turns. phase record usage for mistral-vibe

    Now:

    {
      "$schema": "https://docs.phax.run/schemas/phase-record-manifest/0.30.0.json",
      …
      "usage": {
        "available": true,
        "usage": {
          "provider": "mistral-vibe",
          "inputTokens": 45575,
          "cachedInputTokens": 12800,
          "outputTokens": 291,
          "costUsd": 0.070545
        }
      }
    }
    Before
    {
      "$schema": "https://docs.phax.run/schemas/phase-record-manifest/0.26.0.json",
      …
      "usage": {
        "available": true,
        "usage": {
          "provider": "mistral-vibe",
          "inputTokens": 45575,
          "outputTokens": 291,
          "sessionCostUsd": 0.070545,
          "toolCallsAgreed": 4,
          "toolCallsRejected": 0,
          "toolCallsFailed": 0,
          "toolCallsSucceeded": 4
        }
      }
    }
  • A secure Vibe phase's security record now shows it as fully secured, with its agent commands enforced. <run>/<phase>/security.json for a secure mistral-vibe phase

    Now:

    {
      "version": 2,
      "mode": "secure",
      "provider": "mistral-vibe",
      …
      "downgraded": false,
      "marks": [],
      "agentCommands": [
        { "command": "git status", "source": "config", "explicit": true, "requiredByPlan": false, "enforcement": "prefix", "degraded": false }
      ],
      …
    }
    Before
    {
      "version": 2,
      "mode": "secure",
      "provider": "mistral-vibe",
      …
      "downgraded": true,
      "marks": ["partial-filesystem", "command-precision"],
      "agentCommands": [
        { "command": "git status", "source": "config", "explicit": true, "requiredByPlan": false, "enforcement": "none", "degraded": true }
      ],
      …
    }
  • A closed stop on Vibe now records that its first and closing turns ran with read-only tools. stop.turns in a closed mistral-vibe stop's security.json

    Now:

    "stop": {
      "cause": "held",
      "personWork": "outside-phax-enforcement",
      "turns": [
        { "turn": "closing-turn-01", "kind": "closing-turn", "narrowing": "read-only-tools", "agentCommands": [] }
      ]
    }
    Before
    "stop": {
      "cause": "held",
      "personWork": "outside-phax-enforcement",
      "turns": [
        { "turn": "closing-turn-01", "kind": "closing-turn", "narrowing": "none", "agentCommands": [] }
      ]
    }
  • If the installed Vibe is older than 3.0.3, phax now stops before any session starts and shows the command to upgrade. Vibe version-floor refusal

    Now:

    $ phax run docs/plans/2610281200-example-plan.md
    ✗ mistral-vibe 2.13.0 is installed; phax needs 3.0.3 or later.
      Upgrade it: uv tool upgrade mistral-vibe
  • The security guide now lists Mistral Vibe's filesystem jail as strong, and its MCP allowlist as unsupported, since phax turns Vibe's MCP off instead. docs/security.md provider capability table

    Now:

    | `mistral-vibe` | Strong          | None                             | Unsupported (MCP off) |
    Before
    | `mistral-vibe` | Partial         | None                             | Supported   |
  • A secure or read-only Vibe session is now refused while a custom accept-edits agent profile, or Vibe config under an extra writable root, could widen it, naming the file to rename or move. Vibe session refused for a config phax cannot vouch for

    Now:

    $ phax run --plan docs/plans/2611020915-invoice-export-plan.md
    Run: acme.invoice-export
    phax run failed: /home/ana/.vibe/agents/accept-edits.toml replaces Vibe's built-in accept-edits agent, which phax's secure and read-only Vibe sessions run under, so phax cannot vouch for their confinement. Rename it (for example to my-accept-edits.toml) and pick it with --agent my-accept-edits in your own sessions.
    $? = 11
    Before
    $ phax run --plan docs/plans/2611020915-invoice-export-plan.md
    Run: acme.invoice-export

Provider failures

The provider's own verdict on its session is the cause phax reports, first and in full; nothing the session read or wrote is ever taken for it.

  • When the writer session fails, phax names the provider's error first, with a retry hint, and the record refusal that follows says so. phax artifact decide (a failed writer session)

    Now:

    $ phax artifact decide docs/specs/2610271200-audio-export.md --verdicts verdicts.json
    ✗ claude-code session failed (network): API Error: Can't reach the API server — check your internet or DNS (ENOTFOUND)
      session: 8c1e4f2a-3b7d-4e1a-9f0c-2d6b5a7e9c31
      See ~/.phax/decide/2610271200-audio-export/02/agent-error.log and output.jsonl
      Retry once the network is back: phax artifact decide docs/specs/2610271200-audio-export.md --verdicts verdicts.json
    ⚠ decision record refused, following the failed session: this repo's visibility could not be determined — gh: error connecting to api.github.com (remedy: retry once the network is back)
    $? = 14
    Before
    $ phax artifact decide docs/specs/2610271200-audio-export.md --verdicts verdicts.json
    ⚠ decision record refused: this repo's visibility could not be determined, and transcripts are enabled with an in-repo destination (remedy: set records.destination.acknowledgedUnknownVisibility to true once you have confirmed this repo is private, or disable transcripts)
    ✗ claude exited with code 1
      See ~/.phax/decide/2610271200-audio-export/02/agent-error.log for the full agent output.
    $? = 5
  • A run whose agent loses the network pauses resumable and tells you to resume it, instead of failing. phax run (a network failure mid-phase)

    Now:

    $ phax run docs/plans/2610261000-audio-export-plan.md
    …
    ✗ claude-code session failed (network): API Error: Can't reach the API server — check your internet or DNS (ENOTFOUND)
      session: 3f9a0c1d-6e2b-4c7a-8d15-b0e4f7a29c68
      See ~/.phax/runs/notebook.audio-export/phase-02/agent-error.log and output.jsonl
      Run notebook.audio-export is paused at phase-02.
      Retry once the network is back: phax resume notebook.audio-export
    $? = 14
    Before
    $ phax run docs/plans/2610261000-audio-export-plan.md
    …
    ✗ claude exited with code 1
      See ~/.phax/runs/notebook.audio-export/phase-02/agent-error.log for the full agent output.
    $? = 5
  • A session that crashes after a tool merely printed "429" is reported as the crash it is, not as a rate limit. phax run (a crash after a tool printed "429")

    Now:

    $ phax run docs/plans/2610261000-audio-export-plan.md
    …
    ✗ claude-code session failed (provider-error): error_during_execution
      session: 3f9a0c1d-6e2b-4c7a-8d15-b0e4f7a29c68
      See ~/.phax/runs/notebook.audio-export/phase-02/agent-error.log and output.jsonl
    $? = 5
    Before
    $ phax run docs/plans/2610261000-audio-export-plan.md
    …
    ✗ Claude Code stopped: rate limit hit.
    $? = 8
  • The agent error log now opens with the failure kind, the provider's own message, the session id and the transcript to read. agent-error.log

    Now:

    failure: network
    provider: claude-code
    message: API Error: Can't reach the API server — check your internet or DNS (ENOTFOUND)
    session: 8c1e4f2a-3b7d-4e1a-9f0c-2d6b5a7e9c31
    transcript: ~/.phax/decide/2610271200-audio-export/02/output.jsonl
    
    claude --print --output-format stream-json --verbose --model claude-opus-5-5 --effort high …
    exit code: 1
    
    Before
    claude --print --output-format stream-json --verbose --model claude-opus-5-5 --effort high …
    exit code: 1
    
  • When the repository's visibility cannot be checked, phax quotes why gh failed and, on a network or login problem, tells you to retry rather than to acknowledge an unknown visibility. records visibility refusal

    Now:

    ⚠ decision record refused: this repo's visibility could not be determined — gh: error connecting to api.github.com (remedy: retry once the network is back)
    
    ⚠ decision record refused: this repo's visibility could not be determined — gh: To get started with GitHub CLI, please run:  gh auth login (remedy: run `gh auth login`, then retry)
    
    ⚠ decision record refused: this repo's visibility could not be determined — gh: none of the git remotes configured for this repository point to a known GitHub host (remedy: set records.destination.acknowledgedUnknownVisibility to true once you have confirmed this repo is private, or disable transcripts)
    Before
    ⚠ decision record refused: this repo's visibility could not be determined, and transcripts are enabled with an in-repo destination (remedy: set records.destination.acknowledgedUnknownVisibility to true once you have confirmed this repo is private, or disable transcripts)
  • A failed decision record says why the decide failed. decision record record.json

    Now:

    {
      "$schema": "https://docs.phax.run/schemas/decision-record-manifest/0.30.0.json",
      "kind": "decision",
      "decisionId": "2610271200-audio-export/02",
      "mode": "verdicts",
      "outcome": "failed",
      "failure": "network",
      "commit": null,
      …
    }
    Before
    {
      "$schema": "https://docs.phax.run/schemas/decision-record-manifest/0.28.0.json",
      "kind": "decision",
      "decisionId": "2610271200-audio-export/02",
      "mode": "verdicts",
      "outcome": "failed",
      "commit": null,
      …
    }
  • When a stop's closing turn loses the network, the stop's last close says so, and the run still waits for your review. review-stop.json

    Now:

    {
      "$schema": "https://docs.phax.run/schemas/review-stop/0.30.0.json",
      "run": "notebook.audio-export",
      "cause": "declared",
      "state": "awaiting_review",
      …
      "lastClose": {
        "result": "network",
        "at": "2026-10-27T14:02:11.408Z",
        "exitCode": 14,
        "detail": "API Error: Can't reach the API server — check your internet or DNS (ENOTFOUND)"
      },
      …
    }
    Before
    {
      "$schema": "https://docs.phax.run/schemas/review-stop/0.26.0.json",
      "run": "notebook.audio-export",
      "cause": "declared",
      "state": "awaiting_review",
      …
      "lastClose": {
        "result": "close-error",
        "at": "2026-10-27T14:02:11.408Z",
        "exitCode": 5,
        "step": "closing-turn",
        "detail": "claude exited with code 1"
      },
      …
    }
  • A command whose agent could not reach its provider exits with its own code, so a script can retry it. exit codes

    Now:

    8   Rate or usage limit hit (resumable)
    …
    13  Awaiting review at an interactive or held phase (resumable)
    14  Provider unreachable: network failure (resumable)
    Before
    8   Rate or usage limit hit (resumable)
    …
    13  Awaiting review at an interactive or held phase (resumable)