Files
msd-core/tests/bug-3097-3099-executor-worktree-path-safety.test.cjs
Tom Boucher ba0409e04e fix(#3097, #3099): add cwd-drift sentinel + absolute-path guard to executor worktree protocol (#3144)
* fix(#3097, #3099): add cwd-drift + absolute-path guards to executor worktree protocol

#3097 — cwd-drift sentinel (gsd-executor.md task_commit_protocol step 0a):
  A Bash cd out of the worktree makes [ -f .git ] false, silently skipping
  all HEAD/branch safety guards. Commits land on main's branch.
  Fix: on first commit, capture spawn-time toplevel into sentinel file at
  .git/worktrees/<name>/gsd-spawn-toplevel. Before every subsequent commit,
  verify ACTUAL_TL matches EXPECTED_TL. Exits 1 with recovery instructions
  if drift detected.

#3099 — absolute-path guard (gsd-executor.md task_commit_protocol step 0b):
  Absolute paths constructed from the orchestrator's pwd (main repo root)
  resolve to the main repo inside worktrees. Edit/Write lands in wrong dir;
  git commit sees a clean worktree tree; work silently lost or leaks to main.
  Fix: before any absolute-path Edit/Write, verify path starts with
  WT_ROOT=/Users/thbouc/projects/get-shit-done. Prefer relative paths.

Both guards are documented in references/worktree-path-safety.md, which
is now loaded into every executor spawn prompt via <execution_context>.
The <worktree_branch_check> footnote references all three steps (0/0a/0b).

execute-phase.md: extracted worktree bash commands to reference file
(safe embed — @ files are inlined before the executor processes the prompt).
The blank line in <required_reading> was removed to stay at the XL=1700 line
budget after adding the @ reference.

Suite: 6986/6986. Closes #3097. Closes #3099.

* fix(lint+executor+docs): allow-test-rule, fix [ -f .git ] guard, fail-closed abs-path check, fix INVENTORY count
2026-05-05 15:02:26 -04:00

104 lines
5.0 KiB
JavaScript

'use strict';
// allow-test-rule: reads markdown product files (gsd-executor.md, worktree-path-safety.md) to verify structural protocol — not source-grep
// Regression guards for bug #3097 and #3099.
//
// #3097: gsd-executor's worktree HEAD guard used `if [ -f .git ]` to detect
// worktree mode. After a Bash `cd` out of the worktree into the main repo,
// `.git` is a DIRECTORY (not a file), so the test is false and the entire
// HEAD safety block is silently skipped. Commits then land on whatever branch
// the main repo has checked out — not the per-agent worktree branch.
//
// #3099: Executor agents construct absolute paths from `pwd` captured in the
// orchestrator context (main repo root). Edit/Write calls using these paths
// resolve to the main repo, not the worktree. git commit from the worktree
// sees a clean tree; the work is silently lost or leaks to main.
const { describe, test } = require('node:test');
const assert = require('node:assert/strict');
const fs = require('node:fs');
const path = require('node:path');
const ROOT = path.join(__dirname, '..');
const executorSrc = fs.readFileSync(
path.join(ROOT, 'agents', 'gsd-executor.md'), 'utf8',
);
const executePhaseSrc = fs.readFileSync(
path.join(ROOT, 'get-shit-done', 'workflows', 'execute-phase.md'), 'utf8',
);
describe('bug #3097: cwd-drift sentinel in gsd-executor.md', () => {
test('task_commit_protocol has cwd-drift assertion step (0a)', () => {
const protocolIdx = executorSrc.indexOf('<task_commit_protocol>');
const protocolEnd = executorSrc.indexOf('</task_commit_protocol>');
assert.ok(protocolIdx !== -1 && protocolEnd !== -1, 'task_commit_protocol block not found');
const protocol = executorSrc.slice(protocolIdx, protocolEnd);
assert.ok(
protocol.includes('cwd') || protocol.includes('drift') || protocol.includes('gsd-spawn-toplevel'),
'task_commit_protocol missing cwd-drift assertion step — #3097 fix not applied',
);
});
test('sentinel uses git rev-parse --git-dir to detect worktree', () => {
const protocolIdx = executorSrc.indexOf('<task_commit_protocol>');
const protocolEnd = executorSrc.indexOf('</task_commit_protocol>');
const protocol = executorSrc.slice(protocolIdx, protocolEnd);
assert.ok(
protocol.includes('rev-parse --git-dir') || protocol.includes('worktrees/'),
'cwd-drift detection does not use git rev-parse --git-dir or .git/worktrees/ pattern',
);
});
test('cwd-drift check precedes HEAD assertion', () => {
const protocolIdx = executorSrc.indexOf('<task_commit_protocol>');
const protocolEnd = executorSrc.indexOf('</task_commit_protocol>');
const protocol = executorSrc.slice(protocolIdx, protocolEnd);
const driftIdx = protocol.search(/cwd.drift|gsd-spawn-toplevel|drift.*assertion/i);
const headIdx = protocol.indexOf('Pre-commit HEAD safety assertion');
assert.ok(driftIdx !== -1, 'cwd-drift assertion not found');
assert.ok(headIdx !== -1, 'HEAD assertion not found');
assert.ok(driftIdx < headIdx, 'cwd-drift assertion must precede HEAD assertion (step 0a before step 0)');
});
});
describe('bug #3099: absolute-path safety guidance in gsd-executor.md', () => {
test('task_commit_protocol documents absolute-path safety', () => {
const protocolIdx = executorSrc.indexOf('<task_commit_protocol>');
const protocolEnd = executorSrc.indexOf('</task_commit_protocol>');
const protocol = executorSrc.slice(protocolIdx, protocolEnd);
assert.ok(
(protocol.includes('absolute') || protocol.includes('absolute-path')) &&
(protocol.includes('worktree') || protocol.includes('WT_ROOT')),
'task_commit_protocol missing absolute-path safety guidance — #3099 fix not applied',
);
});
test('execute-phase.md parallel_execution block references path safety', () => {
const parallelIdx = executePhaseSrc.indexOf('<parallel_execution>');
assert.ok(parallelIdx !== -1, 'parallel_execution block not found in execute-phase.md');
// Verify the worktree-path-safety.md reference is present in the execution_context
// (loaded via @ reference rather than inlined — the safe extract pattern)
assert.ok(
executePhaseSrc.includes('worktree-path-safety.md'),
'execute-phase.md does not reference worktree-path-safety.md in execution_context',
);
});
test('worktree-path-safety.md reference file exists', () => {
assert.ok(
fs.existsSync(path.join(ROOT, 'get-shit-done', 'references', 'worktree-path-safety.md')),
'get-shit-done/references/worktree-path-safety.md does not exist',
);
});
test('worktree-path-safety.md contains cwd-drift and absolute-path guards', () => {
const safetySrc = fs.readFileSync(
path.join(ROOT, 'get-shit-done', 'references', 'worktree-path-safety.md'), 'utf8',
);
assert.ok(safetySrc.includes('gsd-spawn-toplevel') || safetySrc.includes('cwd-drift'),
'worktree-path-safety.md missing cwd-drift sentinel content');
assert.ok(safetySrc.includes('WT_ROOT') || safetySrc.includes('absolute'),
'worktree-path-safety.md missing absolute-path guard content');
});
});