Files
get-shit-done/tests/enh-2427-sycophancy-hardening.test.cjs
Tom Boucher f19d0327b2 feat(agents): sycophancy hardening for 9 audit-class agents (#2489)
* fix(tests): update 5 source-text tests to read config-schema.cjs

VALID_CONFIG_KEYS moved from config.cjs to config-schema.cjs in the
drift-prevention companion PR. Tests that read config.cjs source text
and checked for key literal includes() now point to the correct file.

Closes #2480

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(agents): sycophancy hardening for 9 audit-class agents (#2427)

Add adversarial reviewer posture to gsd-plan-checker, gsd-code-reviewer,
gsd-security-auditor, gsd-verifier, gsd-eval-auditor, gsd-nyquist-auditor,
gsd-ui-auditor, gsd-integration-checker, and gsd-doc-verifier.

Four changes per agent:
- Third-person framing: <role> opens with submission framing, not "You are a GSD X"
- FORCE stance: explicit starting hypothesis that the submission is flawed
- Failure modes: agent-specific list of how each reviewer type goes soft
- BLOCKER/WARNING classification: every finding must carry an explicit severity

Also applies to sdk/prompts/agents variants of gsd-plan-checker and gsd-verifier.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-20 18:20:08 -04:00

128 lines
4.1 KiB
JavaScript

'use strict';
/**
* Tests for #2427 — prompt-level sycophancy hardening of audit-class agents.
* Verifies the four required changes are present in each agent file:
* 1. Third-person framing (no "You are a GSD X" opening in <role>)
* 2. FORCE adversarial stance block
* 3. Explicit failure modes list
* 4. BLOCKER/WARNING classification requirement
*/
const { test, describe } = require('node:test');
const assert = require('node:assert/strict');
const fs = require('node:fs');
const path = require('node:path');
const AGENTS_DIR = path.join(__dirname, '../agents');
const SDK_AGENTS_DIR = path.join(__dirname, '../sdk/prompts/agents');
const AUDIT_AGENTS = [
'gsd-plan-checker.md',
'gsd-code-reviewer.md',
'gsd-security-auditor.md',
'gsd-verifier.md',
'gsd-eval-auditor.md',
'gsd-nyquist-auditor.md',
'gsd-ui-auditor.md',
'gsd-integration-checker.md',
'gsd-doc-verifier.md',
];
const SDK_AUDIT_AGENTS = [
'gsd-plan-checker.md',
'gsd-verifier.md',
];
function readAgent(agentsDir, filename) {
return fs.readFileSync(path.join(agentsDir, filename), 'utf-8');
}
function extractRole(content) {
const match = content.match(/<role>([\s\S]*?)<\/role>/);
return match ? match[1] : '';
}
describe('enh-2427 — sycophancy hardening: audit-class agents', () => {
for (const filename of AUDIT_AGENTS) {
const label = filename.replace('.md', '');
describe(label, () => {
let content;
let role;
test('file is readable', () => {
content = readAgent(AGENTS_DIR, filename);
role = extractRole(content);
assert.ok(content.length > 0, `${filename} should not be empty`);
});
test('(1) third-person framing — <role> does not open with "You are a GSD"', () => {
content = content || readAgent(AGENTS_DIR, filename);
role = role || extractRole(content);
const firstSentence = role.trim().slice(0, 80);
assert.ok(
!firstSentence.startsWith('You are a GSD'),
`${filename}: <role> must not open with "You are a GSD" — use third-person submission framing. Got: "${firstSentence}"`
);
});
test('(2) FORCE adversarial stance — <adversarial_stance> block present', () => {
content = content || readAgent(AGENTS_DIR, filename);
assert.ok(
content.includes('<adversarial_stance>'),
`${filename}: must contain <adversarial_stance> block`
);
assert.ok(
content.includes('FORCE stance'),
`${filename}: <adversarial_stance> must contain "FORCE stance"`
);
});
test('(3) explicit failure modes list present', () => {
content = content || readAgent(AGENTS_DIR, filename);
assert.ok(
content.includes('failure modes'),
`${filename}: must contain "failure modes" section in <adversarial_stance>`
);
});
test('(4) BLOCKER/WARNING classification requirement present', () => {
content = content || readAgent(AGENTS_DIR, filename);
assert.ok(
content.includes('**BLOCKER**'),
`${filename}: must define BLOCKER classification in <adversarial_stance>`
);
assert.ok(
content.includes('**WARNING**'),
`${filename}: must define WARNING classification in <adversarial_stance>`
);
});
});
}
describe('sdk/prompts/agents variants', () => {
for (const filename of SDK_AUDIT_AGENTS) {
const label = `sdk/${filename.replace('.md', '')}`;
describe(label, () => {
test('third-person framing and adversarial_stance block present', () => {
const content = readAgent(SDK_AGENTS_DIR, filename);
const role = extractRole(content);
const firstSentence = role.trim().slice(0, 80);
assert.ok(
!firstSentence.startsWith('You are a GSD'),
`${filename}: SDK variant must not open <role> with "You are a GSD"`
);
assert.ok(
content.includes('<adversarial_stance>'),
`${filename}: SDK variant must contain <adversarial_stance> block`
);
});
});
}
});
});