Skip to content

feat(cli): per-run --provider/--model override for archon workflow run (and HTTP API parity) #1433

@Wirasm

Description

@Wirasm

Problem

There's no way today to override a workflow's provider/model for a single invocation. If a workflow YAML declares provider: claude, model: sonnet, every run uses Sonnet — even when the user wants to:

  • run cheaper on Pi/Minimax for a one-off cost experiment,
  • run on Opus instead of Sonnet to compare quality on a hard PR,
  • fall back to Codex when Claude is rate-limited,
  • isolate a model-specific bug.

Today's escape hatch is editing the YAML before each run and reverting after. That doesn't survive parallel runs and clutters git status.

Proposed primitives

CLI

archon workflow run <name> [--provider <p>] [--model <m>] "<args>"
  • --provider <p> alone — uses that provider's default model (e.g. --provider claudesonnet per .archon/config.yaml).
  • --model <m> alone — interpreted under the resolved provider (e.g. --model haiku on a Claude workflow). Loader rejects incompatible combinations (haiku on a Codex workflow).
  • Both — full explicit override.
  • Neither — current behavior (no behavior change for existing users).

HTTP API parity

POST /api/workflows/{name}/runs
{
  ...,
  "provider": "pi",
  "model": "minimax/MiniMax-M2.7"
}

Same semantics as the CLI flags. Keeps Web UI parity easy to add later.

Resolution pipeline

CLI flag (or HTTP body field) set?
  YES → that value applies to every AI node in the run, period.
        Workflow-level AND per-node provider/model in YAML are IGNORED.
        Provider-specific fields that don't apply on the new provider
        (effort/thinking/betas/sandbox/hooks/mcp/skills on Pi, etc.)
        emit the existing loader warnings — same behavior as today.

  NO  → existing pipeline:
        per-node YAML > workflow YAML > .archon/config.yaml > SDK default

That's it. One pipeline, one priority order. No --force- flavor. The flag name (--provider, --model) already conveys force-semantics; a second flavor would introduce a bikeshed without solving a real problem.

The deliberate tradeoff

Per-node provider: / model: fields in YAML are ignored when the CLI/API override is set. Workflow authors lose the ability to force a specific model for a specific node against an explicit override.

Cost: can't express "this node MUST be Opus" via YAML and still respect a user-issued override.

Benefit: --provider pi actually does what it says on the tin. The runtime user issued an explicit override; honoring it is the principle of least surprise.

If a real use case for "lock a node against override" emerges, it can be added later as a single schema field (provider_locked: true on a node) and one priority-rule branch. Keeping it out of v1 to avoid premature optimization.

Validation

Single reusable function in the workflow loader:

validateProviderModelCombo(provider: string, model: string, workflow: WorkflowDefinition): ValidationResult

Called after CLI/API overrides are merged into the effective config. Surfaces the same errors users see today on workflow load:

  • Claude models: sonnet | opus | haiku | claude-* | inherit
  • Codex models: any non-Claude-alias ID
  • Pi models: <vendor>/<model> format (e.g. anthropic/claude-haiku-4-5, openrouter/qwen/qwen3-coder, minimax/MiniMax-M2.7)

Compatibility with SDK-specific options (e.g. betas: ['context-1m-2025-08-07'] required for opus[1m]) follows the existing loader rules — CLI override of a model that requires a beta the workflow doesn't declare is a hard error at run start, not at runtime.

Logging

Extend the existing workflow_provider_resolved event:

{
  "msg": "workflow_provider_resolved",
  "workflowName": "...",
  "provider": "pi",
  "model": "minimax/MiniMax-M2.7",
- "providerSource": "workflow definition",
+ "providerSource": "cli" | "http" | "workflow" | "config" | "sdk",
  ...
}

Same shape; just one more enum value. Lets users answer "why did this run resolve to that model?" from logs alone.

Edge cases

Case Behavior
Workflow has worktree.enabled: false and CLI passes --branch Existing error; unchanged
Workflow has per-node effort:/thinking: (Claude-only) and CLI overrides to --provider pi Loader emits the existing "field ignored on this provider" warnings; run proceeds
--model opus[1m] and the workflow declares no betas: Hard error at run start (existing rule, just applied to CLI-resolved values)
.archon/config.yaml has assistants.claude.model: opus and CLI passes --provider codex --model gpt-5.3 CLI wins. Config never reads.
CLI passes only --provider codex for a workflow that hardcodes Claude features (betas, sandbox) Loader warns the Claude-specific fields are ignored on Codex; run proceeds

Implementation sketch

Rough scope, ~150-200 LOC + tests:

  • packages/cli/src/cli.ts (workflow-run command) — add --provider / --model flag definitions; pass through to executor invocation.
  • packages/workflows/src/executor.ts — accept runtimeOverrides: { provider?: string, model?: string }; apply during resolution before workflow-level defaults; log new providerSource.
  • packages/workflows/src/loader.ts — extract the existing provider/model validator into a reusable function (likely already mostly factored out); call it on the merged config.
  • packages/server/src/routes/api.ts — accept provider / model in the POST runs body; pass through to the same executor entry point as CLI.
  • Tests:
    • unit on the resolver: each priority level + override combinations
    • CLI integration: archon workflow run --provider pi --model minimax/MiniMax-M2.7 e2e-claude-smoke "" and assert the smoke ran on Pi via session log (matches the pattern in e2e-minimax-smoke.yaml).
    • HTTP integration: POST with override body fields; verify resolution.

Open question

Should CLI/HTTP override beat .archon/config.yaml's assistants.* block too? My read: yes, same principle (runtime intent > config). The pipeline above reflects this. Calling it out so it's explicit before someone implements.

Out of scope (future)

  • provider_locked: true per-node opt-out.
  • Environment-variable equivalents (ARCHON_PROVIDER_OVERRIDE=...) for shell scripting.
  • Web UI run-page picker for provider/model.

Each is a small, independent follow-up if real use cases show up.

Related

  • The e2e-minimax-smoke workflow already exercises the resolution pipeline end-to-end, so a CLI override test can lean on the same session-log assertion pattern.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Medium priority - Backlog, when time permitsarea: cliCLI commands and interfacearea: serverHTTP server (packages/server) - API routes, SSE, adaptersarea: workflowsWorkflow engineeffort/mediumFew files, one domain or module, some coordination neededfeatureNew functionality (planned)

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions