Merge branch 'next' into fix/2009-load-failed-capability-injects-blocking-

This commit is contained in:
Tom Boucher
2026-07-07 23:12:09 -04:00
committed by GitHub
64 changed files with 3060 additions and 365 deletions

View File

@@ -0,0 +1,273 @@
'use strict';
/**
* E2E capability-wiring tests for the API-coverage gate (#1562).
*
* Drives the real CLI subprocess (`loop render-hooks verify:pre` and
* `check api-coverage.verify-pre`) against temp projects to prove:
* - the gate is data-driven (activates/deactivates by config) — acceptance #5
* - the seal contract (block / pass) — acceptance #1, #2, #4
* - the matrix persists on disk and is read at seal time — acceptance #6
*
* CONTENT/E2E only: every test drives a real CLI subprocess. No readFileSync
* source-grep. Genuine assertions: each case asserts the SPECIFIC differing
* value (block true/false, capId presence), not a count.
*/
const { describe, test, afterEach } = require('node:test');
const assert = require('node:assert/strict');
const fs = require('node:fs');
const os = require('node:os');
const path = require('node:path');
const { execFileSync } = require('node:child_process');
const { cleanup } = require('./helpers.cjs');
const TOOLS_PATH = path.join(__dirname, '..', 'gsd-core', 'bin', 'gsd-tools.cjs');
const TEST_ENV_BASE = {
GSD_SESSION_KEY: '',
CODEX_THREAD_ID: '',
CLAUDE_SESSION_ID: '',
CLAUDE_CODE_SSE_PORT: '',
OPENCODE_SESSION_ID: '',
GEMINI_SESSION_ID: '',
CURSOR_SESSION_ID: '',
WINDSURF_SESSION_ID: '',
TERM_SESSION: '',
WT_SESSION: '',
TMUX_PANE: '',
ZELLIJ_SESSION_NAME: '',
TTY: '',
SSH_TTY: '',
};
function runTools(args, cwd) {
const argv = Array.isArray(args)
? args
: (args.match(/(?:[^\s"']+|"[^"]*"|'[^']*')+/g) || [])
.map((t) => t.replace(/"([^"]*)"/g, '$1').replace(/'([^']*)'/g, '$1'));
try {
const stdout = execFileSync(process.execPath, [TOOLS_PATH, ...argv], {
cwd,
encoding: 'utf-8',
env: { ...process.env, ...TEST_ENV_BASE },
timeout: 60000,
});
return { success: true, output: stdout.trim(), exitCode: 0, error: '' };
} catch (err) {
return {
success: false,
output: err.stdout?.toString().trim() || '',
error: err.stderr?.toString().trim() || err.message,
exitCode: err.status ?? 1,
};
}
}
function makeProject(workflow) {
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-apicov-'));
fs.mkdirSync(path.join(tmpDir, '.planning'), { recursive: true });
fs.mkdirSync(path.join(tmpDir, '.planning', 'phases'), { recursive: true });
fs.writeFileSync(
path.join(tmpDir, '.planning', 'config.json'),
JSON.stringify({ workflow }),
'utf8'
);
return tmpDir;
}
function makePhaseDir(projectDir, phaseSlug) {
const dir = path.join(projectDir, '.planning', 'phases', phaseSlug);
fs.mkdirSync(dir, { recursive: true });
return dir;
}
function writePlan(phaseDir, planFile, body) {
fs.writeFileSync(path.join(phaseDir, planFile), body, 'utf8');
}
function writeCoverage(phaseDir, body) {
fs.writeFileSync(path.join(phaseDir, 'COVERAGE.md'), body, 'utf8');
}
function verifyPreHooks(cwd) {
const result = runTools('loop render-hooks verify:pre --raw', cwd);
assert.ok(result.success, `render-hooks verify:pre should succeed. stderr: ${result.error}`);
const envelope = JSON.parse(result.output);
assert.strictEqual(envelope.point, 'verify:pre', 'point field must be verify:pre');
assert.ok(Array.isArray(envelope.activeHooks), 'activeHooks must be an array');
return envelope;
}
function findCap(envelope, capId) {
return envelope.activeHooks.find((h) => h.capId === capId) || null;
}
function runGate(cwd, phaseDir) {
return runTools(['check', 'api-coverage.verify-pre', phaseDir, '--raw'], cwd);
}
// ─── Capability wiring: data-driven activation (acceptance #5) ───────────────
describe('api-coverage verify:pre gate — capability wiring (#1562 acceptance #5)', () => {
let tmpDir;
afterEach(() => { if (tmpDir) { cleanup(tmpDir); tmpDir = null; } });
test('gate is ACTIVE when workflow.api_coverage_gate is true', () => {
tmpDir = makeProject({ api_coverage_gate: true });
const env = verifyPreHooks(tmpDir);
const hook = findCap(env, 'ai-integration');
assert.ok(hook, 'ai-integration gate must register at verify:pre when enabled');
assert.strictEqual(hook.kind, 'gate');
assert.strictEqual(hook.blocking, true);
assert.strictEqual(hook.check.query, 'api-coverage.verify-pre');
});
test('gate is ABSENT when workflow.api_coverage_gate is false', () => {
tmpDir = makeProject({ api_coverage_gate: false });
const env = verifyPreHooks(tmpDir);
assert.strictEqual(findCap(env, 'ai-integration'), null, 'gate must not register when disabled');
});
test('gate is ACTIVE by default when the key is absent (opt-out, not opt-in)', () => {
tmpDir = makeProject({});
const env = verifyPreHooks(tmpDir);
assert.ok(findCap(env, 'ai-integration'), 'gate must default ON (full-coverage-by-default)');
});
});
// ─── Seal contract: block / pass (acceptance #1, #2, #4, #6) ──────────────────
describe('api-coverage.verify-pre — seal contract (#1562 acceptance #1,#2,#4,#6)', () => {
let tmpDir;
let phaseDir;
afterEach(() => { if (tmpDir) { cleanup(tmpDir); tmpDir = null; } });
function fresh() {
tmpDir = makeProject({ api_coverage_gate: true });
phaseDir = makePhaseDir(tmpDir, '01-pay');
return phaseDir;
}
test('#1 API phase without a matrix → BLOCKS the seal', () => {
fresh();
writePlan(phaseDir, '01-PLAN.md', '# Plan\nIntegrate the Stripe API for payment processing.');
const r = runGate(tmpDir, phaseDir);
assert.ok(r.success, `gate should succeed (JSON). stderr: ${r.error}`);
const j = JSON.parse(r.output);
assert.strictEqual(j.block, true, 'must block when API integration has no matrix');
assert.strictEqual(j.detected, true);
assert.strictEqual(j.coverage_present, false);
});
test('#4 non-API phase without a matrix → does NOT block', () => {
fresh();
writePlan(phaseDir, '01-PLAN.md', '# Plan\nRefactor the auth helper to use bcrypt.');
const r = runGate(tmpDir, phaseDir);
assert.ok(r.success, `gate should succeed. stderr: ${r.error}`);
const j = JSON.parse(r.output);
assert.strictEqual(j.block, false, 'must not block a non-API phase');
assert.strictEqual(j.detected, false);
});
test('#1/#6 API phase WITH a valid matrix → passes (matrix persists on disk)', () => {
fresh();
writePlan(phaseDir, '01-PLAN.md', '# Plan\nIntegrate the Stripe API for payment processing.');
writeCoverage(
phaseDir,
'| capability | decision | reason |\n|---|---|---|\n' +
'| charge | INTEGRATE | |\n| refund | OPT-OUT | not needed yet |\n'
);
const r = runGate(tmpDir, phaseDir);
assert.ok(r.success, `gate should succeed. stderr: ${r.error}`);
const j = JSON.parse(r.output);
assert.strictEqual(j.block, false);
assert.strictEqual(j.coverage_present, true);
assert.strictEqual(j.counts.surface, 2);
assert.strictEqual(j.counts.optout, 1);
});
test('#2 OPT-OUT without a reason → BLOCKS (un-decided hole)', () => {
fresh();
writePlan(phaseDir, '01-PLAN.md', '# Plan\nIntegrate the Stripe API.');
writeCoverage(
phaseDir,
'| capability | decision | reason |\n|---|---|---|\n| refund | OPT-OUT | |\n'
);
const r = runGate(tmpDir, phaseDir);
const j = JSON.parse(r.output);
assert.strictEqual(j.block, true, 'opt-out without reason must block');
assert.ok(j.errors.some((e) => /missing reason/i.test(e)));
});
test('#2 empty matrix → BLOCKS (surface must be enumerated)', () => {
fresh();
writePlan(phaseDir, '01-PLAN.md', '# Plan\nIntegrate the Stripe API.');
writeCoverage(phaseDir, '| capability | decision | reason |\n|---|---|---|\n');
const r = runGate(tmpDir, phaseDir);
const j = JSON.parse(r.output);
assert.strictEqual(j.block, true);
assert.ok(j.errors.some((e) => /empty/i.test(e)));
});
test('#3 a second platform with full-coverage baseline is accepted (no asymmetry)', () => {
fresh();
writePlan(phaseDir, '01-PLAN.md', '# Plan\nAdd a YouTube SDK as a second media platform.');
// Full-coverage baseline for the second platform: every capability decided.
writeCoverage(
phaseDir,
'| capability | decision | reason |\n|---|---|---|\n' +
'| search | INTEGRATE | |\n| playlists | INTEGRATE | |\n| skip | INTEGRATE | |\n'
);
const r = runGate(tmpDir, phaseDir);
const j = JSON.parse(r.output);
assert.strictEqual(j.block, false, 'a fully-decided second platform seals clean');
assert.strictEqual(j.counts.surface, 3);
});
test('JSON-fenced matrix is accepted (machine-generated form)', () => {
fresh();
writePlan(phaseDir, '01-PLAN.md', '# Plan\nIntegrate the Stripe API.');
writeCoverage(
phaseDir,
'```coverage\n[{"capability":"charge","decision":"INTEGRATE","reason":""}]\n```\n'
);
const r = runGate(tmpDir, phaseDir);
const j = JSON.parse(r.output);
assert.strictEqual(j.block, false);
assert.strictEqual(j.counts.surface, 1);
});
// ── Security (#1562 security review S1/S2): the phase arg is taken only as a
// token resolved under .planning/phases/. Traversal / unresolvable args must
// NOT read files outside the phase dir, and — since the phases tree exists —
// must fail CLOSED (a blocking gate must not silently bypass on a bad arg).
test('path-traversal arg is contained and fails CLOSED (phases tree exists)', () => {
fresh(); // creates .planning/phases/01-pay
const r = runTools(['check', 'api-coverage.verify-pre', '../../etc', '--raw'], tmpDir);
assert.ok(r.success, `gate should succeed (JSON). stderr: ${r.error}`);
const j = JSON.parse(r.output);
assert.strictEqual(j.block, true, 'unresolvable phase under an existing phases tree must block');
assert.strictEqual(j.phase_lookup_failed, true);
});
test('no .planning/phases at all → fail-open (genuine non-GSD project)', () => {
// A project with .planning/config.json but no phases directory.
const noPhases = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-apicov-nophase-'));
try {
fs.mkdirSync(path.join(noPhases, '.planning'), { recursive: true });
fs.writeFileSync(
path.join(noPhases, '.planning', 'config.json'),
JSON.stringify({ workflow: { api_coverage_gate: true } }),
'utf8'
);
const r = runTools(['check', 'api-coverage.verify-pre', '01-pay', '--raw'], noPhases);
assert.ok(r.success);
const j = JSON.parse(r.output);
assert.strictEqual(j.block, false, 'no phases tree → pass (not a GSD project)');
} finally {
cleanup(noPhases);
}
});
});

410
tests/api-coverage.test.cjs Normal file
View File

@@ -0,0 +1,410 @@
/**
* Tests for the API-coverage detector + matrix validator (#1562).
*
* Two pure functions under test, both returning typed IR (asserted, never
* text-matched on prose):
* - detectApiIntegration(text) -> { detected, signals, terms }
* - validateCoverageMatrix(text) -> { valid, errors, counts }
*
* Acceptance-criterion mapping:
* #2 (opt-out needs reason; un-enumerated blocks) → validateCoverageMatrix suite
* #4 (non-API phases unaffected, low false-positive) → false-positive suite
* Matrix parse/render bijectivity → fast-check property
*/
const { describe, test } = require('node:test');
const assert = require('node:assert/strict');
const { spawnSync } = require('node:child_process');
const path = require('node:path');
const fc = require('fast-check');
const MODULE_PATH = path.join(__dirname, '..', 'gsd-core', 'bin', 'lib', 'api-coverage.cjs');
describe('detectApiIntegration — pure detector (#1562)', () => {
let mod;
try {
mod = require(MODULE_PATH);
} catch (err) {
throw new Error(
`Could not require ${MODULE_PATH}. Run "npm run build:lib" first. Underlying: ${err.message}`
);
}
const { detectApiIntegration, DEFAULT_API_COVERAGE_TERMS } = mod;
test('result shape — always carries detected, signals[], terms', () => {
const r = detectApiIntegration('refactor the login function');
assert.strictEqual(r.detected, false);
assert(Array.isArray(r.signals));
assert.strictEqual(r.signals.length, 0);
assert.ok(r.terms && Array.isArray(r.terms.verbs));
assert.ok(Array.isArray(r.terms.nouns));
});
test('non-string input degrades to {detected:false} without throwing', () => {
assert.strictEqual(detectApiIntegration(undefined).detected, false);
assert.strictEqual(detectApiIntegration(null).detected, false);
assert.strictEqual(detectApiIntegration(42).detected, false);
assert.strictEqual(detectApiIntegration({}).detected, false);
});
test('empty / whitespace-only input does not fire', () => {
assert.strictEqual(detectApiIntegration('').detected, false);
assert.strictEqual(detectApiIntegration(' \n\t ').detected, false);
});
test('terms echo is the effective set actually used', () => {
const r = detectApiIntegration('nothing relevant here');
assert.deepStrictEqual(r.terms.verbs, [...DEFAULT_API_COVERAGE_TERMS.verbs]);
assert.deepStrictEqual(r.terms.nouns, [...DEFAULT_API_COVERAGE_TERMS.nouns]);
});
test('terms override — explicit verbs+nouns replace defaults', () => {
const r = detectApiIntegration('xyzzy the frobninator', { verbs: ['xyzzy'], nouns: ['frobninator'] });
assert.deepStrictEqual(r.terms.verbs, ['xyzzy']);
assert.deepStrictEqual(r.terms.nouns, ['frobninator']);
assert.strictEqual(r.detected, true);
});
// ── Positive: compound verb+noun (acceptance #1 trigger) ─────────────────
for (const scope of [
'Integrate the Stripe API for payment processing',
'Wrap the GitHub GraphQL API for issue triage',
'Connect to the SendGrid REST endpoint for transactional email',
'Consume the billing service over gRPC',
'Wire up the Slack webhook for deploy notifications',
'Onboard the Twilio SDK for SMS',
'integrate oauth for login',
]) {
test(`POSITIVE fires on: "${scope}"`, () => {
const r = detectApiIntegration(scope);
assert.strictEqual(r.detected, true, `expected detection for: ${scope}`);
assert.ok(r.signals.length > 0);
assert.ok(r.signals.every((s) => s.snippet.length > 0));
});
}
// ── Positive: explicit <Service> API|SDK surface (no verb needed) ────────
for (const scope of ['Add a Spotify API client', 'Ship the Notion SDK helper']) {
test(`POSITIVE (surface) fires on: "${scope}"`, () => {
const r = detectApiIntegration(scope);
assert.strictEqual(r.detected, true);
assert.ok(r.signals.some((s) => s.verb === '(surface)'));
});
}
// ── Negative: false-positive guards (acceptance #4 — the crux) ───────────
// Each of these is a phase that is NOT an external-API integration. The
// detector must stay silent. The "public API of UserController" case is the
// canonical FP trap: "api" is present but there is no integration verb.
for (const [label, scope] of [
['internal API mention', 'The public API of the UserController should accept pagination params'],
['refactor', 'Refactor the authentication module to use bcrypt'],
['feature toggle', 'Add a dark mode toggle to the settings page'],
['bug fix', 'Fix the off-by-one error in the pagination helper'],
['docs', 'Update the README to document the config options'],
['internal client code', 'Add a client-side helper to debounce input'],
['bare noun no verb', 'We expose a REST-ish JSON shape already'],
['bare verb no noun', 'We will integrate the new design system tokens'],
]) {
test(`NEGATIVE does not fire (${label}): "${scope}"`, () => {
const r = detectApiIntegration(scope);
assert.strictEqual(r.detected, false, `unexpected detection for [${label}]: ${scope}`);
});
}
test('fenced code blocks are stripped — trigger inside a code fence does not fire', () => {
const scope = [
'Refactor the helpers.',
'',
'```bash',
'# integrate the Stripe API (example command in docs)',
'```',
'',
'No integration in this phase.',
].join('\n');
assert.strictEqual(detectApiIntegration(scope).detected, false);
});
// ── H1 fix (#1562 code review): capitalized sentence starters must not fire
// the <Service> API/SDK surface rule. These are common English, not services.
for (const [label, scope] of [
['The API', 'The API documentation needs updating.'],
['An SDK', 'An SDK is already present in the repo.'],
['Our REST', 'Our REST endpoints return JSON.'],
['This GraphQL', 'This GraphQL schema is internal.'],
['New API', 'New API surface was added by the refactor.'],
]) {
test(`NEGATIVE (stopword) does not fire (${label}): "${scope}"`, () => {
assert.strictEqual(detectApiIntegration(scope).detected, false);
});
}
test('compound signal fires when verb and noun are on the same line only', () => {
const sameLine = 'Phase A integrates things.\nLater we mention an api.';
const splitLine = 'Phase A integrates things.\nLater we mention an api here too.';
assert.strictEqual(detectApiIntegration(sameLine).detected, false);
assert.strictEqual(detectApiIntegration(splitLine).detected, false);
assert.strictEqual(detectApiIntegration('integrates the api').detected, true);
});
});
// ──────────────────────────────────────────────────────────────────────────────
// Matrix parse / validate / render
// ──────────────────────────────────────────────────────────────────────────────
describe('coverage matrix — parse / validate (#1562 acceptance #2)', () => {
let mod;
try {
mod = require(MODULE_PATH);
} catch (err) {
throw new Error(`Could not require ${MODULE_PATH}. Run "npm run build:lib". Underlying: ${err.message}`);
}
const { parseCoverageMatrix, validateCoverageMatrix, renderCoverageMatrix } = mod;
test('parse markdown table — header + 2 rows', () => {
const md = [
'| capability | decision | reason |',
'|---|---|---|',
'| search | INTEGRATE | |',
'| playlists | OPT-OUT | not needed yet |',
].join('\n');
const p = parseCoverageMatrix(md);
assert.strictEqual(p.format, 'table');
assert.strictEqual(p.rows.length, 2);
assert.strictEqual(p.rows[0].capability, 'search');
assert.strictEqual(p.rows[0].decision, 'INTEGRATE');
assert.strictEqual(p.rows[1].decision, 'OPT-OUT');
assert.strictEqual(p.rows[1].reason, 'not needed yet');
assert.strictEqual(p.errors.length, 0);
});
test('parse fenced ```coverage JSON block', () => {
const md = [
'Some prose.',
'',
'```coverage',
'[{"capability":"search","decision":"INTEGRATE","reason":""},',
' {"capability":"skip","decision":"OPT-OUT","reason":"out of scope"}]',
'```',
].join('\n');
const p = parseCoverageMatrix(md);
assert.strictEqual(p.format, 'json');
assert.strictEqual(p.rows.length, 2);
assert.strictEqual(p.errors.length, 0);
});
test('parse empty / non-matrix input → format none, no rows, no errors', () => {
const p = parseCoverageMatrix('# Notes\n\nNothing here.');
assert.strictEqual(p.format, 'none');
assert.strictEqual(p.rows.length, 0);
assert.strictEqual(p.errors.length, 0);
});
test('parse non-string → empty result, no throw', () => {
const p = parseCoverageMatrix(undefined);
assert.strictEqual(p.rows.length, 0);
});
test('parse rejects malformed fenced JSON with an error', () => {
const md = '```coverage\n{not json}\n```';
const p = parseCoverageMatrix(md);
assert.strictEqual(p.format, 'json');
assert.ok(p.errors.length > 0);
assert.strictEqual(p.rows.length, 0);
});
// ── validate: boundaries 0 / 1 / 2 rows (limit-1, limit, limit+1) ────────
test('validate — empty matrix is invalid (acceptance #1: surface must be enumerated)', () => {
const v = validateCoverageMatrix('| capability | decision | reason |\n|---|---|---|');
assert.strictEqual(v.valid, false);
assert.ok(v.errors.some((e) => /empty/i.test(e)));
assert.strictEqual(v.counts.surface, 0);
});
test('validate — single INTEGRATE row is valid (limit)', () => {
const md = '| capability | decision | reason |\n|---|---|---|\n| search | INTEGRATE | |';
const v = validateCoverageMatrix(md);
assert.strictEqual(v.valid, true);
assert.strictEqual(v.counts.surface, 1);
assert.strictEqual(v.counts.integrate, 1);
assert.strictEqual(v.counts.optout, 0);
});
test('validate — two rows valid (limit+1)', () => {
const md = [
'| capability | decision | reason |',
'|---|---|---|',
'| search | INTEGRATE | |',
'| playlists | OPT-OUT | not needed |',
].join('\n');
const v = validateCoverageMatrix(md);
assert.strictEqual(v.valid, true);
assert.strictEqual(v.counts.surface, 2);
assert.strictEqual(v.counts.optout, 1);
});
// ── acceptance #2: every OPT-OUT must carry a reason ─────────────────────
test('validate — OPT-OUT without reason is INVALID (acceptance #2)', () => {
const md = '| capability | decision | reason |\n|---|---|---|\n| skip | OPT-OUT | |';
const v = validateCoverageMatrix(md);
assert.strictEqual(v.valid, false);
assert.ok(v.errors.some((e) => /missing reason/i.test(e)));
});
test('validate — OPT-OUT with one-char reason is valid (boundary)', () => {
const md = '| capability | decision | reason |\n|---|---|---|\n| skip | OPT-OUT | x |';
const v = validateCoverageMatrix(md);
assert.strictEqual(v.valid, true);
});
test('validate — duplicate capability is invalid (matrix is a set of decisions)', () => {
const md = [
'| capability | decision | reason |',
'|---|---|---|',
'| search | INTEGRATE | |',
'| Search | OPT-OUT | dup |',
].join('\n');
const v = validateCoverageMatrix(md);
assert.strictEqual(v.valid, false);
assert.ok(v.errors.some((e) => /duplicate/i.test(e)));
});
test('validate — empty capability name is invalid', () => {
const md = '| capability | decision | reason |\n|---|---|---|\n| | INTEGRATE | |';
const v = validateCoverageMatrix(md);
assert.strictEqual(v.valid, false);
assert.ok(v.errors.some((e) => /empty capability/i.test(e)));
});
// ── review-fix coverage: parser robustness (#1562 code review M1/M2/L1/L2) ──
test('validate — invalid decision cell in a table row is an error, not silently dropped (M1)', () => {
const md = '| capability | decision | reason |\n|---|---|---|\n| x | INTEGRAT | |';
const v = validateCoverageMatrix(md);
assert.strictEqual(v.valid, false);
assert.ok(v.errors.some((e) => /not in \{INTEGRATE, OPT-OUT\}/i.test(e)));
});
test('validate — a pipe in a cell adds extra columns → invalid (M2)', () => {
const md = '| capability | decision | reason |\n|---|---|---|\n| x | OPT-OUT | a|b |';
const v = validateCoverageMatrix(md);
assert.strictEqual(v.valid, false);
assert.ok(v.errors.some((e) => /columns|pipe/i.test(e)));
});
test('validate — a pipe in a JSON-fence reason → invalid (M2)', () => {
const md = '```coverage\n[{"capability":"x","decision":"OPT-OUT","reason":"a|b"}]\n```';
const v = validateCoverageMatrix(md);
assert.strictEqual(v.valid, false);
});
test('validate — capability over the length cap → invalid (S3 bound)', () => {
const longCap = 'x'.repeat(81);
const md = `| capability | decision | reason |\n|---|---|---|\n| ${longCap} | INTEGRATE | |`;
const v = validateCoverageMatrix(md);
assert.strictEqual(v.valid, false);
assert.ok(v.errors.some((e) => /exceeds/i.test(e)));
});
test('parse — case-insensitive ```Coverage fence is accepted (L1)', () => {
const md = '```Coverage\n[{"capability":"a","decision":"INTEGRATE","reason":""}]\n```';
const p = parseCoverageMatrix(md);
assert.strictEqual(p.format, 'json');
assert.strictEqual(p.rows.length, 1);
});
test('parse — a single-dash cell is not mistaken for a separator (L2)', () => {
const md = '| capability | decision | reason |\n|---|---|---|\n| - | INTEGRATE | |';
const v = validateCoverageMatrix(md);
assert.strictEqual(v.counts.surface, 1);
assert.ok(v.valid, 'a capability named "-" is legal');
});
// ── render round-trip (manual) ───────────────────────────────────────────
test('render → parse round-trips a valid matrix', () => {
const rows = [
{ capability: 'search', decision: 'INTEGRATE', reason: '' },
{ capability: 'playlists', decision: 'OPT-OUT', reason: 'not needed yet' },
];
const rendered = renderCoverageMatrix(rows);
const v = validateCoverageMatrix(rendered);
assert.strictEqual(v.valid, true);
assert.strictEqual(v.counts.surface, 2);
});
});
// ──────────────────────────────────────────────────────────────────────────────
// Property test: parse/render bijectivity (RULESET.TESTS property-based)
// ──────────────────────────────────────────────────────────────────────────────
describe('coverage matrix — parse/render bijection (fast-check)', () => {
let mod;
try {
mod = require(MODULE_PATH);
} catch (err) {
throw new Error(`Could not require ${MODULE_PATH}. Run "npm run build:lib". Underlying: ${err.message}`);
}
const { renderCoverageMatrix, validateCoverageMatrix } = mod;
test('any valid row set renders and re-validates to the same counts', () => {
const capabilityGen = fc.stringMatching(/^[a-z][a-z0-9-]{0,14}$/);
const rowGen = fc.record({
capability: capabilityGen,
decision: fc.constantFrom('INTEGRATE', 'OPT-OUT'),
// Reasons are short prose (e.g. "not needed yet"). The matrix is a
// markdown table, so cell text is format-safe: no pipes / newlines.
reason: fc.stringMatching(/^[a-z0-9 ,.\-!?]{0,20}$/),
});
// OPT-OUT rows must carry a non-empty reason for the round-trip to validate.
const validRowGen = rowGen.map((r) =>
r.decision === 'OPT-OUT' && r.reason.trim() === ''
? { ...r, reason: 'because' }
: { ...r, reason: r.reason.trim() }
);
const matrixGen = fc.uniqueArray(validRowGen, {
minLength: 1,
maxLength: 8,
selector: (r) => r.capability.toLowerCase(),
});
fc.assert(
fc.property(matrixGen, (rows) => {
const rendered = renderCoverageMatrix(rows);
const v = validateCoverageMatrix(rendered);
// Injected reason guarantees validity; any invalid result is a parser bug.
assert.strictEqual(v.valid, true);
assert.strictEqual(v.counts.surface, rows.length);
return true;
}),
{ numRuns: 100 }
);
});
});
// ──────────────────────────────────────────────────────────────────────────────
// CLI entry point (STDIN → exit codes mirror grep, like assumption-delta)
// ──────────────────────────────────────────────────────────────────────────────
describe('api-coverage CLI — STDIN + exit codes', () => {
const CLI = path.join(__dirname, '..', 'gsd-core', 'bin', 'lib', 'api-coverage.cjs');
function runCli(stdin) {
const r = spawnSync(process.execPath, [CLI, '--json'], {
input: stdin,
encoding: 'utf-8',
timeout: 15000,
});
return { exitCode: r.status, stdout: r.stdout, stderr: r.stderr };
}
test('exit 0 + JSON IR when integration detected', () => {
const r = runCli('Integrate the Stripe API for payments');
assert.strictEqual(r.exitCode, 0);
const body = JSON.parse(r.stdout);
assert.strictEqual(body.detected, true);
assert.ok(Array.isArray(body.signals));
});
test('exit 1 when no integration', () => {
const r = runCli('Refactor the login helper');
assert.strictEqual(r.exitCode, 1);
});
});

View File

@@ -81,6 +81,7 @@ describe('config-field-docs', () => {
verifier: 'workflow.verifier',
nyquist_validation: 'workflow.nyquist_validation',
ai_integration_phase: 'workflow.ai_integration_phase',
api_coverage_gate: 'workflow.api_coverage_gate',
text_mode: 'workflow.text_mode',
subagent_timeout: 'workflow.subagent_timeout',
branching_strategy: 'git.branching_strategy',

View File

@@ -0,0 +1,72 @@
'use strict';
/**
* #2071 — `gsd-tools effort sync` crashed in an INSTALLED runtime because
* commands.cjs did `require('../../../bin/install.js')`, but the installer only
* copies the `gsd-core/` subtree into a runtime home — the package-root
* `bin/install.js` is never present there, so the require threw MODULE_NOT_FOUND.
*
* This does a real minimal install into a temp home (the same helper the
* golden-parity suite uses) and runs the exact repro from the issue against the
* installed shim: `node <configDir>/gsd-core/bin/gsd-tools.cjs effort sync`. Pre-fix
* this throws `Cannot find module '../../../bin/install.js'`; post-fix the
* install-time resolvers live in the shipped sibling
* `gsd-core/bin/lib/install-effort-resolver.cjs` and the require resolves.
*
* `--config-dir <temp>` keeps it hermetic (targets the temp install, never the
* developer's real ~/.claude); effort sync defaults to dry-run so nothing is written.
*/
const { test, describe } = require('node:test');
const assert = require('node:assert/strict');
const fs = require('node:fs');
const path = require('node:path');
const { execFileSync } = require('node:child_process');
const { runMinimalInstall } = require('./helpers/install-shared.cjs');
const { cleanup } = require('./helpers.cjs');
describe('#2071: effort sync runs in an installed runtime (no package-root bin/install.js)', () => {
test('effort sync does not crash reaching for the un-shipped bin/install.js', () => {
if (process.platform === 'win32') return; // install layout is POSIX-path-shaped
const { configDir, root } = runMinimalInstall({ runtime: 'claude', scope: 'global' });
try {
// Installed layout invariant: the package-root installer is never copied in.
assert.ok(!fs.existsSync(path.join(root, 'bin', 'install.js')), 'installed home must not contain bin/install.js');
assert.ok(!fs.existsSync(path.join(configDir, 'bin', 'install.js')), 'no bin/install.js beside gsd-core');
// A project effort config gives the sync something to resolve.
fs.mkdirSync(path.join(root, '.planning'), { recursive: true });
fs.writeFileSync(
path.join(root, '.planning', 'config.json'),
JSON.stringify({ effort: { default: 'high' } }),
);
const gsdTools = path.join(configDir, 'gsd-core', 'bin', 'gsd-tools.cjs');
let combined = '';
try {
combined = execFileSync(
process.execPath,
[gsdTools, 'effort', 'sync', '--config-dir', configDir],
{ cwd: root, encoding: 'utf-8', env: { ...process.env, HOME: root } },
);
} catch (e) {
combined = `${e.stdout || ''}${e.stderr || ''}${e.message || ''}`;
}
assert.doesNotMatch(
combined,
/Cannot find module[^\n]*install\.js|'\.\.\/\.\.\/\.\.\/bin\/install\.js'/,
`effort sync must not reach for the un-shipped bin/install.js:\n${combined}`,
);
assert.doesNotMatch(
combined,
/MODULE_NOT_FOUND/,
`effort sync must not crash on module resolution in an installed runtime:\n${combined}`,
);
} finally {
cleanup(root);
}
});
});

View File

@@ -52,6 +52,7 @@
"gsd-core/references/agent-skills-bootstrap.md": "51b5d0ba5b1e98d9",
"gsd-core/references/ai-evals.md": "b5afa786b938671e",
"gsd-core/references/ai-frameworks.md": "425dd69c629230e7",
"gsd-core/references/api-coverage.md": "205a43c5fa7c221c",
"gsd-core/references/artifact-types.md": "e176817364a7cbf4",
"gsd-core/references/autonomous-smart-discuss.md": "efd80aca449032ad",
"gsd-core/references/checkpoints.md": "2de680837faa9752",
@@ -93,7 +94,7 @@
"gsd-core/references/loop-hook-dispatch.md": "32e5dfb4dba76987",
"gsd-core/references/mandatory-initial-read.md": "fe59abce693717cf",
"gsd-core/references/model-profile-resolution.md": "de81380316d8a37f",
"gsd-core/references/model-profiles.md": "d6ed560db6357ad2",
"gsd-core/references/model-profiles.md": "6568ca29b6ee00d8",
"gsd-core/references/mvp-concepts.md": "3464783eaaef5c10",
"gsd-core/references/phase-argument-parsing.md": "e5bbb985f3bc3e34",
"gsd-core/references/planner-antipatterns.md": "2459c40bfc8a0ea8",
@@ -108,7 +109,7 @@
"gsd-core/references/planner-reviews.md": "da39eace09a10743",
"gsd-core/references/planner-revision.md": "86ba8a511f081f05",
"gsd-core/references/planner-source-audit.md": "7de5bdb07232ce0b",
"gsd-core/references/planning-config.md": "b0e55e9d585f90c1",
"gsd-core/references/planning-config.md": "2e2f418328e52f6a",
"gsd-core/references/prohibition-probe-fixtures/01-streak-reminder/expected.json": "f10df472f2846cc6",
"gsd-core/references/prohibition-probe-fixtures/02-clean-utility/expected.json": "31e8a781eeffe020",
"gsd-core/references/prohibition-probe-fixtures/03-multi-prohibition/expected.json": "70a532a7cc1b6ae8",
@@ -206,13 +207,13 @@
"gsd-core/workflows/autonomous.md": "603ce2019835f00e",
"gsd-core/workflows/check-todos.md": "5a62092df800ad7c",
"gsd-core/workflows/cleanup.md": "12c8fd85d3010fe6",
"gsd-core/workflows/code-review-fix.md": "5738cb929c995008",
"gsd-core/workflows/code-review.md": "d454365f1704d0bc",
"gsd-core/workflows/code-review-fix.md": "60640e633b0a124b",
"gsd-core/workflows/code-review.md": "5c40505c01871153",
"gsd-core/workflows/complete-milestone.md": "aaf272074acec69d",
"gsd-core/workflows/debug.md": "68f1ddc74886ebe5",
"gsd-core/workflows/diagnose-issues.md": "c8c41993c277363c",
"gsd-core/workflows/discovery-phase.md": "3de990caffdde4f8",
"gsd-core/workflows/discuss-phase-assumptions.md": "42210a0cbe1bac70",
"gsd-core/workflows/discuss-phase-assumptions.md": "8581fd77def7ce84",
"gsd-core/workflows/discuss-phase-power.md": "5c0d1ca6abda0383",
"gsd-core/workflows/discuss-phase.md": "c0a977154470b7f5",
"gsd-core/workflows/discuss-phase/modes/advisor.md": "ab0c68941386998b",
@@ -278,7 +279,7 @@
"gsd-core/workflows/pr-branch.md": "dc5598ae8accdecd",
"gsd-core/workflows/profile-user.md": "355af92ac285567f",
"gsd-core/workflows/progress.md": "79a11ce798082054",
"gsd-core/workflows/quick.md": "35582887917ef938",
"gsd-core/workflows/quick.md": "7108075f69e88e7c",
"gsd-core/workflows/reapply-patches.md": "4dcd6117d0a507ca",
"gsd-core/workflows/remove-phase.md": "23b9eb0858a2535e",
"gsd-core/workflows/remove-workspace.md": "d0bd7e0601138798",
@@ -308,7 +309,7 @@
"gsd-core/workflows/update.md": "2c58df5e21c41c31",
"gsd-core/workflows/validate-phase.md": "6c0ab739d15709fa",
"gsd-core/workflows/verify-phase.md": "0eefbb6bb1b0bed6",
"gsd-core/workflows/verify-work.md": "be699ed7920f61b0",
"gsd-core/workflows/verify-work.md": "59276d94999ee022",
"hooks/gsd-check-update-worker.js": "fa301e6366270d5f",
"hooks/gsd-check-update.js": "4617a98bf529e4c3",
"hooks/gsd-config-reload.js": "96546e0e8bb47904",

View File

@@ -123,6 +123,7 @@
"gsd-core/references/agent-skills-bootstrap.md": "5ab875054b1adda9",
"gsd-core/references/ai-evals.md": "b5afa786b938671e",
"gsd-core/references/ai-frameworks.md": "f827de93dde124eb",
"gsd-core/references/api-coverage.md": "66264d41dfd9154a",
"gsd-core/references/artifact-types.md": "a6d2e1f9453ffbf5",
"gsd-core/references/autonomous-smart-discuss.md": "2fc710cde0ec7785",
"gsd-core/references/checkpoints.md": "6aa620c6ca38bdf0",
@@ -164,7 +165,7 @@
"gsd-core/references/loop-hook-dispatch.md": "32e5dfb4dba76987",
"gsd-core/references/mandatory-initial-read.md": "fe59abce693717cf",
"gsd-core/references/model-profile-resolution.md": "f32bb05102839767",
"gsd-core/references/model-profiles.md": "8484ad9799b7f680",
"gsd-core/references/model-profiles.md": "e067ad3df6770db1",
"gsd-core/references/mvp-concepts.md": "72f7e8b1f8ae7118",
"gsd-core/references/phase-argument-parsing.md": "e5bbb985f3bc3e34",
"gsd-core/references/planner-antipatterns.md": "7ed54ec1e2cc54ac",
@@ -179,7 +180,7 @@
"gsd-core/references/planner-reviews.md": "dda0193a0fbd4947",
"gsd-core/references/planner-revision.md": "86ba8a511f081f05",
"gsd-core/references/planner-source-audit.md": "7de5bdb07232ce0b",
"gsd-core/references/planning-config.md": "eb168188abd00101",
"gsd-core/references/planning-config.md": "ac409835e8260a3e",
"gsd-core/references/prohibition-probe-fixtures/01-streak-reminder/expected.json": "f10df472f2846cc6",
"gsd-core/references/prohibition-probe-fixtures/02-clean-utility/expected.json": "31e8a781eeffe020",
"gsd-core/references/prohibition-probe-fixtures/03-multi-prohibition/expected.json": "70a532a7cc1b6ae8",
@@ -277,13 +278,13 @@
"gsd-core/workflows/autonomous.md": "7e5683728ef33707",
"gsd-core/workflows/check-todos.md": "bdeaf43f9c61e0cc",
"gsd-core/workflows/cleanup.md": "0daa2f2720c111f1",
"gsd-core/workflows/code-review-fix.md": "c323f7098b671bd6",
"gsd-core/workflows/code-review.md": "ad450c6ef8459dad",
"gsd-core/workflows/code-review-fix.md": "2e113d1f4350a075",
"gsd-core/workflows/code-review.md": "334c90c401f291f8",
"gsd-core/workflows/complete-milestone.md": "c1f91b77f4ace7f2",
"gsd-core/workflows/debug.md": "849d7e5b9c12dcae",
"gsd-core/workflows/diagnose-issues.md": "6cc3900891dfb927",
"gsd-core/workflows/discovery-phase.md": "3ba7cfb89fb1e761",
"gsd-core/workflows/discuss-phase-assumptions.md": "1e229f6d21831d60",
"gsd-core/workflows/discuss-phase-assumptions.md": "f5b765d33eba4f88",
"gsd-core/workflows/discuss-phase-power.md": "0841f7dc6e9a054a",
"gsd-core/workflows/discuss-phase.md": "5c5e8d8c2c9d95aa",
"gsd-core/workflows/discuss-phase/modes/advisor.md": "db488d74d080c653",
@@ -349,7 +350,7 @@
"gsd-core/workflows/pr-branch.md": "c87db7ac8c28be1b",
"gsd-core/workflows/profile-user.md": "14263db831230142",
"gsd-core/workflows/progress.md": "893aa3c36983f74b",
"gsd-core/workflows/quick.md": "5790ceb09aa685be",
"gsd-core/workflows/quick.md": "069fe37d083a94a3",
"gsd-core/workflows/reapply-patches.md": "39050f72601aec89",
"gsd-core/workflows/remove-phase.md": "df9a45f0b1880999",
"gsd-core/workflows/remove-workspace.md": "a7ca66db6b7c132c",
@@ -379,7 +380,7 @@
"gsd-core/workflows/update.md": "fd160e13f8b7e83c",
"gsd-core/workflows/validate-phase.md": "2c6d7671fcaabcaa",
"gsd-core/workflows/verify-phase.md": "22f18492581f1da5",
"gsd-core/workflows/verify-work.md": "34e980a6950cd83c",
"gsd-core/workflows/verify-work.md": "63b3f680d8f0a6f3",
"hooks/gsd-check-update-worker.js": "cc1ef5f840f9dfc9",
"hooks/gsd-check-update.js": "7b3a7983d5f1f5d3",
"hooks/gsd-config-reload.js": "96546e0e8bb47904",

View File

@@ -51,6 +51,7 @@
"gsd-core/references/agent-skills-bootstrap.md": "5ab875054b1adda9",
"gsd-core/references/ai-evals.md": "b5afa786b938671e",
"gsd-core/references/ai-frameworks.md": "f827de93dde124eb",
"gsd-core/references/api-coverage.md": "205a43c5fa7c221c",
"gsd-core/references/artifact-types.md": "8bd01fd75a2ba70e",
"gsd-core/references/autonomous-smart-discuss.md": "2fc710cde0ec7785",
"gsd-core/references/checkpoints.md": "6aa620c6ca38bdf0",
@@ -92,7 +93,7 @@
"gsd-core/references/loop-hook-dispatch.md": "32e5dfb4dba76987",
"gsd-core/references/mandatory-initial-read.md": "fe59abce693717cf",
"gsd-core/references/model-profile-resolution.md": "f32bb05102839767",
"gsd-core/references/model-profiles.md": "90e2c9bc577278fd",
"gsd-core/references/model-profiles.md": "c249163663bbea53",
"gsd-core/references/mvp-concepts.md": "3464783eaaef5c10",
"gsd-core/references/phase-argument-parsing.md": "e5bbb985f3bc3e34",
"gsd-core/references/planner-antipatterns.md": "7ed54ec1e2cc54ac",
@@ -107,7 +108,7 @@
"gsd-core/references/planner-reviews.md": "da39eace09a10743",
"gsd-core/references/planner-revision.md": "86ba8a511f081f05",
"gsd-core/references/planner-source-audit.md": "7de5bdb07232ce0b",
"gsd-core/references/planning-config.md": "99c46b7d318adf1a",
"gsd-core/references/planning-config.md": "b025429fc72f9285",
"gsd-core/references/prohibition-probe-fixtures/01-streak-reminder/expected.json": "f10df472f2846cc6",
"gsd-core/references/prohibition-probe-fixtures/02-clean-utility/expected.json": "31e8a781eeffe020",
"gsd-core/references/prohibition-probe-fixtures/03-multi-prohibition/expected.json": "70a532a7cc1b6ae8",
@@ -205,13 +206,13 @@
"gsd-core/workflows/autonomous.md": "722397c04272dfaa",
"gsd-core/workflows/check-todos.md": "6c2a43d1d3e86589",
"gsd-core/workflows/cleanup.md": "0daa2f2720c111f1",
"gsd-core/workflows/code-review-fix.md": "adb62c695b39ef61",
"gsd-core/workflows/code-review.md": "6c40b95e799907d0",
"gsd-core/workflows/code-review-fix.md": "722416b71b31c5fc",
"gsd-core/workflows/code-review.md": "506412604f767adc",
"gsd-core/workflows/complete-milestone.md": "dcf1182398efb1ca",
"gsd-core/workflows/debug.md": "a9397fd35cca2240",
"gsd-core/workflows/diagnose-issues.md": "75ffc381ac3059ff",
"gsd-core/workflows/discovery-phase.md": "6161c60d752d0058",
"gsd-core/workflows/discuss-phase-assumptions.md": "46876f83547db40a",
"gsd-core/workflows/discuss-phase-assumptions.md": "a8cd1db094fefd35",
"gsd-core/workflows/discuss-phase-power.md": "290c0d83d783f9f6",
"gsd-core/workflows/discuss-phase.md": "df56c8cd170b2150",
"gsd-core/workflows/discuss-phase/modes/advisor.md": "db488d74d080c653",
@@ -277,7 +278,7 @@
"gsd-core/workflows/pr-branch.md": "ab157cd8e49621dd",
"gsd-core/workflows/profile-user.md": "ff3820a27731ceb8",
"gsd-core/workflows/progress.md": "bd1ecf9207331bda",
"gsd-core/workflows/quick.md": "da83b1a15b7f1bf7",
"gsd-core/workflows/quick.md": "3363bdfefd403686",
"gsd-core/workflows/reapply-patches.md": "ba9406b60f2c4041",
"gsd-core/workflows/remove-phase.md": "ada8a0546c686483",
"gsd-core/workflows/remove-workspace.md": "f3ab3a88a7e9e1ed",
@@ -307,7 +308,7 @@
"gsd-core/workflows/update.md": "f9e7d8a760d0d3c8",
"gsd-core/workflows/validate-phase.md": "2ac231dc541441c2",
"gsd-core/workflows/verify-phase.md": "e0957e153788a222",
"gsd-core/workflows/verify-work.md": "efe57bdbbb3af03f",
"gsd-core/workflows/verify-work.md": "c8ffee621e7319de",
"hooks/gsd-check-update-worker.js": "a530efdb5fdc0da3",
"hooks/gsd-check-update.js": "25cde66a12d6b886",
"hooks/gsd-config-reload.js": "96546e0e8bb47904",

View File

@@ -55,6 +55,7 @@
"gsd-core/references/agent-skills-bootstrap.md": "59f782556e4e611f",
"gsd-core/references/ai-evals.md": "b5afa786b938671e",
"gsd-core/references/ai-frameworks.md": "f827de93dde124eb",
"gsd-core/references/api-coverage.md": "66264d41dfd9154a",
"gsd-core/references/artifact-types.md": "a6d2e1f9453ffbf5",
"gsd-core/references/autonomous-smart-discuss.md": "2fc710cde0ec7785",
"gsd-core/references/checkpoints.md": "9a7ba3a17ece1698",
@@ -96,7 +97,7 @@
"gsd-core/references/loop-hook-dispatch.md": "32e5dfb4dba76987",
"gsd-core/references/mandatory-initial-read.md": "fe59abce693717cf",
"gsd-core/references/model-profile-resolution.md": "f32bb05102839767",
"gsd-core/references/model-profiles.md": "eb144b83e31196b8",
"gsd-core/references/model-profiles.md": "1794ad3d9854129e",
"gsd-core/references/mvp-concepts.md": "72f7e8b1f8ae7118",
"gsd-core/references/phase-argument-parsing.md": "e5bbb985f3bc3e34",
"gsd-core/references/planner-antipatterns.md": "7ed54ec1e2cc54ac",
@@ -111,7 +112,7 @@
"gsd-core/references/planner-reviews.md": "dda0193a0fbd4947",
"gsd-core/references/planner-revision.md": "86ba8a511f081f05",
"gsd-core/references/planner-source-audit.md": "7de5bdb07232ce0b",
"gsd-core/references/planning-config.md": "a0dc2dddb953b22c",
"gsd-core/references/planning-config.md": "5a40b2934db6a321",
"gsd-core/references/prohibition-probe-fixtures/01-streak-reminder/expected.json": "f10df472f2846cc6",
"gsd-core/references/prohibition-probe-fixtures/02-clean-utility/expected.json": "31e8a781eeffe020",
"gsd-core/references/prohibition-probe-fixtures/03-multi-prohibition/expected.json": "70a532a7cc1b6ae8",
@@ -209,13 +210,13 @@
"gsd-core/workflows/autonomous.md": "cc5217b1b2238a4c",
"gsd-core/workflows/check-todos.md": "32fdf33f5dc8bdde",
"gsd-core/workflows/cleanup.md": "b0ebfc48792b407b",
"gsd-core/workflows/code-review-fix.md": "b946fe7bcb303852",
"gsd-core/workflows/code-review.md": "0b24efcfa71a2ed2",
"gsd-core/workflows/code-review-fix.md": "e4549af672e74e6f",
"gsd-core/workflows/code-review.md": "a65e3e869508f89e",
"gsd-core/workflows/complete-milestone.md": "c0808127038a8f86",
"gsd-core/workflows/debug.md": "f42e8e3cbc298694",
"gsd-core/workflows/diagnose-issues.md": "e616d0d730328d68",
"gsd-core/workflows/discovery-phase.md": "3ba7cfb89fb1e761",
"gsd-core/workflows/discuss-phase-assumptions.md": "89987fd183e0d552",
"gsd-core/workflows/discuss-phase-assumptions.md": "9efcb2ef6a9245b3",
"gsd-core/workflows/discuss-phase-power.md": "0841f7dc6e9a054a",
"gsd-core/workflows/discuss-phase.md": "a290d5ee19607bb7",
"gsd-core/workflows/discuss-phase/modes/advisor.md": "250de9aae90daebc",
@@ -281,7 +282,7 @@
"gsd-core/workflows/pr-branch.md": "9923878a4f6a2d91",
"gsd-core/workflows/profile-user.md": "26f74db0a7fcd268",
"gsd-core/workflows/progress.md": "9f326d63afb4b76b",
"gsd-core/workflows/quick.md": "68a9dfcd53f0859f",
"gsd-core/workflows/quick.md": "2afc046e94daad0a",
"gsd-core/workflows/reapply-patches.md": "eb4272145a117904",
"gsd-core/workflows/remove-phase.md": "e336350f8113a328",
"gsd-core/workflows/remove-workspace.md": "e685dfbd736dfd90",
@@ -311,7 +312,7 @@
"gsd-core/workflows/update.md": "165beec33490bd28",
"gsd-core/workflows/validate-phase.md": "5b4ae14c87859bd2",
"gsd-core/workflows/verify-phase.md": "a4f918c92c927268",
"gsd-core/workflows/verify-work.md": "6fa216623778c541",
"gsd-core/workflows/verify-work.md": "3fb282f2bce34a79",
"scripts/changeset/README.md": "86ff89331dfd94b2",
"scripts/changeset/cli.cjs": "68f92a344b199271",
"scripts/changeset/github-release-notes.cjs": "795677f0c009b132",

View File

@@ -123,6 +123,7 @@
"gsd-core/references/agent-skills-bootstrap.md": "5ab875054b1adda9",
"gsd-core/references/ai-evals.md": "b5afa786b938671e",
"gsd-core/references/ai-frameworks.md": "f827de93dde124eb",
"gsd-core/references/api-coverage.md": "66264d41dfd9154a",
"gsd-core/references/artifact-types.md": "a6d2e1f9453ffbf5",
"gsd-core/references/autonomous-smart-discuss.md": "2fc710cde0ec7785",
"gsd-core/references/checkpoints.md": "6aa620c6ca38bdf0",
@@ -164,7 +165,7 @@
"gsd-core/references/loop-hook-dispatch.md": "32e5dfb4dba76987",
"gsd-core/references/mandatory-initial-read.md": "fe59abce693717cf",
"gsd-core/references/model-profile-resolution.md": "f32bb05102839767",
"gsd-core/references/model-profiles.md": "8484ad9799b7f680",
"gsd-core/references/model-profiles.md": "e067ad3df6770db1",
"gsd-core/references/mvp-concepts.md": "72f7e8b1f8ae7118",
"gsd-core/references/phase-argument-parsing.md": "e5bbb985f3bc3e34",
"gsd-core/references/planner-antipatterns.md": "7ed54ec1e2cc54ac",
@@ -179,7 +180,7 @@
"gsd-core/references/planner-reviews.md": "dda0193a0fbd4947",
"gsd-core/references/planner-revision.md": "86ba8a511f081f05",
"gsd-core/references/planner-source-audit.md": "7de5bdb07232ce0b",
"gsd-core/references/planning-config.md": "eb168188abd00101",
"gsd-core/references/planning-config.md": "ac409835e8260a3e",
"gsd-core/references/prohibition-probe-fixtures/01-streak-reminder/expected.json": "f10df472f2846cc6",
"gsd-core/references/prohibition-probe-fixtures/02-clean-utility/expected.json": "31e8a781eeffe020",
"gsd-core/references/prohibition-probe-fixtures/03-multi-prohibition/expected.json": "70a532a7cc1b6ae8",
@@ -277,13 +278,13 @@
"gsd-core/workflows/autonomous.md": "7e5683728ef33707",
"gsd-core/workflows/check-todos.md": "bdeaf43f9c61e0cc",
"gsd-core/workflows/cleanup.md": "0daa2f2720c111f1",
"gsd-core/workflows/code-review-fix.md": "c323f7098b671bd6",
"gsd-core/workflows/code-review.md": "ad450c6ef8459dad",
"gsd-core/workflows/code-review-fix.md": "2e113d1f4350a075",
"gsd-core/workflows/code-review.md": "334c90c401f291f8",
"gsd-core/workflows/complete-milestone.md": "c1f91b77f4ace7f2",
"gsd-core/workflows/debug.md": "849d7e5b9c12dcae",
"gsd-core/workflows/diagnose-issues.md": "77d98ac07c4a26ff",
"gsd-core/workflows/discovery-phase.md": "3ba7cfb89fb1e761",
"gsd-core/workflows/discuss-phase-assumptions.md": "1e229f6d21831d60",
"gsd-core/workflows/discuss-phase-assumptions.md": "f5b765d33eba4f88",
"gsd-core/workflows/discuss-phase-power.md": "0841f7dc6e9a054a",
"gsd-core/workflows/discuss-phase.md": "02a5381c9a2173be",
"gsd-core/workflows/discuss-phase/modes/advisor.md": "db488d74d080c653",
@@ -349,7 +350,7 @@
"gsd-core/workflows/pr-branch.md": "c87db7ac8c28be1b",
"gsd-core/workflows/profile-user.md": "4fa910d15dea5695",
"gsd-core/workflows/progress.md": "893aa3c36983f74b",
"gsd-core/workflows/quick.md": "9033dbe58443af36",
"gsd-core/workflows/quick.md": "cc530299ba461539",
"gsd-core/workflows/reapply-patches.md": "39050f72601aec89",
"gsd-core/workflows/remove-phase.md": "df9a45f0b1880999",
"gsd-core/workflows/remove-workspace.md": "a7ca66db6b7c132c",
@@ -379,7 +380,7 @@
"gsd-core/workflows/update.md": "5ff1f77222977648",
"gsd-core/workflows/validate-phase.md": "2c6d7671fcaabcaa",
"gsd-core/workflows/verify-phase.md": "22f18492581f1da5",
"gsd-core/workflows/verify-work.md": "34e980a6950cd83c",
"gsd-core/workflows/verify-work.md": "63b3f680d8f0a6f3",
"hooks/gsd-check-update-worker.js": "bdc9324a2f080ddd",
"hooks/gsd-check-update.js": "b7669f605631e506",
"hooks/gsd-config-reload.js": "96546e0e8bb47904",

View File

@@ -87,6 +87,7 @@
"gsd-core/references/agent-skills-bootstrap.md": "51b5d0ba5b1e98d9",
"gsd-core/references/ai-evals.md": "b5afa786b938671e",
"gsd-core/references/ai-frameworks.md": "425dd69c629230e7",
"gsd-core/references/api-coverage.md": "524382216a8e713f",
"gsd-core/references/artifact-types.md": "3218cafb0c92dc32",
"gsd-core/references/autonomous-smart-discuss.md": "4156025334411073",
"gsd-core/references/checkpoints.md": "9feb961f644afa96",
@@ -128,7 +129,7 @@
"gsd-core/references/loop-hook-dispatch.md": "32e5dfb4dba76987",
"gsd-core/references/mandatory-initial-read.md": "fe59abce693717cf",
"gsd-core/references/model-profile-resolution.md": "f32bb05102839767",
"gsd-core/references/model-profiles.md": "86e81f17c2f23fff",
"gsd-core/references/model-profiles.md": "bd90862f68007f2e",
"gsd-core/references/mvp-concepts.md": "23201c8118fb074a",
"gsd-core/references/phase-argument-parsing.md": "531176f66da49c98",
"gsd-core/references/planner-antipatterns.md": "2459c40bfc8a0ea8",
@@ -143,7 +144,7 @@
"gsd-core/references/planner-reviews.md": "7889bfa28e82156b",
"gsd-core/references/planner-revision.md": "2ebf1a714d1ec4bf",
"gsd-core/references/planner-source-audit.md": "7de5bdb07232ce0b",
"gsd-core/references/planning-config.md": "40d4abb1ed0f3723",
"gsd-core/references/planning-config.md": "b01856d7a63e73a2",
"gsd-core/references/prohibition-probe-fixtures/01-streak-reminder/expected.json": "f10df472f2846cc6",
"gsd-core/references/prohibition-probe-fixtures/02-clean-utility/expected.json": "31e8a781eeffe020",
"gsd-core/references/prohibition-probe-fixtures/03-multi-prohibition/expected.json": "70a532a7cc1b6ae8",
@@ -241,13 +242,13 @@
"gsd-core/workflows/autonomous.md": "92f08626d4aea668",
"gsd-core/workflows/check-todos.md": "b2b103e8638e760a",
"gsd-core/workflows/cleanup.md": "5d48d64664222a3e",
"gsd-core/workflows/code-review-fix.md": "3ff5f7eac2269ca3",
"gsd-core/workflows/code-review.md": "82858ab037b9176e",
"gsd-core/workflows/code-review-fix.md": "ae7f9c6b39a23c12",
"gsd-core/workflows/code-review.md": "eadada9e0a89adf2",
"gsd-core/workflows/complete-milestone.md": "017df7443bd08da5",
"gsd-core/workflows/debug.md": "cc7b2d2fd4307a78",
"gsd-core/workflows/diagnose-issues.md": "e38bb21d06dff077",
"gsd-core/workflows/discovery-phase.md": "71a4b78ff876a854",
"gsd-core/workflows/discuss-phase-assumptions.md": "f23abfe76623ed63",
"gsd-core/workflows/discuss-phase-assumptions.md": "0d936ec25299917c",
"gsd-core/workflows/discuss-phase-power.md": "3f8b83dc6be2e9d5",
"gsd-core/workflows/discuss-phase.md": "7c9df6656b81fda9",
"gsd-core/workflows/discuss-phase/modes/advisor.md": "db488d74d080c653",
@@ -313,7 +314,7 @@
"gsd-core/workflows/pr-branch.md": "d13e1cc81de40896",
"gsd-core/workflows/profile-user.md": "05828c8cc61ef384",
"gsd-core/workflows/progress.md": "7bedc431bb55edb8",
"gsd-core/workflows/quick.md": "3e7686705da2af19",
"gsd-core/workflows/quick.md": "62b9e138dc70b479",
"gsd-core/workflows/reapply-patches.md": "26297b84736e66a4",
"gsd-core/workflows/remove-phase.md": "9ee0fddd11a0d9d4",
"gsd-core/workflows/remove-workspace.md": "19d7465aaa50cb62",
@@ -343,7 +344,7 @@
"gsd-core/workflows/update.md": "5c35c0ec0f462ea6",
"gsd-core/workflows/validate-phase.md": "020201a41049679f",
"gsd-core/workflows/verify-phase.md": "2e12c3cb97a9122a",
"gsd-core/workflows/verify-work.md": "69e27f6f419d0bba",
"gsd-core/workflows/verify-work.md": "5b348e73fc0d0829",
"hooks/gsd-check-update.js": "ef48957eb6ac6a10",
"hooks/gsd-context-monitor.js": "76fecaaa2babd6c1",
"scripts/changeset/README.md": "86ff89331dfd94b2",

View File

@@ -53,6 +53,7 @@
"gsd-core/references/agent-skills-bootstrap.md": "51b5d0ba5b1e98d9",
"gsd-core/references/ai-evals.md": "b5afa786b938671e",
"gsd-core/references/ai-frameworks.md": "425dd69c629230e7",
"gsd-core/references/api-coverage.md": "205a43c5fa7c221c",
"gsd-core/references/artifact-types.md": "f992de8b2b1a4420",
"gsd-core/references/autonomous-smart-discuss.md": "efd80aca449032ad",
"gsd-core/references/checkpoints.md": "c70b323dcb1583d5",
@@ -94,7 +95,7 @@
"gsd-core/references/loop-hook-dispatch.md": "32e5dfb4dba76987",
"gsd-core/references/mandatory-initial-read.md": "fe59abce693717cf",
"gsd-core/references/model-profile-resolution.md": "1d392e37a746742a",
"gsd-core/references/model-profiles.md": "d6ed560db6357ad2",
"gsd-core/references/model-profiles.md": "6568ca29b6ee00d8",
"gsd-core/references/mvp-concepts.md": "3464783eaaef5c10",
"gsd-core/references/phase-argument-parsing.md": "e5bbb985f3bc3e34",
"gsd-core/references/planner-antipatterns.md": "2459c40bfc8a0ea8",
@@ -109,7 +110,7 @@
"gsd-core/references/planner-reviews.md": "da39eace09a10743",
"gsd-core/references/planner-revision.md": "86ba8a511f081f05",
"gsd-core/references/planner-source-audit.md": "7de5bdb07232ce0b",
"gsd-core/references/planning-config.md": "fd81dd276828eab4",
"gsd-core/references/planning-config.md": "309a566a0e13e43e",
"gsd-core/references/prohibition-probe-fixtures/01-streak-reminder/expected.json": "f10df472f2846cc6",
"gsd-core/references/prohibition-probe-fixtures/02-clean-utility/expected.json": "31e8a781eeffe020",
"gsd-core/references/prohibition-probe-fixtures/03-multi-prohibition/expected.json": "70a532a7cc1b6ae8",
@@ -207,13 +208,13 @@
"gsd-core/workflows/autonomous.md": "dd972ddde9663295",
"gsd-core/workflows/check-todos.md": "be9b50b5f28d7504",
"gsd-core/workflows/cleanup.md": "8233b05ebf011bec",
"gsd-core/workflows/code-review-fix.md": "ab2b467b41522c10",
"gsd-core/workflows/code-review.md": "1bbbe61f45fccf4c",
"gsd-core/workflows/code-review-fix.md": "fda53892ae4b17fc",
"gsd-core/workflows/code-review.md": "f1c045ec4d33abc8",
"gsd-core/workflows/complete-milestone.md": "470cf39261400ee2",
"gsd-core/workflows/debug.md": "36cb536be452d79e",
"gsd-core/workflows/diagnose-issues.md": "42acbe2a43fc886e",
"gsd-core/workflows/discovery-phase.md": "8e99da61fb2b7074",
"gsd-core/workflows/discuss-phase-assumptions.md": "ab2d26b194805ac1",
"gsd-core/workflows/discuss-phase-assumptions.md": "ab0c432b84038681",
"gsd-core/workflows/discuss-phase-power.md": "5c0d1ca6abda0383",
"gsd-core/workflows/discuss-phase.md": "31a79ea11c1cdd5f",
"gsd-core/workflows/discuss-phase/modes/advisor.md": "654cdaf1138d5e27",
@@ -279,7 +280,7 @@
"gsd-core/workflows/pr-branch.md": "2833905f119b5722",
"gsd-core/workflows/profile-user.md": "5cc032206c99ef71",
"gsd-core/workflows/progress.md": "6b9a84a43dc55af5",
"gsd-core/workflows/quick.md": "7ddfe17f048541ec",
"gsd-core/workflows/quick.md": "eb68ea8748546331",
"gsd-core/workflows/reapply-patches.md": "8fd59e24b486f180",
"gsd-core/workflows/remove-phase.md": "e262654e319d1bc4",
"gsd-core/workflows/remove-workspace.md": "ceddfeef5f2d6754",
@@ -309,7 +310,7 @@
"gsd-core/workflows/update.md": "f444a7cfcd246cfb",
"gsd-core/workflows/validate-phase.md": "2f705775a4b76d42",
"gsd-core/workflows/verify-phase.md": "5c72780e34214e27",
"gsd-core/workflows/verify-work.md": "58e9b1b16f773b53",
"gsd-core/workflows/verify-work.md": "c966971a3cbd1d71",
"hooks/gsd-session.json": "0a462834f2a28fee",
"scripts/changeset/README.md": "86ff89331dfd94b2",
"scripts/changeset/cli.cjs": "68f92a344b199271",

View File

@@ -123,6 +123,7 @@
"gsd-core/references/agent-skills-bootstrap.md": "3d62d178004db5cc",
"gsd-core/references/ai-evals.md": "b5afa786b938671e",
"gsd-core/references/ai-frameworks.md": "f827de93dde124eb",
"gsd-core/references/api-coverage.md": "205a43c5fa7c221c",
"gsd-core/references/artifact-types.md": "8bd01fd75a2ba70e",
"gsd-core/references/autonomous-smart-discuss.md": "273b371c5751f35a",
"gsd-core/references/checkpoints.md": "3001eeccb319781b",
@@ -164,7 +165,7 @@
"gsd-core/references/loop-hook-dispatch.md": "32e5dfb4dba76987",
"gsd-core/references/mandatory-initial-read.md": "fe59abce693717cf",
"gsd-core/references/model-profile-resolution.md": "f32bb05102839767",
"gsd-core/references/model-profiles.md": "f4c013e700c52b08",
"gsd-core/references/model-profiles.md": "5452b2e19e19f77e",
"gsd-core/references/mvp-concepts.md": "3464783eaaef5c10",
"gsd-core/references/phase-argument-parsing.md": "e562947d1bf5c5a4",
"gsd-core/references/planner-antipatterns.md": "7ed54ec1e2cc54ac",
@@ -179,7 +180,7 @@
"gsd-core/references/planner-reviews.md": "da39eace09a10743",
"gsd-core/references/planner-revision.md": "86ba8a511f081f05",
"gsd-core/references/planner-source-audit.md": "7de5bdb07232ce0b",
"gsd-core/references/planning-config.md": "8af05de88771e9f4",
"gsd-core/references/planning-config.md": "a3f1b4aca291db59",
"gsd-core/references/prohibition-probe-fixtures/01-streak-reminder/expected.json": "f10df472f2846cc6",
"gsd-core/references/prohibition-probe-fixtures/02-clean-utility/expected.json": "31e8a781eeffe020",
"gsd-core/references/prohibition-probe-fixtures/03-multi-prohibition/expected.json": "70a532a7cc1b6ae8",
@@ -277,13 +278,13 @@
"gsd-core/workflows/autonomous.md": "4b20eda9a0b587ce",
"gsd-core/workflows/check-todos.md": "1a67337d1630848f",
"gsd-core/workflows/cleanup.md": "db47963f103c2748",
"gsd-core/workflows/code-review-fix.md": "49e7f024c551b3a5",
"gsd-core/workflows/code-review.md": "6a9fd2996a6b600e",
"gsd-core/workflows/code-review-fix.md": "c57af378033b1b58",
"gsd-core/workflows/code-review.md": "32c37bcbec8b8698",
"gsd-core/workflows/complete-milestone.md": "1400a4856f592f3e",
"gsd-core/workflows/debug.md": "a73d8018986131ba",
"gsd-core/workflows/diagnose-issues.md": "cd582747131726e3",
"gsd-core/workflows/discovery-phase.md": "7dcf150998559c11",
"gsd-core/workflows/discuss-phase-assumptions.md": "373788c6b7eab272",
"gsd-core/workflows/discuss-phase-assumptions.md": "c25c6a6c633d71b6",
"gsd-core/workflows/discuss-phase-power.md": "290c0d83d783f9f6",
"gsd-core/workflows/discuss-phase.md": "14688ff19ce2184c",
"gsd-core/workflows/discuss-phase/modes/advisor.md": "30612a2de153cb5b",
@@ -349,7 +350,7 @@
"gsd-core/workflows/pr-branch.md": "c67d90c65da47168",
"gsd-core/workflows/profile-user.md": "8c943983241260b5",
"gsd-core/workflows/progress.md": "65aabee5e8a6dd82",
"gsd-core/workflows/quick.md": "762256cf6d177c06",
"gsd-core/workflows/quick.md": "32a71f62041c8510",
"gsd-core/workflows/reapply-patches.md": "ba9406b60f2c4041",
"gsd-core/workflows/remove-phase.md": "ada8a0546c686483",
"gsd-core/workflows/remove-workspace.md": "433affcd1a200826",
@@ -379,7 +380,7 @@
"gsd-core/workflows/update.md": "23e294ba707c3580",
"gsd-core/workflows/validate-phase.md": "2df0c6e298a5f249",
"gsd-core/workflows/verify-phase.md": "e0957e153788a222",
"gsd-core/workflows/verify-work.md": "145596b2542c457a",
"gsd-core/workflows/verify-work.md": "e7e7e900c4874490",
"hooks/gsd-cursor-post-tool.js": "019d503aee8b4a3f",
"hooks/gsd-cursor-session-start.js": "c6e04ed597ea7020",
"scripts/changeset/README.md": "86ff89331dfd94b2",

View File

@@ -52,6 +52,7 @@
"gsd-core/references/agent-skills-bootstrap.md": "892f086e846cd8e4",
"gsd-core/references/ai-evals.md": "b5afa786b938671e",
"gsd-core/references/ai-frameworks.md": "f827de93dde124eb",
"gsd-core/references/api-coverage.md": "205a43c5fa7c221c",
"gsd-core/references/artifact-types.md": "8bd01fd75a2ba70e",
"gsd-core/references/autonomous-smart-discuss.md": "2fc710cde0ec7785",
"gsd-core/references/checkpoints.md": "db8a7425ed808e24",
@@ -93,7 +94,7 @@
"gsd-core/references/loop-hook-dispatch.md": "32e5dfb4dba76987",
"gsd-core/references/mandatory-initial-read.md": "fe59abce693717cf",
"gsd-core/references/model-profile-resolution.md": "f32bb05102839767",
"gsd-core/references/model-profiles.md": "8472d453a325cc1b",
"gsd-core/references/model-profiles.md": "6012c3b53473f04f",
"gsd-core/references/mvp-concepts.md": "3464783eaaef5c10",
"gsd-core/references/phase-argument-parsing.md": "e5bbb985f3bc3e34",
"gsd-core/references/planner-antipatterns.md": "7ed54ec1e2cc54ac",
@@ -108,7 +109,7 @@
"gsd-core/references/planner-reviews.md": "da39eace09a10743",
"gsd-core/references/planner-revision.md": "86ba8a511f081f05",
"gsd-core/references/planner-source-audit.md": "7de5bdb07232ce0b",
"gsd-core/references/planning-config.md": "1358a14bded944f5",
"gsd-core/references/planning-config.md": "831d626c214d92e4",
"gsd-core/references/prohibition-probe-fixtures/01-streak-reminder/expected.json": "f10df472f2846cc6",
"gsd-core/references/prohibition-probe-fixtures/02-clean-utility/expected.json": "31e8a781eeffe020",
"gsd-core/references/prohibition-probe-fixtures/03-multi-prohibition/expected.json": "70a532a7cc1b6ae8",
@@ -206,13 +207,13 @@
"gsd-core/workflows/autonomous.md": "dd572ca89862e5ec",
"gsd-core/workflows/check-todos.md": "ea9a303c48a5d752",
"gsd-core/workflows/cleanup.md": "6c488059fc152a49",
"gsd-core/workflows/code-review-fix.md": "04b143a963067236",
"gsd-core/workflows/code-review.md": "ff28724fc216972c",
"gsd-core/workflows/code-review-fix.md": "e829d3baf9901b54",
"gsd-core/workflows/code-review.md": "50a05ab8957bd05f",
"gsd-core/workflows/complete-milestone.md": "f1866541148dc291",
"gsd-core/workflows/debug.md": "15cf6999e85dcc8c",
"gsd-core/workflows/diagnose-issues.md": "16f2d2a85335641f",
"gsd-core/workflows/discovery-phase.md": "6161c60d752d0058",
"gsd-core/workflows/discuss-phase-assumptions.md": "db60ec68b59a40eb",
"gsd-core/workflows/discuss-phase-assumptions.md": "3a1e215890d2b3f4",
"gsd-core/workflows/discuss-phase-power.md": "290c0d83d783f9f6",
"gsd-core/workflows/discuss-phase.md": "0aa052ae4ee70bf6",
"gsd-core/workflows/discuss-phase/modes/advisor.md": "536e2fa842f1bc95",
@@ -278,7 +279,7 @@
"gsd-core/workflows/pr-branch.md": "ecabd55e4eabf229",
"gsd-core/workflows/profile-user.md": "de5030437226cf2c",
"gsd-core/workflows/progress.md": "f18db000584d9cb1",
"gsd-core/workflows/quick.md": "5044ce4e7512e174",
"gsd-core/workflows/quick.md": "e7a395b9e7786b56",
"gsd-core/workflows/reapply-patches.md": "158083a310859594",
"gsd-core/workflows/remove-phase.md": "fce799aae3ab2715",
"gsd-core/workflows/remove-workspace.md": "8facde381657dd71",
@@ -308,7 +309,7 @@
"gsd-core/workflows/update.md": "7499bb4cb2a3ce6f",
"gsd-core/workflows/validate-phase.md": "75e8971d3981d06b",
"gsd-core/workflows/verify-phase.md": "5c8d1305b47fbef4",
"gsd-core/workflows/verify-work.md": "e56e07475d5eb51e",
"gsd-core/workflows/verify-work.md": "7a9c9541d2d73fdc",
"hooks/gsd-check-update-worker.js": "7989cc2bedd1138d",
"hooks/gsd-check-update.js": "25f5ad726f76fc11",
"hooks/gsd-config-reload.js": "880b696458e85e9b",

View File

@@ -123,6 +123,7 @@
"gsd-core/references/agent-skills-bootstrap.md": "51b5d0ba5b1e98d9",
"gsd-core/references/ai-evals.md": "b5afa786b938671e",
"gsd-core/references/ai-frameworks.md": "425dd69c629230e7",
"gsd-core/references/api-coverage.md": "205a43c5fa7c221c",
"gsd-core/references/artifact-types.md": "8bd01fd75a2ba70e",
"gsd-core/references/autonomous-smart-discuss.md": "3986d58011bf9006",
"gsd-core/references/checkpoints.md": "9feb961f644afa96",
@@ -164,7 +165,7 @@
"gsd-core/references/loop-hook-dispatch.md": "32e5dfb4dba76987",
"gsd-core/references/mandatory-initial-read.md": "fe59abce693717cf",
"gsd-core/references/model-profile-resolution.md": "f32bb05102839767",
"gsd-core/references/model-profiles.md": "d6ed560db6357ad2",
"gsd-core/references/model-profiles.md": "6568ca29b6ee00d8",
"gsd-core/references/mvp-concepts.md": "3464783eaaef5c10",
"gsd-core/references/phase-argument-parsing.md": "e5bbb985f3bc3e34",
"gsd-core/references/planner-antipatterns.md": "2459c40bfc8a0ea8",
@@ -179,7 +180,7 @@
"gsd-core/references/planner-reviews.md": "da39eace09a10743",
"gsd-core/references/planner-revision.md": "86ba8a511f081f05",
"gsd-core/references/planner-source-audit.md": "7de5bdb07232ce0b",
"gsd-core/references/planning-config.md": "aad463ddda23dbaf",
"gsd-core/references/planning-config.md": "37ab69107a2f7dc6",
"gsd-core/references/prohibition-probe-fixtures/01-streak-reminder/expected.json": "f10df472f2846cc6",
"gsd-core/references/prohibition-probe-fixtures/02-clean-utility/expected.json": "31e8a781eeffe020",
"gsd-core/references/prohibition-probe-fixtures/03-multi-prohibition/expected.json": "70a532a7cc1b6ae8",
@@ -277,13 +278,13 @@
"gsd-core/workflows/autonomous.md": "73f429f7472c9949",
"gsd-core/workflows/check-todos.md": "ed4b4eb12be222d7",
"gsd-core/workflows/cleanup.md": "c5f1bf186395d67d",
"gsd-core/workflows/code-review-fix.md": "adb62c695b39ef61",
"gsd-core/workflows/code-review.md": "6c40b95e799907d0",
"gsd-core/workflows/code-review-fix.md": "722416b71b31c5fc",
"gsd-core/workflows/code-review.md": "506412604f767adc",
"gsd-core/workflows/complete-milestone.md": "59753bf44d4300da",
"gsd-core/workflows/debug.md": "a72d830ac3df74aa",
"gsd-core/workflows/diagnose-issues.md": "210b5b313e8a559a",
"gsd-core/workflows/discovery-phase.md": "ca7b2be46e59e862",
"gsd-core/workflows/discuss-phase-assumptions.md": "36f7edcb666745f2",
"gsd-core/workflows/discuss-phase-assumptions.md": "3ef1df313e715387",
"gsd-core/workflows/discuss-phase-power.md": "5c0d1ca6abda0383",
"gsd-core/workflows/discuss-phase.md": "bd4b26ba168bb51f",
"gsd-core/workflows/discuss-phase/modes/advisor.md": "f8ae26ba4e07672b",
@@ -349,7 +350,7 @@
"gsd-core/workflows/pr-branch.md": "ab157cd8e49621dd",
"gsd-core/workflows/profile-user.md": "203ebe3f8f3876a8",
"gsd-core/workflows/progress.md": "9381c59676ccb937",
"gsd-core/workflows/quick.md": "fd7461e5a92fe450",
"gsd-core/workflows/quick.md": "e71c92cf0463a461",
"gsd-core/workflows/reapply-patches.md": "becf9728cdb124c4",
"gsd-core/workflows/remove-phase.md": "ada8a0546c686483",
"gsd-core/workflows/remove-workspace.md": "fc83f362a2d0a1b7",
@@ -379,7 +380,7 @@
"gsd-core/workflows/update.md": "07dc2fba78ad1865",
"gsd-core/workflows/validate-phase.md": "557e3251e3b9349a",
"gsd-core/workflows/verify-phase.md": "e0957e153788a222",
"gsd-core/workflows/verify-work.md": "b68ac37f6301a3b5",
"gsd-core/workflows/verify-work.md": "9fc717845828c6e3",
"kilo.json": "13151e97ff23c1aa",
"scripts/changeset/README.md": "86ff89331dfd94b2",
"scripts/changeset/cli.cjs": "68f92a344b199271",

View File

@@ -88,6 +88,7 @@
"gsd-core/references/agent-skills-bootstrap.md": "5ab875054b1adda9",
"gsd-core/references/ai-evals.md": "b5afa786b938671e",
"gsd-core/references/ai-frameworks.md": "f827de93dde124eb",
"gsd-core/references/api-coverage.md": "66264d41dfd9154a",
"gsd-core/references/artifact-types.md": "a6d2e1f9453ffbf5",
"gsd-core/references/autonomous-smart-discuss.md": "2fc710cde0ec7785",
"gsd-core/references/checkpoints.md": "6aa620c6ca38bdf0",
@@ -129,7 +130,7 @@
"gsd-core/references/loop-hook-dispatch.md": "32e5dfb4dba76987",
"gsd-core/references/mandatory-initial-read.md": "fe59abce693717cf",
"gsd-core/references/model-profile-resolution.md": "f32bb05102839767",
"gsd-core/references/model-profiles.md": "8484ad9799b7f680",
"gsd-core/references/model-profiles.md": "e067ad3df6770db1",
"gsd-core/references/mvp-concepts.md": "72f7e8b1f8ae7118",
"gsd-core/references/phase-argument-parsing.md": "e5bbb985f3bc3e34",
"gsd-core/references/planner-antipatterns.md": "7ed54ec1e2cc54ac",
@@ -144,7 +145,7 @@
"gsd-core/references/planner-reviews.md": "dda0193a0fbd4947",
"gsd-core/references/planner-revision.md": "86ba8a511f081f05",
"gsd-core/references/planner-source-audit.md": "7de5bdb07232ce0b",
"gsd-core/references/planning-config.md": "eb168188abd00101",
"gsd-core/references/planning-config.md": "ac409835e8260a3e",
"gsd-core/references/prohibition-probe-fixtures/01-streak-reminder/expected.json": "f10df472f2846cc6",
"gsd-core/references/prohibition-probe-fixtures/02-clean-utility/expected.json": "31e8a781eeffe020",
"gsd-core/references/prohibition-probe-fixtures/03-multi-prohibition/expected.json": "70a532a7cc1b6ae8",
@@ -242,13 +243,13 @@
"gsd-core/workflows/autonomous.md": "7e5683728ef33707",
"gsd-core/workflows/check-todos.md": "bdeaf43f9c61e0cc",
"gsd-core/workflows/cleanup.md": "0daa2f2720c111f1",
"gsd-core/workflows/code-review-fix.md": "c323f7098b671bd6",
"gsd-core/workflows/code-review.md": "ad450c6ef8459dad",
"gsd-core/workflows/code-review-fix.md": "2e113d1f4350a075",
"gsd-core/workflows/code-review.md": "334c90c401f291f8",
"gsd-core/workflows/complete-milestone.md": "c1f91b77f4ace7f2",
"gsd-core/workflows/debug.md": "849d7e5b9c12dcae",
"gsd-core/workflows/diagnose-issues.md": "67c058fc7ae6026b",
"gsd-core/workflows/discovery-phase.md": "3ba7cfb89fb1e761",
"gsd-core/workflows/discuss-phase-assumptions.md": "1e229f6d21831d60",
"gsd-core/workflows/discuss-phase-assumptions.md": "f5b765d33eba4f88",
"gsd-core/workflows/discuss-phase-power.md": "0841f7dc6e9a054a",
"gsd-core/workflows/discuss-phase.md": "0c0465ba668adb33",
"gsd-core/workflows/discuss-phase/modes/advisor.md": "db488d74d080c653",
@@ -314,7 +315,7 @@
"gsd-core/workflows/pr-branch.md": "c87db7ac8c28be1b",
"gsd-core/workflows/profile-user.md": "5abfae83739fa978",
"gsd-core/workflows/progress.md": "893aa3c36983f74b",
"gsd-core/workflows/quick.md": "e265e01cfe117215",
"gsd-core/workflows/quick.md": "09d88dd1c0b3e03a",
"gsd-core/workflows/reapply-patches.md": "39050f72601aec89",
"gsd-core/workflows/remove-phase.md": "df9a45f0b1880999",
"gsd-core/workflows/remove-workspace.md": "a7ca66db6b7c132c",
@@ -344,7 +345,7 @@
"gsd-core/workflows/update.md": "6718e0632bba26ca",
"gsd-core/workflows/validate-phase.md": "2c6d7671fcaabcaa",
"gsd-core/workflows/verify-phase.md": "22f18492581f1da5",
"gsd-core/workflows/verify-work.md": "34e980a6950cd83c",
"gsd-core/workflows/verify-work.md": "63b3f680d8f0a6f3",
"scripts/changeset/README.md": "86ff89331dfd94b2",
"scripts/changeset/cli.cjs": "68f92a344b199271",
"scripts/changeset/github-release-notes.cjs": "795677f0c009b132",

View File

@@ -123,6 +123,7 @@
"gsd-core/references/agent-skills-bootstrap.md": "51b5d0ba5b1e98d9",
"gsd-core/references/ai-evals.md": "b5afa786b938671e",
"gsd-core/references/ai-frameworks.md": "425dd69c629230e7",
"gsd-core/references/api-coverage.md": "205a43c5fa7c221c",
"gsd-core/references/artifact-types.md": "218c55caf8aff6df",
"gsd-core/references/autonomous-smart-discuss.md": "3986d58011bf9006",
"gsd-core/references/checkpoints.md": "9feb961f644afa96",
@@ -164,7 +165,7 @@
"gsd-core/references/loop-hook-dispatch.md": "32e5dfb4dba76987",
"gsd-core/references/mandatory-initial-read.md": "fe59abce693717cf",
"gsd-core/references/model-profile-resolution.md": "858c16730df68ac2",
"gsd-core/references/model-profiles.md": "d6ed560db6357ad2",
"gsd-core/references/model-profiles.md": "6568ca29b6ee00d8",
"gsd-core/references/mvp-concepts.md": "3464783eaaef5c10",
"gsd-core/references/phase-argument-parsing.md": "e5bbb985f3bc3e34",
"gsd-core/references/planner-antipatterns.md": "2459c40bfc8a0ea8",
@@ -179,7 +180,7 @@
"gsd-core/references/planner-reviews.md": "da39eace09a10743",
"gsd-core/references/planner-revision.md": "86ba8a511f081f05",
"gsd-core/references/planner-source-audit.md": "7de5bdb07232ce0b",
"gsd-core/references/planning-config.md": "aad463ddda23dbaf",
"gsd-core/references/planning-config.md": "37ab69107a2f7dc6",
"gsd-core/references/prohibition-probe-fixtures/01-streak-reminder/expected.json": "f10df472f2846cc6",
"gsd-core/references/prohibition-probe-fixtures/02-clean-utility/expected.json": "31e8a781eeffe020",
"gsd-core/references/prohibition-probe-fixtures/03-multi-prohibition/expected.json": "70a532a7cc1b6ae8",
@@ -277,13 +278,13 @@
"gsd-core/workflows/autonomous.md": "6cb0c014f5f56965",
"gsd-core/workflows/check-todos.md": "6526b64ee88c7d2e",
"gsd-core/workflows/cleanup.md": "94d771d26f62dc86",
"gsd-core/workflows/code-review-fix.md": "5d97b960da4fef73",
"gsd-core/workflows/code-review.md": "aec8ef4de1829607",
"gsd-core/workflows/code-review-fix.md": "adb9388bb157610c",
"gsd-core/workflows/code-review.md": "ae6bcbd1575aeec4",
"gsd-core/workflows/complete-milestone.md": "614299b2c08e66c3",
"gsd-core/workflows/debug.md": "9d4a8afc8d36be93",
"gsd-core/workflows/diagnose-issues.md": "2971c699d52f1b85",
"gsd-core/workflows/discovery-phase.md": "724408336596c50c",
"gsd-core/workflows/discuss-phase-assumptions.md": "310b08dc31710cbc",
"gsd-core/workflows/discuss-phase-assumptions.md": "92e43cd12200c610",
"gsd-core/workflows/discuss-phase-power.md": "5c0d1ca6abda0383",
"gsd-core/workflows/discuss-phase.md": "3a62a8d4c374c69f",
"gsd-core/workflows/discuss-phase/modes/advisor.md": "83ca3ea4fac785af",
@@ -349,7 +350,7 @@
"gsd-core/workflows/pr-branch.md": "929b7cb0c99c7b9e",
"gsd-core/workflows/profile-user.md": "248d59a31948e0ed",
"gsd-core/workflows/progress.md": "8fc3404087f50b95",
"gsd-core/workflows/quick.md": "10cc8ddd4bc3f0ee",
"gsd-core/workflows/quick.md": "9bdccb7c6a5530c9",
"gsd-core/workflows/reapply-patches.md": "a0e9b53f90abceb2",
"gsd-core/workflows/remove-phase.md": "dea4661e8f89596f",
"gsd-core/workflows/remove-workspace.md": "446847e71aa52504",
@@ -379,7 +380,7 @@
"gsd-core/workflows/update.md": "71b6cd852f38b4bc",
"gsd-core/workflows/validate-phase.md": "abcdbc1b56780565",
"gsd-core/workflows/verify-phase.md": "126be1d026900102",
"gsd-core/workflows/verify-work.md": "f0d205568abfaf74",
"gsd-core/workflows/verify-work.md": "ae07b3fa8b6f864e",
"hooks/gsd-check-update-worker.js": "385fb7c67810baf6",
"hooks/gsd-check-update.js": "4549451414ffa7d7",
"hooks/gsd-config-reload.js": "96546e0e8bb47904",

View File

@@ -52,6 +52,7 @@
"gsd-core/references/agent-skills-bootstrap.md": "be09755fed7ad856",
"gsd-core/references/ai-evals.md": "b5afa786b938671e",
"gsd-core/references/ai-frameworks.md": "f827de93dde124eb",
"gsd-core/references/api-coverage.md": "205a43c5fa7c221c",
"gsd-core/references/artifact-types.md": "8bd01fd75a2ba70e",
"gsd-core/references/autonomous-smart-discuss.md": "2fc710cde0ec7785",
"gsd-core/references/checkpoints.md": "046171320f816346",
@@ -93,7 +94,7 @@
"gsd-core/references/loop-hook-dispatch.md": "32e5dfb4dba76987",
"gsd-core/references/mandatory-initial-read.md": "fe59abce693717cf",
"gsd-core/references/model-profile-resolution.md": "f32bb05102839767",
"gsd-core/references/model-profiles.md": "cc1efc2942164744",
"gsd-core/references/model-profiles.md": "0b7e06ed2e4abac8",
"gsd-core/references/mvp-concepts.md": "3464783eaaef5c10",
"gsd-core/references/phase-argument-parsing.md": "e5bbb985f3bc3e34",
"gsd-core/references/planner-antipatterns.md": "7ed54ec1e2cc54ac",
@@ -108,7 +109,7 @@
"gsd-core/references/planner-reviews.md": "da39eace09a10743",
"gsd-core/references/planner-revision.md": "86ba8a511f081f05",
"gsd-core/references/planner-source-audit.md": "7de5bdb07232ce0b",
"gsd-core/references/planning-config.md": "0037314fe38ca55c",
"gsd-core/references/planning-config.md": "4c6de9b6d66aca73",
"gsd-core/references/prohibition-probe-fixtures/01-streak-reminder/expected.json": "f10df472f2846cc6",
"gsd-core/references/prohibition-probe-fixtures/02-clean-utility/expected.json": "31e8a781eeffe020",
"gsd-core/references/prohibition-probe-fixtures/03-multi-prohibition/expected.json": "70a532a7cc1b6ae8",
@@ -206,13 +207,13 @@
"gsd-core/workflows/autonomous.md": "3014ff90115f0daf",
"gsd-core/workflows/check-todos.md": "ec8c22920f6b2df1",
"gsd-core/workflows/cleanup.md": "6a18b165752e3087",
"gsd-core/workflows/code-review-fix.md": "e14afce87cf0d420",
"gsd-core/workflows/code-review.md": "d6d385b6dadae5a0",
"gsd-core/workflows/code-review-fix.md": "f3725ae9d685bed2",
"gsd-core/workflows/code-review.md": "29125604bed2c467",
"gsd-core/workflows/complete-milestone.md": "40085d32b15805c8",
"gsd-core/workflows/debug.md": "c23d067580b09f63",
"gsd-core/workflows/diagnose-issues.md": "652ae26975f82242",
"gsd-core/workflows/discovery-phase.md": "6161c60d752d0058",
"gsd-core/workflows/discuss-phase-assumptions.md": "3865afc23e9cd46a",
"gsd-core/workflows/discuss-phase-assumptions.md": "18712f78bb960ec8",
"gsd-core/workflows/discuss-phase-power.md": "290c0d83d783f9f6",
"gsd-core/workflows/discuss-phase.md": "8644b72386a51241",
"gsd-core/workflows/discuss-phase/modes/advisor.md": "3102430dfad9c012",
@@ -278,7 +279,7 @@
"gsd-core/workflows/pr-branch.md": "cef0f65b16d500b4",
"gsd-core/workflows/profile-user.md": "263c0693563d98da",
"gsd-core/workflows/progress.md": "3b1b2142a74af85c",
"gsd-core/workflows/quick.md": "0a9a7dacc73f8e53",
"gsd-core/workflows/quick.md": "bb9a4a145a7edc36",
"gsd-core/workflows/reapply-patches.md": "de0ee8acfe7245b2",
"gsd-core/workflows/remove-phase.md": "e8ae4fbbfac700f0",
"gsd-core/workflows/remove-workspace.md": "4ac64de862dc650e",
@@ -308,7 +309,7 @@
"gsd-core/workflows/update.md": "b34cb866152d4de3",
"gsd-core/workflows/validate-phase.md": "e989cbaa4228564c",
"gsd-core/workflows/verify-phase.md": "0dc52b9e629a5f5a",
"gsd-core/workflows/verify-work.md": "b5afb65fdf311301",
"gsd-core/workflows/verify-work.md": "3355ddc14f052fff",
"hooks/gsd-check-update-worker.js": "4bb354044e0dff91",
"hooks/gsd-check-update.js": "d2065cb3e725a42a",
"hooks/gsd-config-reload.js": "4f52b8a0120bb1b8",

View File

@@ -52,6 +52,7 @@
"gsd-core/references/agent-skills-bootstrap.md": "18d1ff7c7fa0bab1",
"gsd-core/references/ai-evals.md": "b5afa786b938671e",
"gsd-core/references/ai-frameworks.md": "f827de93dde124eb",
"gsd-core/references/api-coverage.md": "205a43c5fa7c221c",
"gsd-core/references/artifact-types.md": "8bd01fd75a2ba70e",
"gsd-core/references/autonomous-smart-discuss.md": "2fc710cde0ec7785",
"gsd-core/references/checkpoints.md": "4b8c645ffa695067",
@@ -93,7 +94,7 @@
"gsd-core/references/loop-hook-dispatch.md": "32e5dfb4dba76987",
"gsd-core/references/mandatory-initial-read.md": "fe59abce693717cf",
"gsd-core/references/model-profile-resolution.md": "f32bb05102839767",
"gsd-core/references/model-profiles.md": "41b0af6f5f77af81",
"gsd-core/references/model-profiles.md": "b4527b0f255d193f",
"gsd-core/references/mvp-concepts.md": "3464783eaaef5c10",
"gsd-core/references/phase-argument-parsing.md": "e562947d1bf5c5a4",
"gsd-core/references/planner-antipatterns.md": "7ed54ec1e2cc54ac",
@@ -108,7 +109,7 @@
"gsd-core/references/planner-reviews.md": "da39eace09a10743",
"gsd-core/references/planner-revision.md": "86ba8a511f081f05",
"gsd-core/references/planner-source-audit.md": "7de5bdb07232ce0b",
"gsd-core/references/planning-config.md": "e47cdb2337e10dac",
"gsd-core/references/planning-config.md": "a686c1363ae626c5",
"gsd-core/references/prohibition-probe-fixtures/01-streak-reminder/expected.json": "f10df472f2846cc6",
"gsd-core/references/prohibition-probe-fixtures/02-clean-utility/expected.json": "31e8a781eeffe020",
"gsd-core/references/prohibition-probe-fixtures/03-multi-prohibition/expected.json": "70a532a7cc1b6ae8",
@@ -206,13 +207,13 @@
"gsd-core/workflows/autonomous.md": "c482645c5d1ead46",
"gsd-core/workflows/check-todos.md": "0dda8236355e8c9c",
"gsd-core/workflows/cleanup.md": "82f65f5214ecd748",
"gsd-core/workflows/code-review-fix.md": "959a65773b325ed2",
"gsd-core/workflows/code-review.md": "b31bbd7dbe5ed303",
"gsd-core/workflows/code-review-fix.md": "f2761f7f8c4a5674",
"gsd-core/workflows/code-review.md": "47663a2922756c5e",
"gsd-core/workflows/complete-milestone.md": "6e918b72bd885426",
"gsd-core/workflows/debug.md": "197d3642a6704de5",
"gsd-core/workflows/diagnose-issues.md": "9274b11a3db98c65",
"gsd-core/workflows/discovery-phase.md": "b32b6197b66c9a13",
"gsd-core/workflows/discuss-phase-assumptions.md": "3d05b08eae75c7bd",
"gsd-core/workflows/discuss-phase-assumptions.md": "e376b1cf29379df4",
"gsd-core/workflows/discuss-phase-power.md": "290c0d83d783f9f6",
"gsd-core/workflows/discuss-phase.md": "4509657816c5566a",
"gsd-core/workflows/discuss-phase/modes/advisor.md": "c9cd7db62e76ebcb",
@@ -278,7 +279,7 @@
"gsd-core/workflows/pr-branch.md": "79fd55b88ea2db9c",
"gsd-core/workflows/profile-user.md": "672821e6b1266645",
"gsd-core/workflows/progress.md": "94768f835b0b8908",
"gsd-core/workflows/quick.md": "9bbf907e39688638",
"gsd-core/workflows/quick.md": "a568015cccb53615",
"gsd-core/workflows/reapply-patches.md": "21b38c374f19fd78",
"gsd-core/workflows/remove-phase.md": "a46c2fe853bf4e86",
"gsd-core/workflows/remove-workspace.md": "ae0e1c6d4438d663",
@@ -308,7 +309,7 @@
"gsd-core/workflows/update.md": "1f935251fca1f276",
"gsd-core/workflows/validate-phase.md": "1c0ebe56d96a14d1",
"gsd-core/workflows/verify-phase.md": "a151ed36eb51813b",
"gsd-core/workflows/verify-work.md": "d3a0970205acc6a5",
"gsd-core/workflows/verify-work.md": "dde2a42a56c27c4e",
"scripts/changeset/README.md": "86ff89331dfd94b2",
"scripts/changeset/cli.cjs": "68f92a344b199271",
"scripts/changeset/github-release-notes.cjs": "795677f0c009b132",

View File

@@ -52,6 +52,7 @@
"gsd-core/references/agent-skills-bootstrap.md": "849977da771f2b06",
"gsd-core/references/ai-evals.md": "b5afa786b938671e",
"gsd-core/references/ai-frameworks.md": "f827de93dde124eb",
"gsd-core/references/api-coverage.md": "205a43c5fa7c221c",
"gsd-core/references/artifact-types.md": "8bd01fd75a2ba70e",
"gsd-core/references/autonomous-smart-discuss.md": "273b371c5751f35a",
"gsd-core/references/checkpoints.md": "808e4fcaa2fda15c",
@@ -93,7 +94,7 @@
"gsd-core/references/loop-hook-dispatch.md": "32e5dfb4dba76987",
"gsd-core/references/mandatory-initial-read.md": "fe59abce693717cf",
"gsd-core/references/model-profile-resolution.md": "f32bb05102839767",
"gsd-core/references/model-profiles.md": "b3ac5dc094bce1dc",
"gsd-core/references/model-profiles.md": "09aa53e3f1764a41",
"gsd-core/references/mvp-concepts.md": "3464783eaaef5c10",
"gsd-core/references/phase-argument-parsing.md": "e562947d1bf5c5a4",
"gsd-core/references/planner-antipatterns.md": "7ed54ec1e2cc54ac",
@@ -108,7 +109,7 @@
"gsd-core/references/planner-reviews.md": "da39eace09a10743",
"gsd-core/references/planner-revision.md": "86ba8a511f081f05",
"gsd-core/references/planner-source-audit.md": "7de5bdb07232ce0b",
"gsd-core/references/planning-config.md": "aafbfc62a3bb84b1",
"gsd-core/references/planning-config.md": "1fc70920778d7c8e",
"gsd-core/references/prohibition-probe-fixtures/01-streak-reminder/expected.json": "f10df472f2846cc6",
"gsd-core/references/prohibition-probe-fixtures/02-clean-utility/expected.json": "31e8a781eeffe020",
"gsd-core/references/prohibition-probe-fixtures/03-multi-prohibition/expected.json": "70a532a7cc1b6ae8",
@@ -206,13 +207,13 @@
"gsd-core/workflows/autonomous.md": "fe7bb7c978f305a2",
"gsd-core/workflows/check-todos.md": "afc840fceb07bf99",
"gsd-core/workflows/cleanup.md": "93c14107979a4428",
"gsd-core/workflows/code-review-fix.md": "9b2de93a6087774d",
"gsd-core/workflows/code-review.md": "2f0ebfb7a2e33764",
"gsd-core/workflows/code-review-fix.md": "b99b1f20bb27c291",
"gsd-core/workflows/code-review.md": "faa87faf07ae765a",
"gsd-core/workflows/complete-milestone.md": "f463bf4e86ac26f6",
"gsd-core/workflows/debug.md": "52243eb936a43150",
"gsd-core/workflows/diagnose-issues.md": "447072aa72385271",
"gsd-core/workflows/discovery-phase.md": "7dcf150998559c11",
"gsd-core/workflows/discuss-phase-assumptions.md": "f57000d5c5fb178a",
"gsd-core/workflows/discuss-phase-assumptions.md": "b091b3d3e580dd29",
"gsd-core/workflows/discuss-phase-power.md": "290c0d83d783f9f6",
"gsd-core/workflows/discuss-phase.md": "ce4e6abce9cb2d4e",
"gsd-core/workflows/discuss-phase/modes/advisor.md": "cb49f485c4a1f09e",
@@ -278,7 +279,7 @@
"gsd-core/workflows/pr-branch.md": "acd59f915d018ad4",
"gsd-core/workflows/profile-user.md": "c4313672b81b5bcd",
"gsd-core/workflows/progress.md": "18813a345bd2343a",
"gsd-core/workflows/quick.md": "3acd391cc0c3813a",
"gsd-core/workflows/quick.md": "b7813e1810c683e1",
"gsd-core/workflows/reapply-patches.md": "d449a23d3acf6379",
"gsd-core/workflows/remove-phase.md": "e7a6af429b36e77b",
"gsd-core/workflows/remove-workspace.md": "b5e60fbb33b3e33a",
@@ -308,7 +309,7 @@
"gsd-core/workflows/update.md": "79aaf4b8f1f83045",
"gsd-core/workflows/validate-phase.md": "2db47bf5547d7b9d",
"gsd-core/workflows/verify-phase.md": "f961cdb3ef03ff05",
"gsd-core/workflows/verify-work.md": "5ad63a5edfb6acac",
"gsd-core/workflows/verify-work.md": "cce8ae44b30957f1",
"scripts/changeset/README.md": "86ff89331dfd94b2",
"scripts/changeset/cli.cjs": "68f92a344b199271",
"scripts/changeset/github-release-notes.cjs": "795677f0c009b132",

View File

@@ -123,6 +123,7 @@
"gsd-core/references/agent-skills-bootstrap.md": "5ab875054b1adda9",
"gsd-core/references/ai-evals.md": "b5afa786b938671e",
"gsd-core/references/ai-frameworks.md": "f827de93dde124eb",
"gsd-core/references/api-coverage.md": "66264d41dfd9154a",
"gsd-core/references/artifact-types.md": "a6d2e1f9453ffbf5",
"gsd-core/references/autonomous-smart-discuss.md": "2fc710cde0ec7785",
"gsd-core/references/checkpoints.md": "6aa620c6ca38bdf0",
@@ -164,7 +165,7 @@
"gsd-core/references/loop-hook-dispatch.md": "32e5dfb4dba76987",
"gsd-core/references/mandatory-initial-read.md": "fe59abce693717cf",
"gsd-core/references/model-profile-resolution.md": "f32bb05102839767",
"gsd-core/references/model-profiles.md": "8484ad9799b7f680",
"gsd-core/references/model-profiles.md": "e067ad3df6770db1",
"gsd-core/references/mvp-concepts.md": "72f7e8b1f8ae7118",
"gsd-core/references/phase-argument-parsing.md": "e5bbb985f3bc3e34",
"gsd-core/references/planner-antipatterns.md": "7ed54ec1e2cc54ac",
@@ -179,7 +180,7 @@
"gsd-core/references/planner-reviews.md": "dda0193a0fbd4947",
"gsd-core/references/planner-revision.md": "86ba8a511f081f05",
"gsd-core/references/planner-source-audit.md": "7de5bdb07232ce0b",
"gsd-core/references/planning-config.md": "eb168188abd00101",
"gsd-core/references/planning-config.md": "ac409835e8260a3e",
"gsd-core/references/prohibition-probe-fixtures/01-streak-reminder/expected.json": "f10df472f2846cc6",
"gsd-core/references/prohibition-probe-fixtures/02-clean-utility/expected.json": "31e8a781eeffe020",
"gsd-core/references/prohibition-probe-fixtures/03-multi-prohibition/expected.json": "70a532a7cc1b6ae8",
@@ -277,13 +278,13 @@
"gsd-core/workflows/autonomous.md": "7e5683728ef33707",
"gsd-core/workflows/check-todos.md": "bdeaf43f9c61e0cc",
"gsd-core/workflows/cleanup.md": "0daa2f2720c111f1",
"gsd-core/workflows/code-review-fix.md": "c323f7098b671bd6",
"gsd-core/workflows/code-review.md": "ad450c6ef8459dad",
"gsd-core/workflows/code-review-fix.md": "2e113d1f4350a075",
"gsd-core/workflows/code-review.md": "334c90c401f291f8",
"gsd-core/workflows/complete-milestone.md": "c1f91b77f4ace7f2",
"gsd-core/workflows/debug.md": "849d7e5b9c12dcae",
"gsd-core/workflows/diagnose-issues.md": "aa8d787db8f3c46c",
"gsd-core/workflows/discovery-phase.md": "3ba7cfb89fb1e761",
"gsd-core/workflows/discuss-phase-assumptions.md": "1e229f6d21831d60",
"gsd-core/workflows/discuss-phase-assumptions.md": "f5b765d33eba4f88",
"gsd-core/workflows/discuss-phase-power.md": "0841f7dc6e9a054a",
"gsd-core/workflows/discuss-phase.md": "d0a67b6f77b4d339",
"gsd-core/workflows/discuss-phase/modes/advisor.md": "db488d74d080c653",
@@ -349,7 +350,7 @@
"gsd-core/workflows/pr-branch.md": "c87db7ac8c28be1b",
"gsd-core/workflows/profile-user.md": "e23bea0a69c0bb4b",
"gsd-core/workflows/progress.md": "893aa3c36983f74b",
"gsd-core/workflows/quick.md": "5747c2d8a37affbb",
"gsd-core/workflows/quick.md": "12917fdc9a624a0c",
"gsd-core/workflows/reapply-patches.md": "39050f72601aec89",
"gsd-core/workflows/remove-phase.md": "df9a45f0b1880999",
"gsd-core/workflows/remove-workspace.md": "a7ca66db6b7c132c",
@@ -379,7 +380,7 @@
"gsd-core/workflows/update.md": "dc580dee13f881a6",
"gsd-core/workflows/validate-phase.md": "2c6d7671fcaabcaa",
"gsd-core/workflows/verify-phase.md": "22f18492581f1da5",
"gsd-core/workflows/verify-work.md": "34e980a6950cd83c",
"gsd-core/workflows/verify-work.md": "63b3f680d8f0a6f3",
"scripts/changeset/README.md": "86ff89331dfd94b2",
"scripts/changeset/cli.cjs": "68f92a344b199271",
"scripts/changeset/github-release-notes.cjs": "795677f0c009b132",

View File

@@ -5,7 +5,10 @@
* Hook points tested: discuss:pre, discuss:post, execute:pre, execute:wave:pre,
* verify:pre, ship:post
*
* All 6 points have zero hooks in the real registry by design.
* 5 of these points have zero hooks in the real registry by design.
* verify:pre graduated out of the empty set in #1562 (it now carries the
* ai-integration `api-coverage.verify-pre` blocking gate); SECTION 5 pins the
* new contract + retains synthetic extension-point-mechanics coverage.
* Tests pin: exact envelope shape, placeholder string contract (Hyrum's Law),
* resolver-filter mechanics (schema default / config-override / capabilityStatesById),
* CLI contract (missing-arg, invalid-point), and Postel-leniency (malformed config).
@@ -481,8 +484,13 @@ describe('execute:wave:pre — real registry empty-resolution + synthetic mechan
// ─────────────────────────────────────────────────────────────────────────────
// SECTION 5: verify:pre
// ─────────────────────────────────────────────────────────────────────────────
// NOTE: verify:pre was an empty extension point until #1562 added the
// ai-integration `api-coverage.verify-pre` blocking gate (default-on, opt-out
// via workflow.api_coverage_gate=false). The real-registry assertions below pin
// the NEW contract; the synthetic BVA tests retain extension-point-mechanics
// coverage. Empty-point coverage for the other 5 points is unaffected.
describe('verify:pre — real registry empty-resolution + synthetic extension-point readiness', () => {
describe('verify:pre — real registry carries the api-coverage gate (#1562); synthetic mechanics', () => {
let tmpEmptyProjectDir;
let tmpProjectDirAllOn;
before(() => {
@@ -494,29 +502,47 @@ describe('verify:pre — real registry empty-resolution + synthetic extension-po
cleanup(tmpProjectDirAllOn);
});
it('[empty-resolution] verify:pre with real registry and no config yields empty activeHooks and exact placeholder (Gall\'s Law)', () => {
it('[default-on] verify:pre with real registry and no config yields the api-coverage blocking gate (full-coverage-by-default)', () => {
const resolved = resolveLoopHooks({ point: 'verify:pre', registry: realRegistry, config: {} });
assert.strictEqual(resolved.activeHooks.length, 0);
assert.strictEqual(renderLoopHooks(resolved), '_No active hooks at verify:pre._');
const gate = resolved.activeHooks.find((h) => h.capId === 'ai-integration' && h.kind === 'gate');
assert.ok(gate, 'ai-integration api-coverage gate must register at verify:pre by default');
assert.strictEqual(gate.blocking, true);
assert.strictEqual(gate.check.query, 'api-coverage.verify-pre');
});
it('[happy] verify:pre E2E subprocess returns well-formed 3-key JSON envelope with empty activeHooks (Hyrum\'s Law contract pin)', () => {
it('[happy] verify:pre E2E subprocess returns a well-formed envelope carrying the gate (Hyrum\'s Law contract pin)', () => {
const result = spawnGsd(['loop', 'render-hooks', 'verify:pre', '--cwd', tmpEmptyProjectDir, '--raw'], tmpEmptyProjectDir);
assert.strictEqual(result.status, 0, `expected exit 0. stderr: ${result.stderr}`);
const envelope = JSON.parse(result.stdout.trim());
assert.strictEqual(envelope.point, 'verify:pre');
assert.deepEqual(envelope.activeHooks, []);
assert.strictEqual(envelope.rendered, '_No active hooks at verify:pre._');
assert.ok(Array.isArray(envelope.activeHooks));
assert.ok(envelope.activeHooks.some((h) => h.capId === 'ai-integration' && h.kind === 'gate'));
assert.deepEqual(Object.keys(envelope).sort(), ['activeHooks', 'point', 'rendered']);
});
it('[negative] verify:pre with all capability config keys set to true still yields empty activeHooks — no leakage from other points', () => {
it('[opt-out] verify:pre with workflow.api_coverage_gate=false yields NO ai-integration gate — config opt-out empties the point', () => {
const resolved = resolveLoopHooks({
point: 'verify:pre',
registry: realRegistry,
config: { workflow: { api_coverage_gate: false } },
});
assert.strictEqual(
resolved.activeHooks.find((h) => h.capId === 'ai-integration'),
undefined,
'opting out api_coverage_gate must remove the gate from verify:pre'
);
});
it('[negative] verify:pre with unrelated capability keys on does not bleed non-verify:pre hooks into the point', () => {
const resolved = resolveLoopHooks({
point: 'verify:pre',
registry: realRegistry,
config: { workflow: { ui_phase: true, ui_review: true, ui_safety_gate: true } },
});
assert.strictEqual(resolved.activeHooks.length, 0, 'UI and other capabilities must not bleed through to verify:pre');
// The only verify:pre hook is the api-coverage gate; UI/other hooks must not bleed in.
for (const h of resolved.activeHooks) {
assert.notStrictEqual(h.capId, 'ui', 'UI capability must not bleed through to verify:pre');
}
});
it('[bva] Synthetic step at verify:pre with configSchema default=true fires correctly — extension point readiness', () => {
@@ -547,7 +573,10 @@ describe('verify:pre — real registry empty-resolution + synthetic extension-po
const result = spawnGsd(['loop', 'render-hooks', 'verify:pre', '--cwd', malformedDir, '--raw'], malformedDir);
assert.strictEqual(result.status, 0, `must not crash on malformed config. stderr: ${result.stderr}`);
const envelope = JSON.parse(result.stdout.trim());
assert.deepEqual(envelope.activeHooks, []);
// Malformed config degrades to defaults (gate default-on); the contract
// here is leniency (exit 0, well-formed envelope), not emptiness.
assert.strictEqual(envelope.point, 'verify:pre');
assert.ok(Array.isArray(envelope.activeHooks));
} finally {
cleanup(malformedDir);
}
@@ -720,13 +749,12 @@ describe('CLI contract — missing/invalid point argument (shared across all 6 e
// SECTION 8: Parametric empty-point sweep across all 6 points (E2E regression guard)
// ─────────────────────────────────────────────────────────────────────────────
describe('Parametric E2E sweep — 5 empty points return correct envelope shape via real registry (ship:post excluded — mempalace registers 1 step there)', () => {
describe('Parametric E2E sweep — 4 empty points return correct envelope shape via real registry (ship:post excluded — mempalace registers 1 step there; verify:pre excluded since #1562 added the api-coverage gate)', () => {
const EMPTY_POINTS = [
'discuss:pre',
'discuss:post',
'execute:pre',
'execute:wave:pre',
'verify:pre',
];
for (const point of EMPTY_POINTS) {

View File

@@ -120,6 +120,36 @@ describe('resolveModelInternal', () => {
assert.ok(typeof model === 'string' && model.length > 0);
});
// #2072 acceptance: these two catalog agents' config MUST resolve — the bug was
// that the workflows never threaded the resolved value, not that the resolver
// ignored it. These assert the value the (now-threaded) spawns receive.
test('#2072: model_overrides applies to gsd-code-reviewer', () => {
writeConfig(tmpDir, { model_overrides: { 'gsd-code-reviewer': 'my-custom-model' } });
assert.strictEqual(resolveModelInternal(tmpDir, 'gsd-code-reviewer'), 'my-custom-model');
});
test('#2072: model_overrides applies to gsd-assumptions-analyzer', () => {
writeConfig(tmpDir, { model_overrides: { 'gsd-assumptions-analyzer': 'my-custom-model' } });
assert.strictEqual(resolveModelInternal(tmpDir, 'gsd-assumptions-analyzer'), 'my-custom-model');
});
test('#2072: models.verification tier applies to gsd-code-reviewer', () => {
writeConfig(tmpDir, { models: { verification: 'opus' } });
assert.strictEqual(resolveModelInternal(tmpDir, 'gsd-code-reviewer'), 'opus');
});
test('#2072: models.discuss tier applies to gsd-assumptions-analyzer', () => {
writeConfig(tmpDir, { models: { discuss: 'opus' } });
assert.strictEqual(resolveModelInternal(tmpDir, 'gsd-assumptions-analyzer'), 'opus');
});
test('#2072: model_overrides + models.execution apply to gsd-code-fixer', () => {
writeConfig(tmpDir, { model_overrides: { 'gsd-code-fixer': 'my-custom-model' } });
assert.strictEqual(resolveModelInternal(tmpDir, 'gsd-code-fixer'), 'my-custom-model');
writeConfig(tmpDir, { models: { execution: 'opus' } });
assert.strictEqual(resolveModelInternal(tmpDir, 'gsd-code-fixer'), 'opus');
});
test('runtime non-claude + model_profile_overrides for runtime tier', () => {
writeConfig(tmpDir, {
runtime: 'codex',

View File

@@ -0,0 +1,150 @@
// allow-test-rule: source-text-is-the-product #2072
// Workflow .md files ARE the deployed orchestration contract the runtime executes;
// asserting that a spawn threads a resolved model= is asserting the deployed contract.
'use strict';
/**
* #2072 — model_overrides / models.<phaseType> were silently inert for
* gsd-assumptions-analyzer and gsd-code-reviewer on Claude: the resolver honored
* them, but the workflows spawned the agents with NO model= param, so the resolved
* value never reached the Agent tool and the agents inherited the session model.
*
* Fix contract: every spawn of these two catalog agents must thread a resolved
* model=, and the workflow must obtain that model (inline `resolve-model` for the
* single-agent workflows, or the `reviewer_model` field of the init.quick bundle).
*/
const { test, describe } = require('node:test');
const assert = require('node:assert/strict');
const fs = require('node:fs');
const path = require('node:path');
const WF = path.join(__dirname, '..', 'gsd-core', 'workflows');
const read = (rel) => fs.readFileSync(path.join(WF, rel), 'utf-8');
// Every .md under gsd-core/workflows (incl. nested steps/ and modes/).
function allWorkflowMd() {
const out = [];
(function walk(dir, rel) {
for (const e of fs.readdirSync(dir, { withFileTypes: true })) {
if (e.isDirectory()) walk(path.join(dir, e.name), path.join(rel, e.name));
else if (e.name.endsWith('.md')) out.push(path.join(rel, e.name));
}
})(WF, '');
return out;
}
// Return the full text of every `Agent( … )` call in `content`, tracking string
// state (triple- and single-double-quoted) and paren depth so a prompt body's own
// parens/quotes don't end the call early. Order-independent: a call's params are
// captured whether subagent_type= appears before or after the prompt.
function agentCalls(content) {
const calls = [];
const re = /Agent\(/g;
let m;
while ((m = re.exec(content)) !== null) {
let i = m.index + m[0].length;
let depth = 1;
let tq = false; // inside """ … """
let sq = false; // inside " … "
while (i < content.length && depth > 0) {
if (tq) {
if (content.startsWith('"""', i)) { tq = false; i += 3; continue; }
i++; continue;
}
if (sq) {
if (content[i] === '\\') { i += 2; continue; }
if (content[i] === '"') { sq = false; }
i++; continue;
}
if (content.startsWith('"""', i)) { tq = true; i += 3; continue; }
if (content[i] === '"') { sq = true; i++; continue; }
if (content[i] === '(') { depth++; }
else if (content[i] === ')') { depth--; }
i++;
}
calls.push(content.slice(m.index, i));
re.lastIndex = i; // don't re-scan inside this call
}
return calls;
}
describe('#2072: routed-agent spawns thread the resolved model', () => {
test('discuss-phase-assumptions.md resolves + threads gsd-assumptions-analyzer model', () => {
const c = read('discuss-phase-assumptions.md');
assert.match(c, /resolve-model gsd-assumptions-analyzer/, 'must resolve the routed model');
assert.match(
c,
/subagent_type="gsd-assumptions-analyzer",\s*model="\{ANALYZER_MODEL\}"/,
'spawn must thread the resolved model=',
);
});
test('code-review.md resolves + threads gsd-code-reviewer model', () => {
const c = read('code-review.md');
assert.match(c, /resolve-model gsd-code-reviewer/);
assert.match(c, /subagent_type="gsd-code-reviewer",\s*model="\{REVIEWER_MODEL\}"/);
});
test('code-review-fix.md re-review resolves + threads gsd-code-reviewer model', () => {
const c = read('code-review-fix.md');
assert.match(c, /resolve-model gsd-code-reviewer/);
assert.match(c, /subagent_type="gsd-code-reviewer",\s*model="\{REVIEWER_MODEL\}"/);
});
test('quick.md review step threads gsd-code-reviewer own model (not executor_model)', () => {
const c = read('quick.md');
// reviewer_model comes from the init.quick bundle; the spawn must use it.
assert.match(c, /subagent_type="gsd-code-reviewer",\s*\n\s*model="\{reviewer_model\}"/);
assert.doesNotMatch(
c,
/subagent_type="gsd-code-reviewer",\s*\n\s*model="\{executor_model\}"/,
'reviewer must not reuse the executor model (own model_overrides would be ignored)',
);
});
test('code-review-fix.md threads gsd-code-fixer model at both fixer spawns', () => {
const c = read('code-review-fix.md');
assert.match(c, /resolve-model gsd-code-fixer/, 'must resolve the fixer model');
const fixerSpawns = c.match(/subagent_type="gsd-code-fixer", model="\{FIXER_MODEL\}"/g) || [];
assert.strictEqual(fixerSpawns.length, 2, 'both gsd-code-fixer spawns must thread FIXER_MODEL');
});
// Parity guard: EVERY spawn of the fixed routed agents across ALL workflows must
// carry a model= in its Agent(...) call, so a new silently-inert spawn cannot
// regress. Uses a quote/paren-aware scan of the WHOLE Agent(...) call, so it holds
// for single-line and multi-line calls, multiple spawns per file, and either param
// ordering (subagent_type= before OR after the prompt body).
test('no spawn of the fixed routed agents is missing a model= (parity guard)', () => {
const ROUTED = /subagent_type="(gsd-code-reviewer|gsd-assumptions-analyzer|gsd-code-fixer)"/;
const offenders = [];
for (const rel of allWorkflowMd()) {
for (const call of agentCalls(read(rel))) {
const routed = call.match(ROUTED);
if (routed && !/\bmodel\s*=/.test(call)) {
offenders.push(`${rel}: ${routed[1]} spawn missing model=`);
}
}
}
assert.deepEqual(offenders, [], `routed-agent spawn(s) missing model=:\n${offenders.join('\n')}`);
});
// The scanner itself must catch a prompt-first, un-threaded spawn (the exact blind
// spot a naive "params before prompt=" heuristic misses) — otherwise the guard
// above could pass vacuously.
test('parity guard detects a prompt-first spawn that omits model=', () => {
const synthetic = [
'Agent(',
' prompt="""do the thing (with parens) and a " quote""",',
' subagent_type="gsd-code-reviewer"',
')',
].join('\n');
const [call] = agentCalls(synthetic);
assert.ok(/subagent_type="gsd-code-reviewer"/.test(call), 'scanner must capture the prompt-first subagent_type');
assert.ok(!/\bmodel\s*=/.test(call), 'and correctly see that model= is absent');
// A well-formed prompt-first spawn WITH model= must be accepted.
const ok = agentCalls(synthetic.replace(')', ' model="{reviewer_model}"\n)'))[0];
assert.ok(/\bmodel\s*=/.test(ok));
});
});

View File

@@ -246,6 +246,7 @@ describe('plan:pre all-off — empty resolution', () => {
tmpDir = makeProject({
workflow: {
ai_integration_phase: false,
api_coverage_gate: false,
tdd_mode: false,
security_enforcement: false,
ui_phase: false,

View File

@@ -11,13 +11,13 @@
"autonomous.md": 42474,
"check-todos.md": 9475,
"cleanup.md": 9941,
"code-review-fix.md": 23934,
"code-review.md": 31646,
"code-review-fix.md": 24320,
"code-review.md": 31916,
"complete-milestone.md": 31071,
"debug.md": 13549,
"diagnose-issues.md": 12864,
"discovery-phase.md": 8651,
"discuss-phase-assumptions.md": 27028,
"discuss-phase-assumptions.md": 27302,
"discuss-phase-power.md": 11273,
"discuss-phase.md": 31986,
"do.md": 10353,
@@ -59,7 +59,7 @@
"pr-branch.md": 15963,
"profile-user.md": 21246,
"progress.md": 30599,
"quick.md": 50452,
"quick.md": 50470,
"reapply-patches.md": 20312,
"remove-phase.md": 8513,
"remove-workspace.md": 7551,
@@ -89,5 +89,5 @@
"update.md": 20914,
"validate-phase.md": 10789,
"verify-phase.md": 40923,
"verify-work.md": 38267
"verify-work.md": 40247
}