'use strict'; const { describe, test } = require('node:test'); const assert = require('node:assert/strict'); const fs = require('node:fs'); const path = require('node:path'); const ROOT = path.resolve(__dirname, '..'); const registry = require('../gsd-core/bin/lib/capability-registry.cjs'); function workflow(name) { return fs.readFileSync(path.join(ROOT, 'gsd-core', 'workflows', name), 'utf8'); } function doc(name) { return fs.readFileSync(path.join(ROOT, 'docs', name), 'utf8'); } function sectionBetween(content, startNeedle, endNeedle) { const start = content.indexOf(startNeedle); assert.ok(start !== -1, `${startNeedle} section must exist`); const end = endNeedle ? content.indexOf(endNeedle, start) : -1; return content.slice(start, end === -1 ? undefined : end); } function hookAt(point, kind, capId) { return registry.byLoopPoint[point][kind].find((hook) => hook.capId === capId); } describe('ADR-857 phase 6 verification and review capability migration', () => { test('registry declares code-review, security, and nyquist feature capabilities', () => { for (const capId of ['code-review', 'security', 'nyquist']) { assert.ok(registry.capabilities[capId], `${capId} capability must exist`); assert.equal(registry.capabilities[capId].role, 'feature'); } }); test('code-review capability owns review skills, agents, config, and execute hook', () => { assert.equal(registry.bySkill['code-review'], 'code-review'); assert.equal(registry.byAgent['gsd-code-reviewer'], 'code-review'); assert.equal(registry.byAgent['gsd-code-fixer'], 'code-review'); assert.equal(registry.configKeys['workflow.code_review'], 'code-review'); assert.equal(registry.configKeys['workflow.code_review_depth'], 'code-review'); const hook = hookAt('execute:post', 'steps', 'code-review'); assert.ok(hook, 'code-review must register an execute:post step'); assert.deepEqual(hook.ref, { skill: 'code-review' }); assert.equal(hook.when, 'workflow.code_review'); assert.deepEqual(hook.produces, ['REVIEW.md']); assert.ok(hook.consumes.includes('SUMMARY.md')); }); test('security capability owns secure-phase wiring and blocking ship gate', () => { assert.equal(registry.bySkill['secure-phase'], 'security'); assert.equal(registry.byAgent['gsd-security-auditor'], 'security'); assert.equal(registry.configKeys['workflow.security_enforcement'], 'security'); assert.equal(registry.configKeys['workflow.security_asvs_level'], 'security'); assert.equal(registry.configKeys['workflow.security_block_on'], 'security'); const step = hookAt('verify:post', 'steps', 'security'); assert.ok(step, 'security must register a verify:post step'); assert.deepEqual(step.ref, { skill: 'secure-phase' }); assert.equal(step.when, 'workflow.security_enforcement'); assert.equal(step.onError, 'halt'); const gate = hookAt('ship:pre', 'gates', 'security'); assert.ok(gate, 'security must register a blocking ship:pre gate'); assert.equal(gate.blocking, true); assert.equal(gate.onError, 'halt'); const planContribution = hookAt('plan:pre', 'contributions', 'security'); assert.ok(planContribution, 'security must register a plan:pre contribution for threat-model guidance'); assert.equal(planContribution.into, 'planner'); assert.equal(planContribution.when, 'workflow.security_enforcement'); }); test('nyquist capability owns validate-phase wiring and config', () => { assert.equal(registry.bySkill['validate-phase'], 'nyquist'); assert.equal(registry.byAgent['gsd-nyquist-auditor'], 'nyquist'); assert.equal(registry.configKeys['workflow.nyquist_validation'], 'nyquist'); const hook = hookAt('verify:post', 'steps', 'nyquist'); assert.ok(hook, 'nyquist must register a verify:post step'); assert.deepEqual(hook.ref, { skill: 'validate-phase' }); assert.equal(hook.when, 'workflow.nyquist_validation'); assert.deepEqual(hook.produces, ['VALIDATION.md']); assert.ok(hook.consumes.includes('SUMMARY.md')); }); test('autonomous code review resolves execute:post hooks instead of inlining code_review config', () => { const content = workflow('autonomous.md'); const section = sectionBetween( content, '**3c.5. Code Review and Fix**', '**3d. Post-Execution Routing**', ); assert.ok(section.includes('loop render-hooks execute:post')); assert.ok(section.includes('gsd-${ref.skill}')); assert.ok(!section.includes('config-get workflow.code_review')); }); test('execute-phase code-review gate resolves execute:post hooks instead of inlining code_review config', () => { const content = workflow('execute-phase.md'); // close_parent_artifacts was extracted to // gsd-core/workflows/execute-phase/steps/gap-closure-artifacts.md; the parent now // marks that boundary with a comment // immediately after code_review_gate's closing , so it remains the correct // end-of-step delimiter for isolating this step's body. const section = sectionBetween(content, ' { const content = workflow('quick.md'); // #2994 fragmentization moved Step 6.5 (Verification) out of quick.md into // gsd-core/workflows/quick/steps/quick-verification.md; the parent now marks // that boundary with a `` comment // immediately after Step 6.25's content, so it remains the correct // end-of-step delimiter (mirrors the execute-phase.md gap-closure-artifacts // pattern above) instead of bleeding to end-of-file. const section = sectionBetween(content, '**Step 6.25: Code review (auto)**', '