diff --git a/tests/verify.test.cjs b/tests/verify.test.cjs
index a89af1b05..b4a185bd7 100644
--- a/tests/verify.test.cjs
+++ b/tests/verify.test.cjs
@@ -545,4 +545,469 @@ describe('verify summary command', () => {
});
});
+// ─────────────────────────────────────────────────────────────────────────────
+// verify references command
+// ─────────────────────────────────────────────────────────────────────────────
+describe('verify references command', () => {
+ let tmpDir;
+
+ beforeEach(() => {
+ tmpDir = createTempProject();
+ fs.mkdirSync(path.join(tmpDir, 'src', 'utils'), { recursive: true });
+ fs.mkdirSync(path.join(tmpDir, '.planning', 'phases', '01-test'), { recursive: true });
+ });
+
+ afterEach(() => {
+ cleanup(tmpDir);
+ });
+
+ test('reports valid when all referenced files exist', () => {
+ fs.writeFileSync(path.join(tmpDir, 'src', 'app.js'), 'console.log("app");\n');
+ const filePath = path.join(tmpDir, '.planning', 'phases', '01-test', 'doc.md');
+ fs.writeFileSync(filePath, '@src/app.js\n');
+
+ const result = runGsdTools('verify references .planning/phases/01-test/doc.md', tmpDir);
+ assert.ok(result.success, `Command failed: ${result.error}`);
+
+ const output = JSON.parse(result.output);
+ assert.strictEqual(output.valid, true, `should be valid: ${JSON.stringify(output)}`);
+ assert.strictEqual(output.found, 1, `should find 1 file: ${JSON.stringify(output)}`);
+ });
+
+ test('reports missing for nonexistent referenced files', () => {
+ const filePath = path.join(tmpDir, '.planning', 'phases', '01-test', 'doc.md');
+ fs.writeFileSync(filePath, '@src/missing.js\n');
+
+ const result = runGsdTools('verify references .planning/phases/01-test/doc.md', tmpDir);
+ assert.ok(result.success, `Command failed: ${result.error}`);
+
+ const output = JSON.parse(result.output);
+ assert.strictEqual(output.valid, false, 'should be invalid');
+ assert.ok(
+ output.missing.includes('src/missing.js'),
+ `Expected missing to include "src/missing.js": ${JSON.stringify(output.missing)}`
+ );
+ });
+
+ test('detects backtick file paths', () => {
+ fs.writeFileSync(path.join(tmpDir, 'src', 'utils', 'helper.js'), 'module.exports = {};\n');
+ const filePath = path.join(tmpDir, '.planning', 'phases', '01-test', 'doc.md');
+ fs.writeFileSync(filePath, 'See `src/utils/helper.js` for details.\n');
+
+ const result = runGsdTools('verify references .planning/phases/01-test/doc.md', tmpDir);
+ assert.ok(result.success, `Command failed: ${result.error}`);
+
+ const output = JSON.parse(result.output);
+ assert.ok(output.found >= 1, `Expected at least 1 found, got ${output.found}`);
+ });
+
+ test('skips backtick template expressions', () => {
+ // Template expressions like ${variable} in backtick paths are skipped
+ // @-refs with http are processed but not found on disk
+ const filePath = path.join(tmpDir, '.planning', 'phases', '01-test', 'doc.md');
+ fs.writeFileSync(filePath, '`${variable}/path/file.js`\n');
+
+ const result = runGsdTools('verify references .planning/phases/01-test/doc.md', tmpDir);
+ assert.ok(result.success, `Command failed: ${result.error}`);
+
+ const output = JSON.parse(result.output);
+ // Template expression is skipped entirely — total should be 0
+ assert.strictEqual(output.total, 0, `Expected total 0 (template skipped): ${JSON.stringify(output)}`);
+ });
+
+ test('returns error for nonexistent file', () => {
+ const result = runGsdTools('verify references .planning/phases/01-test/nonexistent.md', tmpDir);
+ assert.ok(result.success, `Command failed: ${result.error}`);
+
+ const output = JSON.parse(result.output);
+ assert.ok(output.error, `Expected error field: ${JSON.stringify(output)}`);
+ });
+});
+
+// ─────────────────────────────────────────────────────────────────────────────
+// verify commits command
+// ─────────────────────────────────────────────────────────────────────────────
+
+describe('verify commits command', () => {
+ let tmpDir;
+
+ beforeEach(() => {
+ tmpDir = createTempGitProject();
+ });
+
+ afterEach(() => {
+ cleanup(tmpDir);
+ });
+
+ test('validates real commit hashes', () => {
+ const hash = execSync('git rev-parse --short HEAD', { cwd: tmpDir, encoding: 'utf-8' }).trim();
+
+ const result = runGsdTools(`verify commits ${hash}`, tmpDir);
+ assert.ok(result.success, `Command failed: ${result.error}`);
+
+ const output = JSON.parse(result.output);
+ assert.strictEqual(output.all_valid, true, `Expected all_valid true: ${JSON.stringify(output)}`);
+ assert.ok(output.valid.includes(hash), `Expected valid to include ${hash}: ${JSON.stringify(output.valid)}`);
+ });
+
+ test('reports invalid for fake hashes', () => {
+ const result = runGsdTools('verify commits abcdef1234567', tmpDir);
+ assert.ok(result.success, `Command failed: ${result.error}`);
+
+ const output = JSON.parse(result.output);
+ assert.strictEqual(output.all_valid, false, `Expected all_valid false: ${JSON.stringify(output)}`);
+ assert.ok(
+ output.invalid.includes('abcdef1234567'),
+ `Expected invalid to include "abcdef1234567": ${JSON.stringify(output.invalid)}`
+ );
+ });
+
+ test('handles mixed valid and invalid hashes', () => {
+ const hash = execSync('git rev-parse --short HEAD', { cwd: tmpDir, encoding: 'utf-8' }).trim();
+
+ const result = runGsdTools(`verify commits ${hash} abcdef1234567`, tmpDir);
+ assert.ok(result.success, `Command failed: ${result.error}`);
+
+ const output = JSON.parse(result.output);
+ assert.strictEqual(output.valid.length, 1, `Expected 1 valid: ${JSON.stringify(output)}`);
+ assert.strictEqual(output.invalid.length, 1, `Expected 1 invalid: ${JSON.stringify(output)}`);
+ assert.strictEqual(output.all_valid, false, `Expected all_valid false: ${JSON.stringify(output)}`);
+ });
+});
+
+// ─────────────────────────────────────────────────────────────────────────────
+// verify artifacts command
+// ─────────────────────────────────────────────────────────────────────────────
+
+describe('verify artifacts command', () => {
+ let tmpDir;
+
+ beforeEach(() => {
+ tmpDir = createTempProject();
+ fs.mkdirSync(path.join(tmpDir, '.planning', 'phases', '01-test'), { recursive: true });
+ fs.mkdirSync(path.join(tmpDir, 'src'), { recursive: true });
+ });
+
+ afterEach(() => {
+ cleanup(tmpDir);
+ });
+
+ function writePlanWithArtifacts(tmpDir, artifactsYaml) {
+ // parseMustHavesBlock expects 4-space indent for block name, 6-space for items, 8-space for keys
+ const content = [
+ '---',
+ 'phase: 01-test',
+ 'plan: 01',
+ 'type: execute',
+ 'wave: 1',
+ 'depends_on: []',
+ 'files_modified: [src/app.js]',
+ 'autonomous: true',
+ 'must_haves:',
+ ' artifacts:',
+ ...artifactsYaml.map(line => ` ${line}`),
+ '---',
+ '',
+ '',
+ '',
+ ' Task 1: Do thing',
+ ' src/app.js',
+ ' Do it',
+ ' echo ok',
+ ' Done',
+ '',
+ '',
+ ].join('\n');
+ const planPath = path.join(tmpDir, '.planning', 'phases', '01-test', '01-01-PLAN.md');
+ fs.writeFileSync(planPath, content);
+ }
+
+ test('passes when all artifacts exist and match criteria', () => {
+ writePlanWithArtifacts(tmpDir, [
+ '- path: "src/app.js"',
+ ' min_lines: 2',
+ ' contains: "export"',
+ ]);
+ fs.writeFileSync(path.join(tmpDir, 'src', 'app.js'), 'const x = 1;\nexport default x;\nconst y = 2;\n');
+
+ const result = runGsdTools('verify artifacts .planning/phases/01-test/01-01-PLAN.md', tmpDir);
+ assert.ok(result.success, `Command failed: ${result.error}`);
+
+ const output = JSON.parse(result.output);
+ assert.strictEqual(output.all_passed, true, `Expected all_passed true: ${JSON.stringify(output)}`);
+ });
+
+ test('reports missing artifact file', () => {
+ writePlanWithArtifacts(tmpDir, [
+ '- path: "src/nonexistent.js"',
+ ]);
+
+ const result = runGsdTools('verify artifacts .planning/phases/01-test/01-01-PLAN.md', tmpDir);
+ assert.ok(result.success, `Command failed: ${result.error}`);
+
+ const output = JSON.parse(result.output);
+ assert.strictEqual(output.all_passed, false, 'Expected all_passed false');
+ assert.ok(
+ output.artifacts[0].issues.some(i => i.includes('File not found')),
+ `Expected "File not found" in issues: ${JSON.stringify(output.artifacts[0].issues)}`
+ );
+ });
+
+ test('reports insufficient line count', () => {
+ writePlanWithArtifacts(tmpDir, [
+ '- path: "src/app.js"',
+ ' min_lines: 10',
+ ]);
+ fs.writeFileSync(path.join(tmpDir, 'src', 'app.js'), 'const x = 1;\n');
+
+ const result = runGsdTools('verify artifacts .planning/phases/01-test/01-01-PLAN.md', tmpDir);
+ assert.ok(result.success, `Command failed: ${result.error}`);
+
+ const output = JSON.parse(result.output);
+ assert.strictEqual(output.all_passed, false, 'Expected all_passed false');
+ assert.ok(
+ output.artifacts[0].issues.some(i => i.includes('Only') && i.includes('lines, need 10')),
+ `Expected line count issue: ${JSON.stringify(output.artifacts[0].issues)}`
+ );
+ });
+
+ test('reports missing pattern', () => {
+ writePlanWithArtifacts(tmpDir, [
+ '- path: "src/app.js"',
+ ' contains: "module.exports"',
+ ]);
+ fs.writeFileSync(path.join(tmpDir, 'src', 'app.js'), 'const x = 1;\n');
+
+ const result = runGsdTools('verify artifacts .planning/phases/01-test/01-01-PLAN.md', tmpDir);
+ assert.ok(result.success, `Command failed: ${result.error}`);
+
+ const output = JSON.parse(result.output);
+ assert.strictEqual(output.all_passed, false, 'Expected all_passed false');
+ assert.ok(
+ output.artifacts[0].issues.some(i => i.includes('Missing pattern')),
+ `Expected "Missing pattern" in issues: ${JSON.stringify(output.artifacts[0].issues)}`
+ );
+ });
+
+ test('reports missing export', () => {
+ writePlanWithArtifacts(tmpDir, [
+ '- path: "src/app.js"',
+ ' exports:',
+ ' - GET',
+ ]);
+ fs.writeFileSync(path.join(tmpDir, 'src', 'app.js'), 'const x = 1;\nexport const POST = () => {};\n');
+
+ const result = runGsdTools('verify artifacts .planning/phases/01-test/01-01-PLAN.md', tmpDir);
+ assert.ok(result.success, `Command failed: ${result.error}`);
+
+ const output = JSON.parse(result.output);
+ assert.strictEqual(output.all_passed, false, 'Expected all_passed false');
+ assert.ok(
+ output.artifacts[0].issues.some(i => i.includes('Missing export')),
+ `Expected "Missing export" in issues: ${JSON.stringify(output.artifacts[0].issues)}`
+ );
+ });
+
+ test('returns error when no artifacts in frontmatter', () => {
+ const content = [
+ '---',
+ 'phase: 01-test',
+ 'plan: 01',
+ 'type: execute',
+ 'wave: 1',
+ 'depends_on: []',
+ 'files_modified: [src/app.js]',
+ 'autonomous: true',
+ 'must_haves:',
+ ' truths:',
+ ' - "something is true"',
+ '---',
+ '',
+ '',
+ ].join('\n');
+ const planPath = path.join(tmpDir, '.planning', 'phases', '01-test', '01-01-PLAN.md');
+ fs.writeFileSync(planPath, content);
+
+ const result = runGsdTools('verify artifacts .planning/phases/01-test/01-01-PLAN.md', tmpDir);
+ assert.ok(result.success, `Command failed: ${result.error}`);
+
+ const output = JSON.parse(result.output);
+ assert.ok(output.error, `Expected error field: ${JSON.stringify(output)}`);
+ assert.ok(
+ output.error.includes('No must_haves.artifacts'),
+ `Expected "No must_haves.artifacts" in error: ${output.error}`
+ );
+ });
+});
+
+// ─────────────────────────────────────────────────────────────────────────────
+// verify key-links command
+// ─────────────────────────────────────────────────────────────────────────────
+
+describe('verify key-links command', () => {
+ let tmpDir;
+
+ beforeEach(() => {
+ tmpDir = createTempProject();
+ fs.mkdirSync(path.join(tmpDir, '.planning', 'phases', '01-test'), { recursive: true });
+ fs.mkdirSync(path.join(tmpDir, 'src'), { recursive: true });
+ });
+
+ afterEach(() => {
+ cleanup(tmpDir);
+ });
+
+ function writePlanWithKeyLinks(tmpDir, keyLinksYaml) {
+ // parseMustHavesBlock expects 4-space indent for block name, 6-space for items, 8-space for keys
+ const content = [
+ '---',
+ 'phase: 01-test',
+ 'plan: 01',
+ 'type: execute',
+ 'wave: 1',
+ 'depends_on: []',
+ 'files_modified: [src/a.js]',
+ 'autonomous: true',
+ 'must_haves:',
+ ' key_links:',
+ ...keyLinksYaml.map(line => ` ${line}`),
+ '---',
+ '',
+ '',
+ '',
+ ' Task 1: Do thing',
+ ' src/a.js',
+ ' Do it',
+ ' echo ok',
+ ' Done',
+ '',
+ '',
+ ].join('\n');
+ const planPath = path.join(tmpDir, '.planning', 'phases', '01-test', '01-01-PLAN.md');
+ fs.writeFileSync(planPath, content);
+ }
+
+ test('verifies link when pattern found in source', () => {
+ writePlanWithKeyLinks(tmpDir, [
+ '- from: "src/a.js"',
+ ' to: "src/b.js"',
+ ' pattern: "import.*b"',
+ ]);
+ fs.writeFileSync(path.join(tmpDir, 'src', 'a.js'), "import { x } from './b';\n");
+ fs.writeFileSync(path.join(tmpDir, 'src', 'b.js'), 'exports.x = 1;\n');
+
+ const result = runGsdTools('verify key-links .planning/phases/01-test/01-01-PLAN.md', tmpDir);
+ assert.ok(result.success, `Command failed: ${result.error}`);
+
+ const output = JSON.parse(result.output);
+ assert.strictEqual(output.all_verified, true, `Expected all_verified true: ${JSON.stringify(output)}`);
+ });
+
+ test('verifies link when pattern found in target', () => {
+ writePlanWithKeyLinks(tmpDir, [
+ '- from: "src/a.js"',
+ ' to: "src/b.js"',
+ ' pattern: "exports\\.targetFunc"',
+ ]);
+ // pattern NOT in source, but found in target
+ fs.writeFileSync(path.join(tmpDir, 'src', 'a.js'), 'const x = 1;\n');
+ fs.writeFileSync(path.join(tmpDir, 'src', 'b.js'), 'exports.targetFunc = () => {};\n');
+
+ const result = runGsdTools('verify key-links .planning/phases/01-test/01-01-PLAN.md', tmpDir);
+ assert.ok(result.success, `Command failed: ${result.error}`);
+
+ const output = JSON.parse(result.output);
+ assert.strictEqual(output.all_verified, true, `Expected verified via target: ${JSON.stringify(output)}`);
+ assert.ok(
+ output.links[0].detail.includes('target'),
+ `Expected detail about target: ${output.links[0].detail}`
+ );
+ });
+
+ test('fails when pattern not found in source or target', () => {
+ writePlanWithKeyLinks(tmpDir, [
+ '- from: "src/a.js"',
+ ' to: "src/b.js"',
+ ' pattern: "missingPattern"',
+ ]);
+ fs.writeFileSync(path.join(tmpDir, 'src', 'a.js'), 'const x = 1;\n');
+ fs.writeFileSync(path.join(tmpDir, 'src', 'b.js'), 'const y = 2;\n');
+
+ const result = runGsdTools('verify key-links .planning/phases/01-test/01-01-PLAN.md', tmpDir);
+ assert.ok(result.success, `Command failed: ${result.error}`);
+
+ const output = JSON.parse(result.output);
+ assert.strictEqual(output.all_verified, false, `Expected all_verified false: ${JSON.stringify(output)}`);
+ assert.strictEqual(output.links[0].verified, false, 'link should not be verified');
+ });
+
+ test('verifies link without pattern using string inclusion', () => {
+ writePlanWithKeyLinks(tmpDir, [
+ '- from: "src/a.js"',
+ ' to: "src/b.js"',
+ ]);
+ // source file contains the 'to' value as a string
+ fs.writeFileSync(path.join(tmpDir, 'src', 'a.js'), "const b = require('./src/b.js');\n");
+ fs.writeFileSync(path.join(tmpDir, 'src', 'b.js'), 'module.exports = {};\n');
+
+ const result = runGsdTools('verify key-links .planning/phases/01-test/01-01-PLAN.md', tmpDir);
+ assert.ok(result.success, `Command failed: ${result.error}`);
+
+ const output = JSON.parse(result.output);
+ assert.strictEqual(output.all_verified, true, `Expected all_verified true: ${JSON.stringify(output)}`);
+ assert.ok(
+ output.links[0].detail.includes('Target referenced in source'),
+ `Expected "Target referenced in source" in detail: ${output.links[0].detail}`
+ );
+ });
+
+ test('reports source file not found', () => {
+ writePlanWithKeyLinks(tmpDir, [
+ '- from: "src/nonexistent.js"',
+ ' to: "src/b.js"',
+ ' pattern: "something"',
+ ]);
+ fs.writeFileSync(path.join(tmpDir, 'src', 'b.js'), 'module.exports = {};\n');
+
+ const result = runGsdTools('verify key-links .planning/phases/01-test/01-01-PLAN.md', tmpDir);
+ assert.ok(result.success, `Command failed: ${result.error}`);
+
+ const output = JSON.parse(result.output);
+ assert.ok(
+ output.links[0].detail.includes('Source file not found'),
+ `Expected "Source file not found" in detail: ${output.links[0].detail}`
+ );
+ });
+
+ test('returns error when no key_links in frontmatter', () => {
+ const content = [
+ '---',
+ 'phase: 01-test',
+ 'plan: 01',
+ 'type: execute',
+ 'wave: 1',
+ 'depends_on: []',
+ 'files_modified: [src/a.js]',
+ 'autonomous: true',
+ 'must_haves:',
+ ' truths:',
+ ' - "something is true"',
+ '---',
+ '',
+ '',
+ ].join('\n');
+ const planPath = path.join(tmpDir, '.planning', 'phases', '01-test', '01-01-PLAN.md');
+ fs.writeFileSync(planPath, content);
+
+ const result = runGsdTools('verify key-links .planning/phases/01-test/01-01-PLAN.md', tmpDir);
+ assert.ok(result.success, `Command failed: ${result.error}`);
+
+ const output = JSON.parse(result.output);
+ assert.ok(output.error, `Expected error field: ${JSON.stringify(output)}`);
+ assert.ok(
+ output.error.includes('No must_haves.key_links'),
+ `Expected "No must_haves.key_links" in error: ${output.error}`
+ );
+ });
+});