Skip to content

Commit 327df15

Browse files
committed
feat(routing): migrate agents to tier aliases for model-agnostic routing
Agents now use `opus`/`sonnet`/`haiku` tier aliases instead of pinned snapshots (`claude-opus-4-6`, `claude-sonnet-4-6`, `claude-haiku-4-5-*`). Claude Code resolves each tier to the current model at runtime, so the plugin stays compatible with new model releases (including Opus 4.7) without plugin-side changes. Each agent's tier can be overridden via SPEC_MODEL_* env vars. New scripts/lib/detect-backend.sh prints a one-time optimization notice when ANTHROPIC_BASE_URL points at a non-Anthropic router. New docs/advanced/model-routing.md documents the full routing model. Bumps plugin version 5.0.3 -> 5.1.0.
1 parent f090e1d commit 327df15

43 files changed

Lines changed: 1603 additions & 85 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.claude-plugin/plugin.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "spec-driven",
3-
"version": "5.0.3",
3+
"version": "5.1.0",
44
"description": "Spec-driven development workflow with structured requirements, design, and task phases",
55
"author": {
66
"name": "habib0x"

.claude/specs/model-agnostic-routing/design.md

Lines changed: 741 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 246 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,246 @@
1+
# Requirements: Model-Agnostic Routing
2+
3+
## Overview
4+
5+
Make the spec-driven plugin work correctly on any Claude-compatible backend (direct Anthropic API, Claude Code Router, LiteLLM, opencode, etc.) by removing snapshot-pinned model IDs from agent frontmatter, supporting tier aliases with env-var overrides, and showing a one-time optimization notice when a non-Anthropic backend is detected.
6+
7+
## User Roles
8+
9+
| Role | Description |
10+
|------|-------------|
11+
| Plugin User (Direct) | Developer using the plugin with the standard Anthropic API |
12+
| Plugin User (Router) | Developer using the plugin through a third-party router or proxy (LiteLLM, CCR, opencode, etc.) |
13+
| Plugin Author | Maintainer of the spec-driven plugin who needs to update model references on new Anthropic releases |
14+
15+
## User Stories
16+
17+
### US-1: Forward-Compatible Model References
18+
19+
**As a** Plugin Author
20+
**I want** agent model references to use tier aliases instead of snapshot-pinned IDs
21+
**So that** the plugin works on current and future Anthropic model generations without manual patching each release
22+
23+
#### Acceptance Criteria (EARS)
24+
25+
1. WHEN the plugin is loaded by Claude Code
26+
THE SYSTEM SHALL resolve agent models from tier alias strings (`opus`, `sonnet`, `haiku`) in the `model:` field of each agent's YAML frontmatter, not from snapshot-pinned identifiers like `claude-opus-4-6` or `claude-haiku-4-5-20251001`
27+
28+
WARNING: `agents/spec-implementer.md` was involved in BUG-1: Anti-stub sweep. Ensure anti-stub enforcement text in the agent's system prompt body remains intact after editing the frontmatter.
29+
30+
WARNING: `agents/spec-tester.md` was involved in BUG-1: Anti-stub sweep. Ensure anti-stub enforcement text in the agent's system prompt body remains intact after editing the frontmatter.
31+
32+
WARNING: `agents/spec-reviewer.md` was involved in BUG-1: Anti-stub sweep. Ensure anti-stub enforcement text in the agent's system prompt body remains intact after editing the frontmatter.
33+
34+
2. WHEN any of the 11 agent files in `agents/*.md` is inspected
35+
THE SYSTEM SHALL contain exactly one of the following values in the `model:` YAML frontmatter field: `opus`, `sonnet`, or `haiku` -- with no version number, snapshot suffix, or `claude-` prefix
36+
37+
3. WHEN the plugin is used on a newer Anthropic model generation (e.g., Opus 4.7, Sonnet 5.0)
38+
THE SYSTEM SHALL route agents to the correct model tier without any file edits, because the frontmatter contains only the tier alias
39+
40+
4. THE SYSTEM SHALL preserve the existing tier assignments: `spec-planner` and `spec-reviewer` on `opus`; `spec-tasker`, `spec-validator`, `spec-implementer`, `spec-tester`, `spec-acceptor`, `spec-consultant`, `spec-documenter`, and `spec-scanner` on `sonnet`; `spec-debugger` on `haiku`
41+
42+
---
43+
44+
### US-2: Non-Anthropic Backend Compatibility
45+
46+
**As a** Plugin User (Router)
47+
**I want** the plugin to work when I run Claude Code against a non-Anthropic backend
48+
**So that** I can use the spec-driven workflow with any Claude-compatible router or proxy
49+
50+
#### Acceptance Criteria (EARS)
51+
52+
1. WHEN a user runs a shell script (`spec-exec.sh`, `spec-loop.sh`, `spec-complete.sh`, `spec-accept.sh`, `spec-docs.sh`, `spec-release.sh`, `spec-verify.sh`, `spec-retro.sh`) and the environment variable `ANTHROPIC_BASE_URL` is unset
53+
THE SYSTEM SHALL treat the backend as Anthropic (direct API) and not display any optimization notice
54+
55+
2. WHEN a user runs a shell script and `ANTHROPIC_BASE_URL` is set to a value containing the substring `anthropic.com` (e.g., `https://api.anthropic.com/v1`)
56+
THE SYSTEM SHALL treat the backend as Anthropic and not display any optimization notice
57+
58+
3. WHEN a user runs a shell script and `ANTHROPIC_BASE_URL` is set to a value that does NOT contain the substring `anthropic.com` (e.g., `http://localhost:4000`, `https://my-litellm.example.com/v1`)
59+
THE SYSTEM SHALL treat the backend as non-Anthropic and display the optimization notice (subject to `SPEC_QUIET` suppression per US-3)
60+
61+
4. THE SYSTEM SHALL NOT make any network calls, DNS lookups, or HTTP requests as part of backend detection -- detection is purely string-based on the `ANTHROPIC_BASE_URL` environment variable
62+
63+
5. WHEN the plugin runs on direct Anthropic API (no `ANTHROPIC_BASE_URL` set, or set to an `anthropic.com` URL)
64+
THE SYSTEM SHALL behave identically to v5.0.3 in all functional aspects (backward compatibility)
65+
66+
WARNING: `scripts/spec-exec.sh` was involved in BUG-1: Anti-stub sweep. Ensure anti-stub enforcement text in the prompt template remains intact after adding backend detection sourcing.
67+
68+
WARNING: `scripts/spec-loop.sh` was involved in BUG-1: Anti-stub sweep. Ensure anti-stub enforcement text in the prompt template remains intact after adding backend detection sourcing.
69+
70+
---
71+
72+
### US-3: Non-Anthropic Backend Guidance Notice
73+
74+
**As a** Plugin User (Router)
75+
**I want** to receive guidance about optimizing model routing when I'm on a non-Anthropic backend
76+
**So that** I know which env vars to set and where to find configuration documentation
77+
78+
#### Acceptance Criteria (EARS)
79+
80+
1. WHEN a non-Anthropic backend is detected (per US-2 AC-3) and `SPEC_QUIET` is not set to `1`
81+
THE SYSTEM SHALL print an optimization notice to stderr containing ALL of the following:
82+
- A header line: `[spec-driven] Non-Anthropic backend detected`
83+
- The detected URL (value of `ANTHROPIC_BASE_URL`)
84+
- The complete list of 11 per-agent env var names: `SPEC_MODEL_PLANNER`, `SPEC_MODEL_IMPLEMENTER`, `SPEC_MODEL_DEBUGGER`, `SPEC_MODEL_TASKER`, `SPEC_MODEL_VALIDATOR`, `SPEC_MODEL_REVIEWER`, `SPEC_MODEL_TESTER`, `SPEC_MODEL_SCANNER`, `SPEC_MODEL_ACCEPTOR`, `SPEC_MODEL_DOCUMENTER`, `SPEC_MODEL_CONSULTANT`
85+
- A placeholder example for at least one env var (e.g., `export SPEC_MODEL_PLANNER=deepseek-v3`)
86+
- A pointer to the documentation file: `docs/advanced/model-routing.md`
87+
- Instructions to suppress the notice: `export SPEC_QUIET=1`
88+
89+
2. THE SYSTEM SHALL print the optimization notice to stderr (file descriptor 2), not stdout (file descriptor 1), so that it does not pollute output that tooling may parse
90+
91+
3. THE SYSTEM SHALL print the optimization notice at most once per script invocation, even if multiple functions or code paths re-source the detection helper
92+
93+
4. WHEN `SPEC_QUIET` is set to `1`
94+
THE SYSTEM SHALL NOT print the optimization notice, regardless of backend detection result
95+
96+
5. WHEN `SPEC_QUIET` is set to any value other than `1` (e.g., `0`, `true`, empty string) or is unset
97+
THE SYSTEM SHALL NOT suppress the notice (only the exact value `1` suppresses)
98+
99+
---
100+
101+
### US-4: Per-Agent Model Override via Environment Variables
102+
103+
**As a** Plugin User (Router)
104+
**I want** to override the model used for each agent via environment variables
105+
**So that** I can map each agent tier to the specific model my router exposes
106+
107+
#### Acceptance Criteria (EARS)
108+
109+
1. WHEN the `/spec` command spawns the `spec-planner` agent via the Task tool and the environment variable `SPEC_MODEL_PLANNER` is set to a non-empty string
110+
THE SYSTEM SHALL pass the value of `SPEC_MODEL_PLANNER` as the `model:` parameter to the Task tool invocation, overriding the frontmatter default
111+
112+
WARNING: `commands/spec.md` was involved in BUG-2: Validate-fix loop routing. Ensure the validate-fix loop in Step 4.5 still routes vague-requirement issues to `spec-planner` and task-traceability issues to `spec-tasker` after adding env-var override instructions.
113+
114+
2. WHEN any `SPEC_MODEL_*` environment variable is set for a given agent
115+
THE SYSTEM SHALL use that value as the model identifier passed to the Task tool, regardless of what tier alias is in the agent's frontmatter
116+
117+
3. WHEN no `SPEC_MODEL_*` environment variable is set for a given agent
118+
THE SYSTEM SHALL use the tier alias from the agent's YAML frontmatter (e.g., `opus`, `sonnet`, `haiku`) as the model
119+
120+
4. THE SYSTEM SHALL support the following env-var-to-agent mapping (one variable per agent, all 11 covered):
121+
122+
| Env Var | Agent | Default Tier |
123+
|---------|-------|-------------|
124+
| `SPEC_MODEL_PLANNER` | spec-planner | opus |
125+
| `SPEC_MODEL_TASKER` | spec-tasker | sonnet |
126+
| `SPEC_MODEL_VALIDATOR` | spec-validator | sonnet |
127+
| `SPEC_MODEL_IMPLEMENTER` | spec-implementer | sonnet |
128+
| `SPEC_MODEL_TESTER` | spec-tester | sonnet |
129+
| `SPEC_MODEL_REVIEWER` | spec-reviewer | opus |
130+
| `SPEC_MODEL_DEBUGGER` | spec-debugger | haiku |
131+
| `SPEC_MODEL_SCANNER` | spec-scanner | sonnet |
132+
| `SPEC_MODEL_ACCEPTOR` | spec-acceptor | sonnet |
133+
| `SPEC_MODEL_DOCUMENTER` | spec-documenter | sonnet |
134+
| `SPEC_MODEL_CONSULTANT` | spec-consultant | sonnet |
135+
136+
5. WHEN a command that spawns agents (at minimum: `commands/spec.md`, `commands/spec-brainstorm.md`, `commands/spec-refine.md`, `commands/spec-tasks.md`, `commands/spec-validate.md`) invokes the Task tool
137+
THE SYSTEM SHALL include instructions in the command prompt to check the relevant `SPEC_MODEL_*` env var and pass it as the `model:` parameter when set
138+
139+
6. THE SYSTEM SHALL NOT attempt env-var interpolation inside YAML frontmatter -- the override mechanism lives entirely in the spawning command's prompt logic
140+
141+
---
142+
143+
### US-5: Accurate Model-Agnostic Documentation
144+
145+
**As a** Plugin User (Direct or Router)
146+
**I want** documentation to describe model routing in terms of tiers rather than pinned version numbers
147+
**So that** the docs remain accurate regardless of which model generation or backend I'm using
148+
149+
#### Acceptance Criteria (EARS)
150+
151+
1. WHEN a user reads `CLAUDE.md`
152+
THE SYSTEM SHALL display a "Model Routing" table that uses tier names (`opus` / `sonnet` / `haiku`) and tier descriptions (e.g., "Deep reasoning for edge cases and architecture") instead of version-specific identifiers like "Opus 4.6" or "Sonnet 4.6"
153+
154+
2. WHEN a user reads `commands/spec.md`
155+
THE SYSTEM SHALL display a "Model Routing" table that uses tier names and descriptions without version-specific identifiers
156+
157+
3. WHEN a user reads any of the 11 agent files in `agents/*.md`
158+
THE SYSTEM SHALL find `description:` text that references tiers (e.g., "runs on the opus tier for deep reasoning") rather than specific versions (e.g., "runs on Opus 4.6")
159+
160+
4. WHEN a user reads documentation under `docs/agents/`, `docs/commands/index.md`, `docs/commands/spec.md`, `docs/workflow/`, `docs/getting-started/`, or `docs/advanced/extending.md`
161+
THE SYSTEM SHALL find tier-based language instead of version-pinned references for all model mentions
162+
163+
5. WHEN a user reads `docs/advanced/model-routing.md`
164+
THE SYSTEM SHALL find a guide containing ALL of:
165+
- Explanation of the three-tier system (opus/sonnet/haiku) and which agents use which tier
166+
- Full list of `SPEC_MODEL_*` env vars with descriptions
167+
- Explanation of override precedence (env var > frontmatter tier alias)
168+
- Description of backend detection behavior (`ANTHROPIC_BASE_URL` logic)
169+
- Instructions for suppressing the notice (`SPEC_QUIET=1`)
170+
- Example router configurations for at least three routers: Claude Code Router (CCR), LiteLLM, and opencode
171+
- Example model mappings using hypothetical OSS models (e.g., DeepSeek-V3 for opus tier, Qwen3-Coder for sonnet tier, a small 7B-8B model for haiku tier)
172+
173+
6. THE SYSTEM SHALL NOT modify any files under `.claude/specs/spec-intelligence-layer/` or `.claude/specs/spec-plugin-v3-enhancements/` (archived specs are out of scope)
174+
175+
---
176+
177+
### US-6: Version Bump
178+
179+
**As a** Plugin Author
180+
**I want** the plugin version to be bumped from `5.0.3` to `5.1.0`
181+
**So that** the model-agnostic routing change is identifiable as a minor (feature) release
182+
183+
#### Acceptance Criteria (EARS)
184+
185+
1. WHEN `.claude-plugin/plugin.json` is inspected
186+
THE SYSTEM SHALL show `"version": "5.1.0"`
187+
188+
2. WHEN `skills/spec-workflow/SKILL.md` is inspected
189+
THE SYSTEM SHALL show `version: 5.1.0` in the YAML frontmatter
190+
191+
---
192+
193+
## Non-Functional Requirements
194+
195+
### NFR-1: Banner Output Isolation
196+
197+
THE SYSTEM SHALL print the optimization notice exclusively to stderr (file descriptor 2). No banner content shall appear on stdout under any circumstances.
198+
199+
### NFR-2: Banner Idempotency
200+
201+
THE SYSTEM SHALL print the optimization notice at most once per shell script invocation. If `detect-backend.sh` is sourced multiple times within a single script (e.g., sourced by both the main script and a nested library), the notice shall only print on the first sourcing.
202+
203+
### NFR-3: No Network Calls for Detection
204+
205+
THE SYSTEM SHALL NOT make any network calls (HTTP requests, DNS lookups, TCP connections) as part of the backend detection logic. Detection is a pure string comparison on the `ANTHROPIC_BASE_URL` environment variable.
206+
207+
### NFR-4: Backward Compatibility
208+
209+
WHEN no `SPEC_MODEL_*` environment variables are set and `ANTHROPIC_BASE_URL` is unset or points to `anthropic.com`
210+
THE SYSTEM SHALL behave identically to v5.0.3 for all functional workflows. The only difference is the tier alias string in frontmatter instead of the snapshot ID, which Claude Code resolves equivalently.
211+
212+
### NFR-5: Shell Script Convention Compliance
213+
214+
THE SYSTEM SHALL follow the existing `set -e` convention (PAT-3) in the new `scripts/lib/detect-backend.sh` helper. The helper shall NOT use `set -euo pipefail` or any convention that deviates from the existing scripts.
215+
216+
### NFR-6: Minimal File Scope
217+
218+
THE SYSTEM SHALL limit file modifications to the files enumerated in design.md. In particular, the following files shall NOT be modified:
219+
- `.claude/specs/spec-intelligence-layer/*` (archived spec)
220+
- `.claude/specs/spec-plugin-v3-enhancements/*` (archived spec)
221+
- `commands/spec-team.sync-conflict-20260407-015530-WTRWCNZ.md` (orphaned file)
222+
223+
---
224+
225+
## Out of Scope
226+
227+
- Runtime model validation (checking if the model string actually resolves on the backend)
228+
- Automatic model mapping (guessing which OSS model corresponds to which tier)
229+
- Configuration files beyond env vars (no `.spec-driven.yml` or similar config file)
230+
- Changes to the Task tool itself or Claude Code's model resolution internals
231+
- Modifying shell scripts to pass `--model` flags (scripts inherit the session model; only the banner is new behavior for scripts)
232+
- Auto-detection of router type (LiteLLM vs. CCR vs. opencode) -- the notice is generic
233+
- Changes to archived spec files under `.claude/specs/`
234+
235+
## Assumptions
236+
237+
1. Claude Code's Task tool accepts `model:` as a parameter that overrides the agent's frontmatter `model:` field. This is confirmed by the existing `commands/spec.md` agent-spawning pattern.
238+
2. Claude Code resolves tier alias strings (`opus`, `sonnet`, `haiku`) in agent frontmatter to the latest available model in that tier. This is the standard Claude Code plugin behavior.
239+
3. The `ANTHROPIC_BASE_URL` environment variable is the standard way users configure non-Anthropic backends. Other variables (like `OPENAI_BASE_URL`) are not relevant to this plugin's detection.
240+
4. Shell scripts inherit the session model from the `claude` CLI invocation and do not need to pass `--model` explicitly. The env-var override mechanism is only needed at the command/Task-tool level, not in scripts.
241+
242+
## Detected Gaps (Informational)
243+
244+
- `spec-scanner` agent: not registered in `plugin.json` or `CLAUDE.md` routing table (confidence: high)
245+
- `spec-debugger` agent: not registered in `plugin.json` (confidence: high)
246+
- `spec-complete` command: no docs page at `docs/commands/spec-complete.md` (confidence: high)

0 commit comments

Comments
 (0)