> 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.

# phax 0.30.0 \{#phax-0300}

Changes since 0.29.0: [v0.29.0...v0.30.0](https://github.com/lbdremy/phax/compare/v0.29.0...v0.30.0).

## Highlights \{#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 \{#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](https://github.com/lbdremy/phax/blob/v0.30.0/docs/agent-session-failures.md#a-failure-after-a-retry).
- **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 \{#whats-new}

### Mistral Vibe 3 \{#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:

  ```text
  {
    "$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
      }
    }
  }
  ```

  :::details Before

  ```text
  {
    "$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:

  ```text
  {
    "version": 2,
    "mode": "secure",
    "provider": "mistral-vibe",
    …
    "downgraded": false,
    "marks": [],
    "agentCommands": [
      { "command": "git status", "source": "config", "explicit": true, "requiredByPlan": false, "enforcement": "prefix", "degraded": false }
    ],
    …
  }
  ```

  :::details Before

  ```text
  {
    "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:

  ```text
  "stop": {
    "cause": "held",
    "personWork": "outside-phax-enforcement",
    "turns": [
      { "turn": "closing-turn-01", "kind": "closing-turn", "narrowing": "read-only-tools", "agentCommands": [] }
    ]
  }
  ```

  :::details Before

  ```text
  "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:

  ```text
  $ 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:

  ```text
  | `mistral-vibe` | Strong          | None                             | Unsupported (MCP off) |
  ```

  :::details Before

  ```text
  | `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:

  ```text
  $ 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
  ```

  :::details Before

  ```text
  $ phax run --plan docs/plans/2611020915-invoice-export-plan.md
  Run: acme.invoice-export
  ```

  :::

### Provider failures \{#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:

  ```text
  $ 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
  ```

  :::details Before

  ```text
  $ 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:

  ```text
  $ 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
  ```

  :::details Before

  ```text
  $ 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:

  ```text
  $ 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
  ```

  :::details Before

  ```text
  $ 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:

  ```text
  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

  ```

  :::details Before

  ```text
  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:

  ```text
  ⚠ 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)
  ```

  :::details Before

  ```text
  ⚠ 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:

  ```text
  {
    "$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,
    …
  }
  ```

  :::details Before

  ```text
  {
    "$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:

  ```text
  {
    "$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)"
    },
    …
  }
  ```

  :::details Before

  ```text
  {
    "$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:

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

  :::details Before

  ```text
  8   Rate or usage limit hit (resumable)
  …
  13  Awaiting review at an interactive or held phase (resumable)
  ```

  :::
