From dbb0a653be4435127417c2079ce20b8b4fe5dd0f Mon Sep 17 00:00:00 2001 From: Tom Boucher Date: Thu, 30 Jul 2026 17:25:55 -0400 Subject: [PATCH] fix(#2771): advisor mode spawns registered gsd-advisor-researcher subagent instead of general-purpose (#2884) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * test(#2771): advisor mode must spawn gsd-advisor-researcher, not general-purpose * fix(#2771): spawn registered gsd-advisor-researcher subagent instead of general-purpose in advisor mode universal-anti-patterns rule 10 (injected into discuss-phase via ) says NEVER use non-GSD agent types. The advisor mode spawned general-purpose and manually told the agent to read the def — but gsd-advisor-researcher IS registered, so spawning by type auto-loads it. Drop the manual-read prompt line (re-specifying the def is a drift risk) and use the registered type. * chore(#2771): changeset fragment (mentions follow-up #2883) * test(#2771): widen manual-read-line regex to deny phrasing variants (review minor) /read\s+@.*gsd-advisor-researcher\.md/i (case-insensitive, any 'read @' lead-in) so a drift variant like 'Read @' or 'Load @' can't sneak the manual-def-read back in. * chore(#2771): backfill changeset PR number (2884) --------- Co-authored-by: Test --- .changeset/curious-pumas-wake.md | 5 +++ .../workflows/discuss-phase/modes/advisor.md | 6 +-- .../issue-2771-advisor-subagent-type.test.cjs | 43 +++++++++++++++++++ 3 files changed, 50 insertions(+), 4 deletions(-) create mode 100644 .changeset/curious-pumas-wake.md create mode 100644 tests/issue-2771-advisor-subagent-type.test.cjs diff --git a/.changeset/curious-pumas-wake.md b/.changeset/curious-pumas-wake.md new file mode 100644 index 000000000..4a09fd49a --- /dev/null +++ b/.changeset/curious-pumas-wake.md @@ -0,0 +1,5 @@ +--- +type: Fixed +pr: 2886 +--- +**Discuss-phase advisor mode now spawns the registered `gsd-advisor-researcher` subagent instead of `general-purpose`** — resolving a contradiction with the universal-anti-patterns rule (injected into the same context) that forbids non-GSD agent types. The manual "read the agent def" prompt line is dropped (spawning by type auto-loads it). (#2771; the sibling assumptions-site needs a design decision — filed as #2883) diff --git a/gsd-core/workflows/discuss-phase/modes/advisor.md b/gsd-core/workflows/discuss-phase/modes/advisor.md index 037908b54..41518a713 100644 --- a/gsd-core/workflows/discuss-phase/modes/advisor.md +++ b/gsd-core/workflows/discuss-phase/modes/advisor.md @@ -92,16 +92,14 @@ research agents. ``` Agent( - prompt="First, read @~/.claude/agents/gsd-advisor-researcher.md for your role and instructions. - - {area_name}: {area_description from gray area identification} + prompt="{area_name}: {area_description from gray area identification} {phase_goal and description from ROADMAP.md} {project name and brief description from PROJECT.md} {resolved calibration tier: full_maturity | standard | minimal_decisive} Research this gray area and return a structured comparison table with rationale. ${AGENT_SKILLS_ADVISOR}", - subagent_type="general-purpose", + subagent_type="gsd-advisor-researcher", model="{ADVISOR_MODEL}", description="Research: {area_name}" ) diff --git a/tests/issue-2771-advisor-subagent-type.test.cjs b/tests/issue-2771-advisor-subagent-type.test.cjs new file mode 100644 index 000000000..c1b41e692 --- /dev/null +++ b/tests/issue-2771-advisor-subagent-type.test.cjs @@ -0,0 +1,43 @@ +// allow-test-rule: structural-implementation-guard (#2771) +'use strict'; + +// Regression guard for #2771: the discuss-phase advisor mode must spawn the REGISTERED +// `gsd-advisor-researcher` subagent (auto-loads the agent def), not `general-purpose` — +// which contradicts universal-anti-patterns rule 10 (injected into discuss-phase via +// ): "NEVER use non-GSD agent types — ALWAYS use gsd-{agent}". +// Spawning general-purpose + a manual "read the agent def" prompt re-specifies what the +// def already owns (a drift risk; the same shape assumptions's answer_validation hit). + +const { test } = require('node:test'); +const assert = require('node:assert/strict'); +const fs = require('fs'); +const path = require('path'); + +const ADVISOR_MD = path.join( + __dirname, '..', 'gsd-core', 'workflows', 'discuss-phase', 'modes', 'advisor.md' +); + +test('advisor mode spawns the registered gsd-advisor-researcher subagent, not general-purpose (#2771)', () => { + const src = fs.readFileSync(ADVISOR_MD, 'utf8'); + + // Locate the Agent() block that researches gray areas. + const agentIdx = src.indexOf('subagent_type='); + assert.ok(agentIdx !== -1, 'advisor.md must contain an Agent() subagent_type declaration'); + + assert.ok( + src.includes('subagent_type="gsd-advisor-researcher"'), + 'advisor mode must spawn subagent_type="gsd-advisor-researcher" (the registered agent def auto-loads) — not general-purpose (#2771, universal-anti-patterns rule 10)' + ); + assert.ok( + !src.includes('subagent_type="general-purpose"'), + 'advisor mode must NOT spawn subagent_type="general-purpose" (contradicts universal-anti-patterns rule 10, injected into the same context) (#2771)' + ); + // The manual "read @.../gsd-advisor-researcher.md" prompt line must be gone — + // spawning by type auto-loads the def; re-specifying it is a drift risk. Deny the + // full class (any "read @" lead-in, case-insensitive) so a phrasing variant can't + // sneak the drift back in. + assert.ok( + !/read\s+@.*gsd-advisor-researcher\.md/i.test(src), + 'advisor mode must not manually instruct reading the agent def — spawning by type auto-loads it (#2771)' + ); +});