fix(#2072): thread resolved model into routed-agent spawns (assumptions-analyzer, code-reviewer, code-fixer)
model_overrides / models.<phaseType> were silently inert for gsd-assumptions-analyzer,
gsd-code-reviewer, and gsd-code-fixer on Claude Code: resolveModelInternal honors them,
but the workflows spawned these agents with no model= param, so the resolved value
never reached the Agent tool and the agents inherited the session model — no warning.
Fix — thread each agent's resolved model at every spawn site (the established
plan-phase pattern; the architecture-consistent Claude mechanism, since 13 other
agents already thread their model):
- discuss-phase-assumptions.md: `resolve-model gsd-assumptions-analyzer --raw`
→ ANALYZER_MODEL, threaded.
- code-review.md + code-review-fix.md (re-review): `resolve-model gsd-code-reviewer --raw`
→ REVIEWER_MODEL, threaded.
- code-review-fix.md (both fixer spawns): `resolve-model gsd-code-fixer --raw`
→ FIXER_MODEL, threaded (same silently-inert bug, same file — folded in per review).
- quick.md review step: was reusing `{executor_model}` for gsd-code-reviewer (so the
reviewer's own override was ignored); init.quick now resolves `reviewer_model`
(gsd-code-reviewer) and the spawn threads it.
resolve-model --raw returns the bare model string (resolve-execution --raw would
return effort — wrong). The resolver maps these agents to phaseType discuss /
verification / execution, so models.<phaseType> apply too.
Scope: the three agents reachable from the two issue-named workflows + quick.md. The
wider systemic class (other agents in UNTOUCHED workflows with the same pattern) stays
documented on the issue for a maintainer-scoped structural decision (thread-at-source
vs embed-at-install like #2256), not widened here.
Docs: the stale "discuss — reserved, no subagent today" model-profile tables now list
gsd-assumptions-analyzer and the verification row includes gsd-code-reviewer, across
the English docs, the shipped gsd-core/references/model-profiles.md reference, and the
ja-JP / zh-CN / ko-KR / pt-BR locale mirrors.
Tests:
- tests/model-resolver.test.cjs: #2072 acceptance — model_overrides and
models.discuss/verification/execution resolve for all three agents.
- tests/model-routing-spawn-threading.test.cjs: every spawn of the three agents threads
a resolved model (fails pre-fix); a header-precise parity guard fails the suite if a
new un-threaded spawn of any of them regresses.
All 16 golden-install-parity fixtures + the workflow size baseline regenerated for the
changed shipped files (4 workflows + the reference doc); bin/lib is excluded from parity.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -23,6 +23,10 @@ INIT=$(gsd_run query init.phase-op "${PHASE_ARG}")
|
||||
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
|
||||
AGENT_SKILLS_FIXER=$(gsd_run query agent-skills gsd-code-fixer)
|
||||
AGENT_SKILLS_REVIEWER=$(gsd_run query agent-skills gsd-code-reviewer)
|
||||
# #2072: resolve the routed models so model_overrides / models.<phaseType> are honored
|
||||
# (gsd-code-reviewer → "verification", gsd-code-fixer → "execution"); thread them below.
|
||||
REVIEWER_MODEL=$(gsd_run query resolve-model gsd-code-reviewer --raw)
|
||||
FIXER_MODEL=$(gsd_run query resolve-model gsd-code-fixer --raw)
|
||||
```
|
||||
|
||||
Parse from init JSON: `phase_found`, `phase_dir`, `phase_number`, `phase_name`, `padded_phase`, `commit_docs`.
|
||||
@@ -194,7 +198,7 @@ echo "Fix scope: ${FIX_SCOPE}"
|
||||
Use Agent() to spawn agent:
|
||||
|
||||
```text
|
||||
Agent(subagent_type="gsd-code-fixer", prompt="
|
||||
Agent(subagent_type="gsd-code-fixer", model="{FIXER_MODEL}", prompt="
|
||||
<files_to_read>
|
||||
${REVIEW_PATH}
|
||||
</files_to_read>
|
||||
@@ -277,7 +281,7 @@ if [ "$AUTO_MODE" = "true" ]; then
|
||||
|
||||
# Spawn gsd-code-reviewer agent to re-review (runs in a subagent — no output until it returns, ~1–5 min; expected, not a freeze)
|
||||
# (This overwrites REVIEW_PATH with latest review state)
|
||||
Agent(subagent_type="gsd-code-reviewer", prompt="
|
||||
Agent(subagent_type="gsd-code-reviewer", model="{REVIEWER_MODEL}", prompt="
|
||||
<config>
|
||||
depth: ${REVIEW_DEPTH}
|
||||
phase_dir: ${PHASE_DIR}
|
||||
@@ -311,7 +315,7 @@ ${AGENT_SKILLS_REVIEWER}")
|
||||
# Still has issues — spawn fixer again (runs in a subagent — no output until it returns, ~1–5 min; expected, not a freeze)
|
||||
echo "Issues remain. Applying fixes for iteration ${ITERATION}..."
|
||||
|
||||
Agent(subagent_type="gsd-code-fixer", prompt="
|
||||
Agent(subagent_type="gsd-code-fixer", model="{FIXER_MODEL}", prompt="
|
||||
<files_to_read>
|
||||
${REVIEW_PATH}
|
||||
</files_to_read>
|
||||
|
||||
@@ -22,6 +22,9 @@ PHASE_ARG="${1}"
|
||||
INIT=$(gsd_run query init.phase-op "${PHASE_ARG}")
|
||||
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
|
||||
AGENT_SKILLS_REVIEWER=$(gsd_run query agent-skills gsd-code-reviewer)
|
||||
# #2072: resolve the routed model so model_overrides / models.verification are honored
|
||||
# (the resolver maps gsd-code-reviewer → phaseType "verification"); thread it below.
|
||||
REVIEWER_MODEL=$(gsd_run query resolve-model gsd-code-reviewer --raw)
|
||||
```
|
||||
|
||||
Parse from init JSON: `phase_found`, `phase_dir`, `phase_number`, `phase_name`, `padded_phase`, `commit_docs`.
|
||||
@@ -482,7 +485,7 @@ Spawn the gsd-code-reviewer agent:
|
||||
Print: `◆ Spawning code reviewer... (runs in a subagent — no output until it returns, ~1–5 min; expected, not a freeze)`
|
||||
|
||||
```
|
||||
Agent(subagent_type="gsd-code-reviewer", prompt="
|
||||
Agent(subagent_type="gsd-code-reviewer", model="{REVIEWER_MODEL}", prompt="
|
||||
<files_to_read>
|
||||
${FILES_TO_READ}
|
||||
</files_to_read>
|
||||
|
||||
@@ -68,6 +68,9 @@ _GSD_SHIM_NAME="gsd-tools.cjs"; _GSD_RUNTIME_ROOT="${RUNTIME_DIR:-$(git rev-pars
|
||||
INIT=$(gsd_run query init.phase-op "${PHASE}")
|
||||
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
|
||||
AGENT_SKILLS_ANALYZER=$(gsd_run query agent-skills gsd-assumptions-analyzer)
|
||||
# #2072: resolve the routed model so model_overrides / models.discuss are honored
|
||||
# (the resolver maps gsd-assumptions-analyzer → phaseType "discuss"); thread it below.
|
||||
ANALYZER_MODEL=$(gsd_run query resolve-model gsd-assumptions-analyzer --raw)
|
||||
```
|
||||
|
||||
Parse JSON for: `commit_docs`, `phase_found`, `phase_dir`, `phase_number`, `phase_name`,
|
||||
@@ -255,7 +258,7 @@ If no USER-PROFILE.md: calibration_tier = "standard"
|
||||
**Spawn Explore subagent** (runs in a subagent — no output until it returns, ~1–5 min; expected, not a freeze)**:**
|
||||
|
||||
```
|
||||
Agent(subagent_type="gsd-assumptions-analyzer", prompt="""
|
||||
Agent(subagent_type="gsd-assumptions-analyzer", model="{ANALYZER_MODEL}", prompt="""
|
||||
Analyze the codebase for Phase {PHASE}: {phase_name}.
|
||||
|
||||
Phase goal: {roadmap_description}
|
||||
|
||||
@@ -134,7 +134,7 @@ AGENT_SKILLS_CHECKER=$(gsd_run query agent-skills gsd-plan-checker)
|
||||
AGENT_SKILLS_VERIFIER=$(gsd_run query agent-skills gsd-verifier)
|
||||
```
|
||||
|
||||
Parse JSON for: `planner_model`, `executor_model`, `checker_model`, `verifier_model`, `commit_docs`, `branch_name`, `quick_id`, `slug`, `date`, `timestamp`, `quick_dir`, `task_dir`, `roadmap_exists`, `planning_exists`.
|
||||
Parse JSON for: `planner_model`, `executor_model`, `checker_model`, `verifier_model`, `reviewer_model`, `commit_docs`, `branch_name`, `quick_id`, `slug`, `date`, `timestamp`, `quick_dir`, `task_dir`, `roadmap_exists`, `planning_exists`.
|
||||
|
||||
```bash
|
||||
USE_WORKTREES=$(gsd_run query config-get workflow.use_worktrees --raw 2>/dev/null || echo "true")
|
||||
@@ -868,7 +868,7 @@ Agent(
|
||||
Output: ${QUICK_DIR}/${quick_id}-REVIEW.md
|
||||
Depth: quick",
|
||||
subagent_type="gsd-code-reviewer",
|
||||
model="{executor_model}"
|
||||
model="{reviewer_model}"
|
||||
)
|
||||
```
|
||||
|
||||
|
||||
Reference in New Issue
Block a user