Adds mcp__perplexity__* to both researcher profiles (generated source-of-truth) and regenerates the agents; adds a generative dispatch-table↔tools parity guard so future provider drift fails CI. Regenerates the agent-size baseline for the +20-byte frontmatter growth. Fixes #1284 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
5
.changeset/happy-voles-roar.md
Normal file
5
.changeset/happy-voles-roar.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
type: Fixed
|
||||
pr: 1288
|
||||
---
|
||||
**Researcher agents can now invoke Perplexity** — `gsd-phase-researcher` and `gsd-project-researcher` referenced `mcp__perplexity__*` in their provider dispatch tables but never granted it in their `tools:` allowlist, so Perplexity web research silently fell through to the next provider. The grant is now generated from the researcher profiles, with a parity guard that fails if a future dispatch-table provider is added without its tool grant. (#1284)
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
name: gsd-phase-researcher
|
||||
description: Researches how to implement a phase before planning. Produces RESEARCH.md consumed by gsd-planner. Spawned by /gsd:plan-phase orchestrator.
|
||||
tools: Read, Write, Edit, Bash, Grep, Glob, Skill, WebSearch, WebFetch, mcp__context7__*, mcp__firecrawl__*, mcp__exa__*, mcp__tavily__*, mcp__ref__*, mcp__jina__*
|
||||
tools: Read, Write, Edit, Bash, Grep, Glob, Skill, WebSearch, WebFetch, mcp__context7__*, mcp__firecrawl__*, mcp__exa__*, mcp__tavily__*, mcp__ref__*, mcp__jina__*, mcp__perplexity__*
|
||||
color: cyan
|
||||
# hooks:
|
||||
# PostToolUse:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
name: gsd-project-researcher
|
||||
description: Researches domain ecosystem before roadmap creation. Produces files in .planning/research/ consumed during roadmap creation. Spawned by /gsd:new-project or /gsd:new-milestone orchestrators.
|
||||
tools: Read, Write, Bash, Grep, Glob, Skill, WebSearch, WebFetch, mcp__context7__*, mcp__firecrawl__*, mcp__exa__*, mcp__tavily__*, mcp__ref__*, mcp__jina__*
|
||||
tools: Read, Write, Bash, Grep, Glob, Skill, WebSearch, WebFetch, mcp__context7__*, mcp__firecrawl__*, mcp__exa__*, mcp__tavily__*, mcp__ref__*, mcp__jina__*, mcp__perplexity__*
|
||||
color: cyan
|
||||
# hooks:
|
||||
# PostToolUse:
|
||||
|
||||
@@ -24,7 +24,7 @@ const PROFILES = [
|
||||
'Researches domain ecosystem before roadmap creation. Produces files in .planning/research/ consumed during roadmap creation. Spawned by /gsd:new-project or /gsd:new-milestone orchestrators.',
|
||||
color: 'cyan',
|
||||
tools:
|
||||
'Read, Write, Bash, Grep, Glob, Skill, WebSearch, WebFetch, mcp__context7__*, mcp__firecrawl__*, mcp__exa__*, mcp__tavily__*, mcp__ref__*, mcp__jina__*',
|
||||
'Read, Write, Bash, Grep, Glob, Skill, WebSearch, WebFetch, mcp__context7__*, mcp__firecrawl__*, mcp__exa__*, mcp__tavily__*, mcp__ref__*, mcp__jina__*, mcp__perplexity__*',
|
||||
requiredIncludes: [
|
||||
'@~/.claude/gsd-core/references/research-documentation-lookup.md',
|
||||
'@~/.claude/gsd-core/references/research-philosophy.md',
|
||||
@@ -46,7 +46,7 @@ const PROFILES = [
|
||||
'Researches how to implement a phase before planning. Produces RESEARCH.md consumed by gsd-planner. Spawned by /gsd:plan-phase orchestrator.',
|
||||
color: 'cyan',
|
||||
tools:
|
||||
'Read, Write, Edit, Bash, Grep, Glob, Skill, WebSearch, WebFetch, mcp__context7__*, mcp__firecrawl__*, mcp__exa__*, mcp__tavily__*, mcp__ref__*, mcp__jina__*',
|
||||
'Read, Write, Edit, Bash, Grep, Glob, Skill, WebSearch, WebFetch, mcp__context7__*, mcp__firecrawl__*, mcp__exa__*, mcp__tavily__*, mcp__ref__*, mcp__jina__*, mcp__perplexity__*',
|
||||
requiredIncludes: [
|
||||
'@~/.claude/gsd-core/references/research-documentation-lookup.md',
|
||||
'@~/.claude/gsd-core/references/research-philosophy.md',
|
||||
|
||||
@@ -21,10 +21,10 @@
|
||||
"gsd-mempalace-curator.md": 4160,
|
||||
"gsd-nyquist-auditor.md": 7255,
|
||||
"gsd-pattern-mapper.md": 12487,
|
||||
"gsd-phase-researcher.md": 40618,
|
||||
"gsd-phase-researcher.md": 40638,
|
||||
"gsd-plan-checker.md": 42003,
|
||||
"gsd-planner.md": 48892,
|
||||
"gsd-project-researcher.md": 21994,
|
||||
"gsd-project-researcher.md": 22014,
|
||||
"gsd-research-synthesizer.md": 13653,
|
||||
"gsd-roadmapper.md": 21781,
|
||||
"gsd-security-auditor.md": 6226,
|
||||
|
||||
@@ -116,3 +116,56 @@ describe('Researcher agent MCP tool set parity: new tools match exa/firecrawl pa
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// --- Regression (#1284): every MCP-backed provider named in the Step-C
|
||||
// dispatch table must be granted in the agent's frontmatter `tools:` line.
|
||||
// Guards against a provider being added to the waterfall + dispatch table
|
||||
// without the matching mcp__<server>__* grant (the perplexity drift). ---
|
||||
describe('researcher Step-C dispatch ↔ tools frontmatter parity (#1284)', () => {
|
||||
const RESEARCHERS = ['gsd-phase-researcher', 'gsd-project-researcher'];
|
||||
|
||||
function mcpServersIn(text) {
|
||||
const servers = new Set();
|
||||
const re = /mcp__([a-z0-9]+)__/gi;
|
||||
let m;
|
||||
while ((m = re.exec(text)) !== null) servers.add(m[1].toLowerCase());
|
||||
return servers;
|
||||
}
|
||||
function readAgent(name) {
|
||||
return fs.readFileSync(path.join(__dirname, '..', 'agents', `${name}.md`), 'utf8');
|
||||
}
|
||||
function toolsLine(content) {
|
||||
const m = content.match(/^tools:\s*(.+)$/m);
|
||||
assert.ok(m, 'agent frontmatter must have a tools: line');
|
||||
return m[1];
|
||||
}
|
||||
function stepCTableRows(content) {
|
||||
const start = content.indexOf('### Step C');
|
||||
assert.ok(start !== -1, 'agent must have a "### Step C" dispatch section');
|
||||
const rest = content.slice(start + 1);
|
||||
const nextHeading = rest.indexOf('\n### ');
|
||||
const section = nextHeading === -1 ? rest : rest.slice(0, nextHeading);
|
||||
// Only the markdown dispatch-table rows (| provider | mcp tool |) define
|
||||
// provider->tool mappings. Generic fallback prose (e.g. `mcp__<provider>__*`)
|
||||
// is intentionally excluded so it cannot create false positives.
|
||||
return section
|
||||
.split('\n')
|
||||
.filter((line) => line.trimStart().startsWith('|'))
|
||||
.join('\n');
|
||||
}
|
||||
|
||||
for (const name of RESEARCHERS) {
|
||||
test(`${name}: grants mcp__<server>__* for every MCP provider in its Step-C table`, () => {
|
||||
const content = readAgent(name);
|
||||
const granted = mcpServersIn(toolsLine(content));
|
||||
const referenced = mcpServersIn(stepCTableRows(content));
|
||||
assert.ok(referenced.size > 0,
|
||||
`${name} Step-C table should reference at least one mcp__ provider`);
|
||||
const missing = [...referenced].filter((srv) => !granted.has(srv));
|
||||
assert.deepStrictEqual(missing, [],
|
||||
`${name}: Step-C references MCP provider(s) not granted in tools: frontmatter: ` +
|
||||
`${missing.join(', ')}. Add mcp__<server>__* to the profile in ` +
|
||||
`scripts/research-profiles.cjs and regenerate.`);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user