* 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>
73 lines
3.4 KiB
JavaScript
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)`,
|
|
);
|
|
});
|
|
}
|
|
});
|