Files
msd-core/tests/code-review-agent-skills.test.cjs
Tom Boucher 4c10eb2253 fix(#991): inject configured agent_skills into code-review family subagents (#1005)
* fix(#991): inject configured agent_skills into code-review family subagents

code-review.md, code-review-fix.md, and eval-review.md spawned their
subagents (gsd-code-reviewer / gsd-code-fixer / gsd-eval-auditor) without
querying or injecting the project-configured agent_skills, while ~20 sibling
workflows do. Subagents don't inherit the orchestrator's auto-loaded context,
so this injection is the only channel — reviewers/fixers/auditors silently ran
without the configured rule/skill context.

Mirror the established sibling idiom: add
`VAR=$(gsd_run query agent-skills <agent-type>)` in each workflow's initialize
step and interpolate `${VAR}` into every Agent() spawn of that type. This
covers all spawn sites, including code-review-fix.md's --auto loop which
re-spawns gsd-code-reviewer in addition to the two gsd-code-fixer spawns.

Regression test reads the workflow text (source-text-is-the-product) and
asserts each file queries agent-skills for every agent type it spawns and
interpolates the result at least once per spawn.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* chore(#991): add changeset for code-review agent_skills injection fix

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 14:07:23 -04:00

73 lines
3.4 KiB
JavaScript

// allow-test-rule: source-text-is-the-product
// The agent_skills injection for the review-family workflows lives as text in
// the workflow .md files — that text IS what the orchestrating runtime loads
// and executes. There is no intermediate runtime that parses these workflows
// into a prompt we could assert on structurally, so the deployed contract is
// the workflow text itself.
//
// Regression guard for #991: code-review.md / code-review-fix.md /
// eval-review.md were the lone outliers among ~20 workflows that never
// injected the project-configured agent_skills into the subagents they spawn.
// Subagents do not inherit the orchestrator's auto-loaded context, so this
// injection is the ONLY channel for reviewer/fixer/auditor rule context.
const { test, describe } = require('node:test');
const assert = require('node:assert/strict');
const fs = require('fs');
const path = require('path');
const WORKFLOWS_DIR = path.join(__dirname, '..', 'gsd-core', 'workflows');
// Workflow file -> EVERY subagent type it spawns and must inject skills for.
// A workflow can spawn more than one agent type: code-review-fix.md spawns the
// fixer (twice) AND re-spawns the reviewer in its --auto loop, so it must
// inject skills for BOTH. Listing every spawned type here is what catches a
// partially-fixed workflow (the gap Codex flagged on the first pass at #991).
const REVIEW_FAMILY = [
{ file: 'code-review.md', agentTypes: ['gsd-code-reviewer'] },
{ file: 'code-review-fix.md', agentTypes: ['gsd-code-fixer', 'gsd-code-reviewer'] },
{ file: 'eval-review.md', agentTypes: ['gsd-eval-auditor'] },
];
function escapeRe(s) {
return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
const cases = REVIEW_FAMILY.flatMap(({ file, agentTypes }) =>
agentTypes.map((agentType) => ({ file, agentType })),
);
describe('agent_skills injection — review-family workflows (#991)', () => {
for (const { file, agentType } of cases) {
test(`${file} queries + injects agent_skills for every ${agentType} spawn`, () => {
const content = fs.readFileSync(path.join(WORKFLOWS_DIR, file), 'utf8');
// 1. Must query the project-configured skills for this agent type, using
// the same `gsd_run query agent-skills <type>` idiom as the ~20
// sibling workflows (plan-phase, execute-phase, secure-phase, ...).
const assignRe = new RegExp(
'([A-Z][A-Z0-9_]*)=\\$\\(\\s*gsd_run query agent-skills ' + escapeRe(agentType) + '\\s*\\)',
);
const m = content.match(assignRe);
assert.ok(
m,
`${file}: missing \`VAR=$(gsd_run query agent-skills ${agentType})\` — configured agent_skills are never queried (#991)`,
);
const varName = m[1];
// 2. The queried block must be interpolated into the spawn prompt for
// EVERY spawn of this agent type. code-review-fix.md spawns the fixer
// twice (initial + auto-iteration re-spawn); both must inject, or a
// spawn runs under-equipped.
const interpolations = content.split('${' + varName + '}').length - 1;
const spawnCount = (
content.match(new RegExp('subagent_type=["\']' + escapeRe(agentType) + '["\']', 'g')) || []
).length;
assert.ok(
interpolations >= Math.max(1, spawnCount),
`${file}: \${${varName}} is interpolated ${interpolations}x but ${agentType} is spawned ${spawnCount}x — every spawn must inject the skills block (#991)`,
);
});
}
});