feat: harden Nyquist defaults, add retroactive validation, compress prompts (#855)
* fix: change nyquist_validation default to true and harden absent-key skip conditions new-project.md never wrote the key, so agents reading config directly treated absent as falsy. Changed all agent skip conditions from "is false" to "explicitly set to false; absent = enabled". Default changed from false to true in core.cjs, config.cjs, and templates/config.json. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: enforce VALIDATION.md creation with verification gate and Check 8e Step 5.5 was narrative markdown that Claude skipped under context pressure. Now MANDATORY with Write tool requirement and file-existence verification. Step 7.5 gates planner spawn on VALIDATION.md presence. Check 8e blocks Dimension 8 if file missing. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: add W008/W009 health checks and addNyquistKey repair for Nyquist drift detection W008 warns when workflow.nyquist_validation key is absent from config.json (agents may skip validation). W009 warns when RESEARCH.md has Validation Architecture section but no VALIDATION.md file exists. addNyquistKey repair adds the missing key with default true value. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: add /gsd:validate-phase command and gsd-nyquist-auditor agent Retroactively applies Nyquist validation to already-executed phases. Works mid-milestone and post-milestone. Detects existing test coverage, maps gaps to phase requirements, writes missing tests, debugs failing ones, and produces {phase}-VALIDATION.md from existing artifacts. Handles three states: VALIDATION.md exists (audit + update), no VALIDATION.md (reconstruct from PLAN.md + SUMMARY.md), phase not yet executed (exit cleanly with guidance). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: audit-milestone reports Nyquist compliance gaps across phases Adds Nyquist coverage table to audit-milestone output when workflow.nyquist_validation is true. Identifies phases missing VALIDATION.md or with nyquist_compliant: false/partial. Routes to /gsd:validate-phase for resolution. Updates USER-GUIDE with retroactive validation documentation. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor: compress Nyquist prompts to match GSD meta-prompt density conventions Auditor agent: deleted philosophy section (35 lines), compressed execution flow 60%, removed redundant constraints. Workflow: cut purpose bloat, collapsed state narrative, compressed auditor spawn template. Command: removed redundant process section. Plan-phase Steps 5.5/7.5: replaced hedging language with directives. Audit-milestone Step 5.5: collapsed sub-steps into inline instructions. Net: -376 lines. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -60,6 +60,7 @@ describe('loadConfig', () => {
|
||||
assert.strictEqual(config.plan_checker, true);
|
||||
assert.strictEqual(config.brave_search, false);
|
||||
assert.strictEqual(config.parallelization, true);
|
||||
assert.strictEqual(config.nyquist_validation, true);
|
||||
});
|
||||
|
||||
test('reads model_profile from config.json', () => {
|
||||
|
||||
@@ -347,6 +347,103 @@ describe('validate health command', () => {
|
||||
);
|
||||
});
|
||||
|
||||
// ─── Check 5b: Nyquist validation key presence (W008) ─────────────────────
|
||||
|
||||
test('detects W008 when workflow.nyquist_validation absent from config', () => {
|
||||
writeMinimalProjectMd(tmpDir);
|
||||
writeMinimalRoadmap(tmpDir, ['1']);
|
||||
writeMinimalStateMd(tmpDir, '# Session State\n\nPhase 1 in progress.\n');
|
||||
// Config with workflow section but WITHOUT nyquist_validation key
|
||||
fs.writeFileSync(
|
||||
path.join(tmpDir, '.planning', 'config.json'),
|
||||
JSON.stringify({ model_profile: 'balanced', workflow: { research: true } }, null, 2)
|
||||
);
|
||||
fs.mkdirSync(path.join(tmpDir, '.planning', 'phases', '01-a'), { recursive: true });
|
||||
|
||||
const result = runGsdTools('validate health', tmpDir);
|
||||
assert.ok(result.success, `Command failed: ${result.error}`);
|
||||
|
||||
const output = JSON.parse(result.output);
|
||||
assert.ok(
|
||||
output.warnings.some(w => w.code === 'W008'),
|
||||
`Expected W008 in warnings: ${JSON.stringify(output.warnings)}`
|
||||
);
|
||||
});
|
||||
|
||||
test('does not emit W008 when nyquist_validation is explicitly set', () => {
|
||||
writeMinimalProjectMd(tmpDir);
|
||||
writeMinimalRoadmap(tmpDir, ['1']);
|
||||
writeMinimalStateMd(tmpDir, '# Session State\n\nPhase 1 in progress.\n');
|
||||
// Config with workflow.nyquist_validation explicitly set
|
||||
fs.writeFileSync(
|
||||
path.join(tmpDir, '.planning', 'config.json'),
|
||||
JSON.stringify({ model_profile: 'balanced', workflow: { research: true, nyquist_validation: true } }, null, 2)
|
||||
);
|
||||
fs.mkdirSync(path.join(tmpDir, '.planning', 'phases', '01-a'), { recursive: true });
|
||||
|
||||
const result = runGsdTools('validate health', tmpDir);
|
||||
assert.ok(result.success, `Command failed: ${result.error}`);
|
||||
|
||||
const output = JSON.parse(result.output);
|
||||
assert.ok(
|
||||
!output.warnings.some(w => w.code === 'W008'),
|
||||
`Should not have W008: ${JSON.stringify(output.warnings)}`
|
||||
);
|
||||
});
|
||||
|
||||
// ─── Check 7b: Nyquist VALIDATION.md consistency (W009) ──────────────────
|
||||
|
||||
test('detects W009 when RESEARCH.md has Validation Architecture but no VALIDATION.md', () => {
|
||||
writeMinimalProjectMd(tmpDir);
|
||||
writeMinimalRoadmap(tmpDir, ['1']);
|
||||
writeMinimalStateMd(tmpDir, '# Session State\n\nPhase 1 in progress.\n');
|
||||
writeValidConfigJson(tmpDir);
|
||||
// Create phase dir with RESEARCH.md containing Validation Architecture
|
||||
const phaseDir = path.join(tmpDir, '.planning', 'phases', '01-setup');
|
||||
fs.mkdirSync(phaseDir, { recursive: true });
|
||||
fs.writeFileSync(
|
||||
path.join(phaseDir, '01-RESEARCH.md'),
|
||||
'# Research\n\n## Validation Architecture\n\nSome validation content.\n'
|
||||
);
|
||||
// No VALIDATION.md
|
||||
|
||||
const result = runGsdTools('validate health', tmpDir);
|
||||
assert.ok(result.success, `Command failed: ${result.error}`);
|
||||
|
||||
const output = JSON.parse(result.output);
|
||||
assert.ok(
|
||||
output.warnings.some(w => w.code === 'W009'),
|
||||
`Expected W009 in warnings: ${JSON.stringify(output.warnings)}`
|
||||
);
|
||||
});
|
||||
|
||||
test('does not emit W009 when VALIDATION.md exists alongside RESEARCH.md', () => {
|
||||
writeMinimalProjectMd(tmpDir);
|
||||
writeMinimalRoadmap(tmpDir, ['1']);
|
||||
writeMinimalStateMd(tmpDir, '# Session State\n\nPhase 1 in progress.\n');
|
||||
writeValidConfigJson(tmpDir);
|
||||
// Create phase dir with both RESEARCH.md and VALIDATION.md
|
||||
const phaseDir = path.join(tmpDir, '.planning', 'phases', '01-setup');
|
||||
fs.mkdirSync(phaseDir, { recursive: true });
|
||||
fs.writeFileSync(
|
||||
path.join(phaseDir, '01-RESEARCH.md'),
|
||||
'# Research\n\n## Validation Architecture\n\nSome validation content.\n'
|
||||
);
|
||||
fs.writeFileSync(
|
||||
path.join(phaseDir, '01-VALIDATION.md'),
|
||||
'# Validation\n\nValidation content.\n'
|
||||
);
|
||||
|
||||
const result = runGsdTools('validate health', tmpDir);
|
||||
assert.ok(result.success, `Command failed: ${result.error}`);
|
||||
|
||||
const output = JSON.parse(result.output);
|
||||
assert.ok(
|
||||
!output.warnings.some(w => w.code === 'W009'),
|
||||
`Should not have W009: ${JSON.stringify(output.warnings)}`
|
||||
);
|
||||
});
|
||||
|
||||
// ─── Overall status ────────────────────────────────────────────────────────
|
||||
|
||||
test("returns 'healthy' when all checks pass", () => {
|
||||
@@ -509,6 +606,32 @@ describe('validate health --repair command', () => {
|
||||
assert.ok(backupContent.includes('Phase 99'), 'backup should contain the original STATE.md content');
|
||||
});
|
||||
|
||||
test('adds nyquist_validation key to config.json via addNyquistKey repair', () => {
|
||||
writeMinimalStateMd(tmpDir, '# Session State\n\nPhase 1 in progress.\n');
|
||||
// Config with workflow section but missing nyquist_validation
|
||||
const configPath = path.join(tmpDir, '.planning', 'config.json');
|
||||
fs.writeFileSync(
|
||||
configPath,
|
||||
JSON.stringify({ model_profile: 'balanced', workflow: { research: true } }, null, 2)
|
||||
);
|
||||
|
||||
const result = runGsdTools('validate health --repair', tmpDir);
|
||||
assert.ok(result.success, `Command failed: ${result.error}`);
|
||||
|
||||
const output = JSON.parse(result.output);
|
||||
assert.ok(
|
||||
Array.isArray(output.repairs_performed),
|
||||
`Expected repairs_performed array: ${JSON.stringify(output)}`
|
||||
);
|
||||
const addKeyAction = output.repairs_performed.find(r => r.action === 'addNyquistKey');
|
||||
assert.ok(addKeyAction, `Expected addNyquistKey action: ${JSON.stringify(output.repairs_performed)}`);
|
||||
assert.strictEqual(addKeyAction.success, true, 'addNyquistKey should succeed');
|
||||
|
||||
// Read config.json and verify workflow.nyquist_validation is true
|
||||
const diskConfig = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
||||
assert.strictEqual(diskConfig.workflow.nyquist_validation, true, 'nyquist_validation should be true');
|
||||
});
|
||||
|
||||
test('reports repairable_count correctly', () => {
|
||||
// No config.json (W003, repairable=true) and no STATE.md (E004, repairable=true)
|
||||
const configPath = path.join(tmpDir, '.planning', 'config.json');
|
||||
|
||||
Reference in New Issue
Block a user