* fix(2119): single SECURITY.md writer — auditor is return-only The gsd-security-auditor held Write/Edit and was instructed to write SECURITY.md (no <N>- prefix, no template frontmatter), while the orchestrator's Step 6 also wrote the correct padded <N>-SECURITY.md from templates/SECURITY.md. Two writers, two naming conventions, two shapes — the auditor's unprefixed file was invisible to the workflow's *-SECURITY.md glob detector and unparseable for the threats_open gate. Fix (option 1 from the issue): make the auditor return-only. - Remove Write/Edit from auditor's tools - Rewrite all 'Write SECURITY.md' instructions to 'Return structured verdict' with threats_open count - Add explicit constraint in workflow Step 5 spawn prompt - Update existing test (was asserting Write in tools — now asserts absence) - Add new regression test for single-writer contract - Update docs/AGENTS.md stale Tools/Produces rows - Regenerate golden fixtures + agent size baseline * docs(changeset): backfill PR number (#2154) * chore(#2119): regenerate pi/qwen golden fixtures after next merge The single-writer change edits gsd-core/workflows/secure-phase.md and agents/gsd-security-auditor.md; pi.json (added on next) and qwen.json (merge straggler) were the only runtime fixtures still holding pre-change hashes for those files. All other runtimes already reflect the change. Regenerated via the sanctioned gen-golden-install-parity script. * merge origin/next — regenerate goldens + baseline for merged state * fix slash-command syntax: /gsd-secure-phase → /gsd:secure-phase (#2154 CI fix)
74 lines
2.4 KiB
JavaScript
74 lines
2.4 KiB
JavaScript
/**
|
|
* Regression test for #2119: /gsd-secure-phase dual SECURITY.md writers.
|
|
*
|
|
* The gsd-security-auditor agent must NOT have Write/Edit tools — the
|
|
* orchestrator (secure-phase.md Step 6) is the sole SECURITY.md writer.
|
|
* The auditor returns a structured verdict; it never writes files.
|
|
*/
|
|
|
|
const { describe, test } = require('node:test');
|
|
const assert = require('node:assert/strict');
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
|
|
const AGENT_PATH = path.join(__dirname, '..', 'agents', 'gsd-security-auditor.md');
|
|
|
|
function parseYamlTools(content) {
|
|
const lines = content.split(/\r?\n/);
|
|
let inFrontmatter = false;
|
|
let inTools = false;
|
|
const tools = [];
|
|
for (const line of lines) {
|
|
const trimmed = line.trim();
|
|
if (trimmed === '---') {
|
|
inFrontmatter = !inFrontmatter;
|
|
if (!inFrontmatter) break;
|
|
continue;
|
|
}
|
|
if (!inFrontmatter) continue;
|
|
if (trimmed.startsWith('tools:')) {
|
|
inTools = true;
|
|
continue;
|
|
}
|
|
if (inTools) {
|
|
if (trimmed.startsWith('- ')) {
|
|
tools.push(trimmed.slice(2).trim());
|
|
} else if (trimmed && !trimmed.startsWith('#')) {
|
|
inTools = false;
|
|
}
|
|
}
|
|
}
|
|
return tools;
|
|
}
|
|
|
|
describe('#2119 — security auditor is return-only (no file writes)', () => {
|
|
const content = fs.readFileSync(AGENT_PATH, 'utf-8');
|
|
|
|
test('auditor tools do not include Write or Edit', () => {
|
|
const tools = parseYamlTools(content);
|
|
assert.ok(tools.length > 0, 'tools list should be non-empty');
|
|
assert.ok(
|
|
!tools.includes('Write'),
|
|
`Write must not be in auditor tools (got: ${tools.join(', ')}) — orchestrator is the sole SECURITY.md writer (#2119)`,
|
|
);
|
|
assert.ok(
|
|
!tools.includes('Edit'),
|
|
`Edit must not be in auditor tools (got: ${tools.join(', ')}) — orchestrator is the sole SECURITY.md writer (#2119)`,
|
|
);
|
|
});
|
|
|
|
test('auditor description does not claim to produce SECURITY.md', () => {
|
|
const lines = content.split(/\r?\n/);
|
|
const descLine = lines.find((l) => l.startsWith('description:'));
|
|
assert.ok(descLine, 'description field must exist');
|
|
assert.ok(
|
|
!descLine.includes('Produces SECURITY.md'),
|
|
'description must not claim to produce SECURITY.md — auditor returns a verdict, orchestrator writes (#2119)',
|
|
);
|
|
assert.ok(
|
|
descLine.includes('Returns structured') || descLine.includes('returns'),
|
|
'description should state the auditor returns a structured verdict',
|
|
);
|
|
});
|
|
});
|