# Spur GLOBAL configuration — `~/.config/spur/config.yaml`
#
# Seeded here by `spur init` on first run (never overwritten once it exists —
# you own this file). It is the DEFAULT layer: every project's
# `.spur/config.yaml` is merged OVER it, so a project config only needs to carry
# its delta.
#
# Merge contract (A4 / config 1.2):
#   - Both layers are validated by the SAME schema; each legitimately carries a
#     subset.
#   - The merge is total and automatic: project layer wins over global, key by
#     key.
#   - Validation runs ONCE, on the merged result — a partial entry here is a
#     legal merge fragment, never a legal config on its own.
#   - `agent.executors` merges by `name`: a project restating `- name: omp` with
#     a different `model` overrides just that field and keeps the rest from
#     here.
#
# Only machine-wide keys belong in this file. Project-shaped keys — `name`,
# `bootstrap`, `rules`, `redaction`, `tasks`, `features` — resolve against a
# project's own folder structure and must stay in `.spur/config.yaml`. The agent
# roster is not a config key at all any more: it is the project-layer `agent.fleet`
# section of `.spur/config.yaml` (ADR-116 carrier moved by 0858; it replaced
# `<projectPath>/.spur/fleet.json`). The retired team roster key is gone: a leftover
# block fails the load and names `agent.fleet` as the replacement (0857), a leftover
# `fleet.json` fails it too (0858), and the `spur team` noun was removed at the G64
# cutover.

$schema: "@gobing-ai/spur/schemas/spur-config.schema.json"

# Inert label (task 0649 R6 ruling): nothing branches on `version` today.
# Reunified at "1.2" across every shipped artifact (init stamp, JSON Schema).
version: "1.2"

agent:
    # `default` is the role used for `--agent auto` when the caller declares
    # nothing (task 0542 R2 — the value domain moved from executor names to
    # roles). A role id selects its tier's cheapest eligible executor; a
    # configured executor name still resolves during the transition with a
    # one-time warning (shim agent-default-executor); any other value fails
    # naming both accepted sets.
    default: coder

    # Role → tier map. Resolution: role → tier → cheapest USABLE executor whose
    # tier >= the role's tier, climbing the ladder across gaps. Keep at least
    # one usable executor at or above each rung or that role fails loudly at
    # dispatch. Stage floors (stage-registry model_policy): changelog=cheap;
    # implement/test/wrap/review/refine/brainstorm=standard;
    # verify/dogfood=capable-1; plan=capable-2. A stage's fallback ladder (auth
    # / resource-exhaustion / gate-fail) only advances when a higher rung has a
    # usable executor — an empty rung turns a quota failure into a hang.
    #
    # The vocabulary is CLOSED (task 0536): re-tier or re-stage these four,
    # never invent a fifth — a config naming any other key fails at load naming
    # the accepted four. A project may override per-field in its own
    # `.spur/config.yaml`; restate only what you change.
    #
    # ADR-078: THIS TABLE IS THE SSOT. `DEFAULT_AGENT_ROLES` in
    # `packages/config/src/index.ts` is a byte-identical fallback that applies
    # only when no config layer supplies `agent.roles` at all (the CF-safe core
    # must resolve roles with no filesystem). The two must stay byte-identical —
    # a fallback that differed would turn a missing config file into a silent
    # behavior change. `plugins/sp/tests/roles.test.ts` R9 gates all three
    # projections (this table, the constant, and
    # `plugins/sp/references/roles.md`).
    roles:
        scribe:
            tier: cheap
            stages: [changelog]
        coder:
            tier: standard
            stages: [implement, test, wrap]
        reviewer:
            tier: capable-1
            stages: [verify, review, dogfood]
        planner:
            tier: capable-2
            stages: [plan, refine, brainstorm]

    # Canonical coding-agent ids (ts-ai-runner DISPLAY_ORDER, 0.4.8+): claude,
    # codex, gemini, pi, omp, opencode, antigravity-cli, openclaw, hermes, grok
    # Grok auth: non-empty XAI_API_KEY and/or ~/.grok/auth.json (no CLI
    # auth-status verb).

    # Named executor profiles (ADR-033 / 0343). Each pairs a canonical agent
    # with an explicit model override and an explicit capability tier. `model`
    # is pinned on every entry: omitting it defers to the agent CLI's own
    # default, which drifts with CLI upgrades and account state — pin what your
    # CLI would resolve today (`grok models`, omp config.yml modelRoles.default,
    # ~/.codex/config.toml, ~/.claude.json). The ids below are working examples,
    # not vendor defaults — re-pin for your accounts.
    #
    # Pairing philosophy (operator guidance, 2026-08): a `native` pair — a
    # vendor's model served by that vendor's own CLI (gpt-5.6-* via codex,
    # claude-*-5 via claude, grok-4.6 via grok, gemini-3.x via agy) —
    # consistently outperforms the same model routed through a third-party CLI,
    # and belongs at the capable rungs. `portable` models (glm-5.x,
    # deepseek-v4-*, …) are provider-agnostic and best carried by pi at
    # the cheap/standard rungs. The ladder below mixes both classes on purpose;
    # measure pairings with the history plane before promoting a portable model
    # into a capable rung. Live tiers: cheap | standard | capable-1 | capable-2
    # | capable-3 (1=low quality within capable, 3=high). DECLARE tier —
    # inference never invents capable-2/3. Stage routing starts on the cheapest
    # executor whose tier >= min_tier; same exact tier ties break by array
    # order. Legacy bare `capable` is accepted at runtime as synonym for
    # capable-1.
    #
    # Because this is the global layer, a project that wants one different model
    # writes only `- name: pi-dsv4-flash` + `model: …` — the agent and tier come
    # from here.
    executors:
        # cheap rung is optional: with none declared, scribe-role work starts
        # one rung up at the cheapest standard executor. Add one when
        # transcript-heavy mechanical commands (changelog/gitmsg/handover) get
        # noisy on cost.
        # - name: minimax
        #   agent: pi
        #   model: minimax/MiniMax-M3
        #   tier: cheap
        # Standard rung. An `agent:` here is a live DISPATCH target, not a label:
        # role routing (`agent.default: coder`, `--agent auto`, every workflow step
        # declaring `agent: auto`) lands on the cheapest eligible standard executor,
        # so listing an agent makes Spur delegate work to it. Keep this rung on an
        # agent the supported install targets actually carry.
        - name: pi-dsv4-flash
          agent: pi
          model: opencode/deepseek-v4-flash
          tier: standard
        - name: pi
          agent: pi
          model: "k3"
          tier: capable-1
        - name: grok
          agent: grok
          model: grok-4.6
          tier: capable-2
        - name: claude
          agent: claude
          model: k3
          tier: capable-3
        - name: codex
          agent: codex
          model: gpt-5.6
          tier: capable-3

# Workflow layers (ADR-113): the project layer is always `<cwd>/.spur/workflows`
# and the shared layer is the installed package's own `config/workflows`, so no
# config entry is needed to discover the shipped workflows. A project registers
# extra workflow folders by concatenating them here (loader merge):
workflows:
    paths: []
