Testing and internals

Testing

pnpm test               # unit + integration — fast, no network, no provider CLIs
pnpm test:e2e:real      # opt-in real E2E — drives the installed provider CLIs, costs tokens

The E2E suite skips automatically unless PHAX_E2E_RUN=1 is set, so it never runs by accident. It runs one real-flow suite per provider (Claude Code, Mistral Vibe, Codex), each forcing its provider with --provider-priority and gated on that provider's CLI being installed — so only the providers you have set up actually run. See docs/e2e-testing.md for prerequisites, isolation model, and how to read failure artifacts.

State Machine

phax is implemented as an explicit hierarchical state machine. Every signal (gate result, rate limit, agent completion, archive request) is a typed PhaxEvent. The pure reducer returns a Disposition — Handled, Ignored, Stale, Rejected, or Unexpected — plus optional side-effect commands. The single dispatch() entry point is the only writer to status.json and run-status.json.

See docs/state-machine.md for:

  • Mermaid diagrams of the run and phase hierarchies
  • The full event-disposition matrix
  • The event and command vocabularies
  • A worked example of adding a new signal

CLI specification (phax.usage.kdl)

phax.usage.kdl is a machine-readable CLI contract generated from the Commander.js program in src/cli/. It is a derived artifact — Commander is the source of truth — and must be regenerated after any change to a command, flag, or argument:

pnpm gen:usage-spec

The integration gate tests/integration/usageSpecDrift.test.ts asserts the committed file is byte-identical to the generator output, so a CLI change without regenerating the spec will fail the gate. Downstream tooling (phax --usage, shell completions, docs/cli/reference.md, and external consumers such as a generated client library or editor integration) all derive from this spec.