* test(#3335): fold the workflow-content & phase-lifecycle fix-* cluster — Wave 3 Folds 13 legacy fix-*.test.cjs regression files (131 test() blocks) into their module's main suite, per H3 (#3315) of the test-hygiene epic (#3053): - 6 files with no prior target coverage: renamed (git mv) into new suites (spike-manifest-scoping, ship-note, add-todo, workflow-jq-dependency, resolve-execution-dynamic-routing, clock) - 7 files merged into 5 pre-existing suites (worktree-base-ref x2, model-resolver, phase-locator x2, frontmatter, verification-status), deduplicated against existing coverage Zero net test-coverage loss: every source assertion preserved or verified as a genuine pre-existing duplicate. No production code changed. Last fix-* wave (Wave 1 #3341, Wave 2 #3342 already merged); 4 issue-* waves remain in #3315. * test(#3335): fix orthogonal-review findings — Wave 3 fold Standards-axis review + Memtrace graph pass found real defects in the just-folded suites, all fixed here: - phase-locator.test.cjs: pinned an unseeded fast-check property test (CONTRIBUTING.md determinism requirement), matching the sibling test's seed:7 convention. - phase-locator.test.cjs: added assert.ok() presence guards after 9 data.plans.find() calls that were dereferenced unguarded, inconsistent with 5 sibling tests in the same file that already guard correctly. Latent robustness gap — an omitted plan would throw an opaque TypeError instead of a clear assertion failure. - Standardized the fold-wrapper convention (block-scoped __foldDescribe) across worktree-base-ref.test.cjs, verification-status.test.cjs, and phase-locator.test.cjs to match the pattern already established in frontmatter.test.cjs and model-resolver.test.cjs from earlier folds. - worktree-base-ref.test.cjs: moved a mid-file require to the top-of-file require block. - Eliminated duplicated env-isolation helpers: model-resolver.test.cjs and phase-locator.test.cjs each reimplemented GSD_WORKSTREAM/GSD_PROJECT save-restore independently; factored a shared isolateWorkstreamEnv()/ restoreWorkstreamEnv() into tests/helpers.cjs and pointed both call sites at it. No test() count changed in any file. No production code touched. --------- Co-authored-by: sim <sim@local>
This commit is contained in:
@@ -14,6 +14,7 @@
|
||||
|
||||
const { describe, test } = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
|
||||
const { makeFaultyGit } = require('./helpers/faulty-deps.cjs');
|
||||
@@ -1037,3 +1038,199 @@ describe('cmdWorktreeBaseCheck — user/global cascade (#1013)', () => {
|
||||
assert.strictEqual(result.reason, 'fork-ref-unknown');
|
||||
});
|
||||
});
|
||||
|
||||
// ─── workflow dispatch-site coverage: worktree.base-check gates (folded from
|
||||
// fix-1941-quick-worktree-stale-base.test.cjs and
|
||||
// fix-2649-diagnose-issues-worktree-stale-base.test.cjs, #3335) ──────────────
|
||||
//
|
||||
// allow-test-rule: source-text-is-the-product #1941 #2649
|
||||
// Workflow .md files are the installed AI instructions — their text IS what the
|
||||
// runtime loads. Testing text content tests the deployed contract. Per
|
||||
// CONTRIBUTING.md exception matrix.
|
||||
//
|
||||
// Root cause shared by #1941 and #2649: Claude Code's isolation="worktree"
|
||||
// forks new worktrees from origin/HEAD, not the live local HEAD. When prior
|
||||
// local commits advance local HEAD without an intervening `git push`,
|
||||
// origin/HEAD stays pinned to a stale ancestor and the executor's
|
||||
// worktree_branch_check guard halts with a base-mismatch fatal. The fix ports
|
||||
// the worktree.base-check auto-degrade pattern (originally execute-phase
|
||||
// #683/#1369) into each not-yet-covered dispatch site: quick.md's
|
||||
// single-dispatch path (#1941), and diagnose-issues.md's spawn_agents step
|
||||
// plus execute-plan.md's Pattern A single-plan dispatch (#2649, fixed
|
||||
// together per that bug's acceptance criterion 5 — same bug class, same
|
||||
// one-line gate).
|
||||
|
||||
{
|
||||
const { describe: __foldDescribe } = require('node:test');
|
||||
__foldDescribe('folded:fix-1941-quick-worktree-stale-base', () => {
|
||||
|
||||
const QUICK_WORKFLOW_PATH = path.join(__dirname, '..', 'gsd-core', 'workflows', 'quick.md');
|
||||
|
||||
describe('quick: pre-dispatch worktree base re-check (#1941)', () => {
|
||||
test('workflow file exists', () => {
|
||||
assert.ok(fs.existsSync(QUICK_WORKFLOW_PATH), 'workflows/quick.md should exist');
|
||||
});
|
||||
|
||||
test('Step 6 runs worktree.base-check before capturing EXPECTED_BASE', () => {
|
||||
const content = fs.readFileSync(QUICK_WORKFLOW_PATH, 'utf-8');
|
||||
const step6Idx = content.indexOf('**Step 6: Spawn executor**');
|
||||
const baseCheckIdx = content.indexOf('worktree.base-check', step6Idx);
|
||||
const expectedBaseIdx = content.indexOf('EXPECTED_BASE=$(git rev-parse HEAD)', step6Idx);
|
||||
assert.ok(step6Idx !== -1, '"Step 6: Spawn executor" must exist in quick.md');
|
||||
assert.ok(baseCheckIdx !== -1, 'worktree.base-check must be invoked within Step 6');
|
||||
assert.ok(expectedBaseIdx !== -1, 'EXPECTED_BASE capture must exist within Step 6');
|
||||
assert.ok(
|
||||
baseCheckIdx < expectedBaseIdx,
|
||||
'worktree.base-check must run BEFORE EXPECTED_BASE is captured so the degrade decision reflects the most current local HEAD'
|
||||
);
|
||||
});
|
||||
|
||||
test('degrade check references #1941 for traceability', () => {
|
||||
const content = fs.readFileSync(QUICK_WORKFLOW_PATH, 'utf-8');
|
||||
assert.ok(content.includes('#1941'), 'quick.md must reference #1941');
|
||||
});
|
||||
|
||||
test('degrade check clears BOTH USE_WORKTREES and ISOLATION when shouldDegrade is true', () => {
|
||||
const content = fs.readFileSync(QUICK_WORKFLOW_PATH, 'utf-8');
|
||||
const baseCheckIdx = content.indexOf('worktree.base-check');
|
||||
const block = content.slice(baseCheckIdx, baseCheckIdx + 900);
|
||||
assert.ok(block.includes('shouldDegrade'), 'degrade check must branch on shouldDegrade');
|
||||
// Both must move together (#2652). Dispatch keys on ISOLATION while the prompt
|
||||
// guard and worktree manifest key on USE_WORKTREES; clearing only one dispatches
|
||||
// an isolated executor with no base guard and no manifest, then blocks in cleanup
|
||||
// looking for a manifest that was never initialized.
|
||||
assert.ok(block.includes('USE_WORKTREES=false'), 'degrade must set USE_WORKTREES=false');
|
||||
assert.ok(
|
||||
block.includes('ISOLATION=none'),
|
||||
'degrade must ALSO set ISOLATION=none — dispatch reads ISOLATION, so clearing only ' +
|
||||
'USE_WORKTREES still passes the harness isolation flag (#2652)'
|
||||
);
|
||||
});
|
||||
|
||||
// #2652: this assertion previously required `RUNTIME = "claude"`, encoding the
|
||||
// pre-#2584 premise that worktree isolation is Claude-specific. #2584 replaced
|
||||
// that with the negotiated dispatch.isolation capability, so the guard now keys
|
||||
// on the capability — Cursor also declares harness-worktree.
|
||||
test('degrade check guards on the negotiated capability, not a runtime id', () => {
|
||||
const content = fs.readFileSync(QUICK_WORKFLOW_PATH, 'utf-8');
|
||||
const baseCheckIdx = content.indexOf('worktree.base-check');
|
||||
const block = content.slice(Math.max(0, baseCheckIdx - 300), baseCheckIdx + 200);
|
||||
assert.ok(
|
||||
block.includes('ISOLATION') && block.includes('harness-worktree'),
|
||||
'degrade check must guard on ISOLATION = harness-worktree'
|
||||
);
|
||||
assert.ok(
|
||||
!/\[\s*"\$RUNTIME"\s*=/.test(block),
|
||||
'degrade check must NOT branch on a RUNTIME literal (#2584/#2652)'
|
||||
);
|
||||
});
|
||||
|
||||
test('degrade check names origin/HEAD as the stale fork base', () => {
|
||||
const content = fs.readFileSync(QUICK_WORKFLOW_PATH, 'utf-8');
|
||||
const step6Idx = content.indexOf('**Step 6: Spawn executor**');
|
||||
const nextSection = content.indexOf('\n---', step6Idx);
|
||||
const section = content.slice(step6Idx, nextSection === -1 ? undefined : nextSection);
|
||||
assert.ok(section.includes('origin/HEAD'), 'Step 6 must name origin/HEAD as the stale fork base');
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
{
|
||||
const { describe: __foldDescribe } = require('node:test');
|
||||
__foldDescribe('folded:fix-2649-diagnose-issues-worktree-stale-base', () => {
|
||||
|
||||
const DIAGNOSE_ISSUES_PATH = path.join(__dirname, '..', 'gsd-core', 'workflows', 'diagnose-issues.md');
|
||||
const EXECUTE_PLAN_PATH = path.join(__dirname, '..', 'gsd-core', 'workflows', 'execute-plan.md');
|
||||
|
||||
describe('diagnose-issues: pre-dispatch worktree base-check (#2649)', () => {
|
||||
test('workflow file exists', () => {
|
||||
assert.ok(fs.existsSync(DIAGNOSE_ISSUES_PATH), 'workflows/diagnose-issues.md should exist');
|
||||
});
|
||||
|
||||
test('spawn_agents step runs worktree.base-check before the Agent() dispatch', () => {
|
||||
const content = fs.readFileSync(DIAGNOSE_ISSUES_PATH, 'utf-8');
|
||||
const spawnIdx = content.indexOf('<step name="spawn_agents">');
|
||||
assert.ok(spawnIdx !== -1, '"spawn_agents" step must exist in diagnose-issues.md');
|
||||
const baseCheckIdx = content.indexOf('worktree.base-check', spawnIdx);
|
||||
assert.ok(baseCheckIdx !== -1, 'worktree.base-check must be invoked within the spawn_agents step');
|
||||
// The load-bearing invariant is "base-check BEFORE the Agent() dispatch" so the
|
||||
// degrade decision can drop isolation from the spawn. (Where EXPECTED_BASE is
|
||||
// captured relative to the check is cosmetic — the check only reads HEAD, never
|
||||
// mutates it — so assert the real invariant, not a loose disjunction.)
|
||||
const agentIdx = content.indexOf('Agent(', spawnIdx);
|
||||
assert.ok(agentIdx !== -1, 'spawn_agents must contain an Agent() dispatch');
|
||||
assert.ok(
|
||||
baseCheckIdx < agentIdx,
|
||||
'worktree.base-check must run before the Agent() dispatch so the degrade decision can drop isolation from the spawn',
|
||||
);
|
||||
});
|
||||
|
||||
test('verify-only worktree_branch_check backstop remains embedded in the Agent() prompt', () => {
|
||||
// Acceptance criterion #4: the base-check is a PRE-DISPATCH degrade; the
|
||||
// <worktree_branch_check> guard is a POST-FORK fail-closed backstop. Both
|
||||
// layers must survive — a future edit that dropped the backstop embedding
|
||||
// would re-open the silent-stale-base class. Guard its continued presence.
|
||||
const content = fs.readFileSync(DIAGNOSE_ISSUES_PATH, 'utf-8');
|
||||
const spawnIdx = content.indexOf('<step name="spawn_agents">');
|
||||
assert.ok(spawnIdx !== -1, '"spawn_agents" step must exist');
|
||||
assert.ok(
|
||||
content.indexOf('worktree-branch-check.md', spawnIdx) !== -1,
|
||||
'spawn_agents must still materialize the <worktree_branch_check> backstop after the base-check gate (#2649 acceptance criterion 4)',
|
||||
);
|
||||
});
|
||||
|
||||
test('degrade check sets USE_WORKTREES=false when shouldDegrade is true', () => {
|
||||
const content = fs.readFileSync(DIAGNOSE_ISSUES_PATH, 'utf-8');
|
||||
const baseCheckIdx = content.indexOf('worktree.base-check');
|
||||
const block = content.slice(baseCheckIdx, baseCheckIdx + 600);
|
||||
assert.ok(
|
||||
block.includes('shouldDegrade') && block.includes('USE_WORKTREES=false'),
|
||||
'degrade check must override USE_WORKTREES=false when shouldDegrade is true',
|
||||
);
|
||||
});
|
||||
|
||||
test('degrade check references #2649 for traceability', () => {
|
||||
const content = fs.readFileSync(DIAGNOSE_ISSUES_PATH, 'utf-8');
|
||||
assert.ok(content.includes('#2649'), 'diagnose-issues.md must reference #2649');
|
||||
});
|
||||
});
|
||||
|
||||
describe('execute-plan Pattern A: pre-dispatch worktree base-check (#2649)', () => {
|
||||
test('workflow file exists', () => {
|
||||
assert.ok(fs.existsSync(EXECUTE_PLAN_PATH), 'workflows/execute-plan.md should exist');
|
||||
});
|
||||
|
||||
test('Pattern A runs the worktree base-check before spawning the executor', () => {
|
||||
const content = fs.readFileSync(EXECUTE_PLAN_PATH, 'utf-8');
|
||||
const patternAIdx = content.indexOf('**Pattern A:**');
|
||||
assert.ok(patternAIdx !== -1, '"Pattern A:" must exist in execute-plan.md');
|
||||
// The base-check instruction must appear within the Pattern A description,
|
||||
// before the isolation="worktree" embedding instruction.
|
||||
const patternAEnd = content.indexOf('**Pattern B:**', patternAIdx);
|
||||
const patternA = content.slice(patternAIdx, patternAEnd === -1 ? undefined : patternAEnd);
|
||||
assert.ok(
|
||||
patternA.includes('#2649') && /worktree\.base-check|base-check/.test(patternA),
|
||||
'Pattern A must run the #2649 worktree base-check before dispatching the executor',
|
||||
);
|
||||
assert.ok(
|
||||
patternA.includes('shouldDegrade'),
|
||||
'Pattern A base-check must consult shouldDegrade',
|
||||
);
|
||||
});
|
||||
|
||||
test('Pattern A documents the auto-degrade (drop isolation on shouldDegrade)', () => {
|
||||
const content = fs.readFileSync(EXECUTE_PLAN_PATH, 'utf-8');
|
||||
const patternAIdx = content.indexOf('**Pattern A:**');
|
||||
const patternAEnd = content.indexOf('**Pattern B:**', patternAIdx);
|
||||
const patternA = content.slice(patternAIdx, patternAEnd === -1 ? undefined : patternAEnd);
|
||||
assert.ok(
|
||||
/degrad|sequential/i.test(patternA),
|
||||
'Pattern A must document auto-degrading to sequential mode when shouldDegrade is true',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user