fix(workflows): respect commit_docs:false in worktree merge and quick task commits (#1802)
Three locations in execute-phase.md and quick.md used raw `git add .planning/` commands that bypassed the commit_docs config check. When users set commit_docs: false during project setup, these raw git commands still staged and committed .planning/ files. Add commit_docs guards (via gsd-tools.cjs config-get) around all raw git add .planning/ invocations. The gsd-tools.cjs commit wrapper already respects this flag — these were the only paths that bypassed it. Fixes #1783 Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
98
tests/commit-docs-bypass.test.cjs
Normal file
98
tests/commit-docs-bypass.test.cjs
Normal file
@@ -0,0 +1,98 @@
|
||||
/**
|
||||
* commit_docs bypass guard tests (#1783)
|
||||
*
|
||||
* When users set commit_docs: false during /gsd-new-project, .planning/
|
||||
* files should never be staged or committed. The gsd-tools.cjs commit
|
||||
* wrapper already checks this flag, but three locations in execute-phase.md
|
||||
* and quick.md used raw `git add .planning/` commands that bypassed it.
|
||||
*
|
||||
* These tests verify that every `git add .planning/` invocation (explicit
|
||||
* or via file_list) is preceded by a commit_docs config check.
|
||||
*/
|
||||
|
||||
const { describe, test } = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const EXECUTE_PHASE_PATH = path.join(__dirname, '..', 'get-shit-done', 'workflows', 'execute-phase.md');
|
||||
const QUICK_PATH = path.join(__dirname, '..', 'get-shit-done', 'workflows', 'quick.md');
|
||||
|
||||
describe('commit_docs bypass guard (#1783)', () => {
|
||||
|
||||
test('execute-phase.md: every git add .planning/ has a commit_docs guard', () => {
|
||||
const content = fs.readFileSync(EXECUTE_PHASE_PATH, 'utf-8');
|
||||
const lines = content.split('\n');
|
||||
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
if (/git add\b.*\.planning\//.test(lines[i])) {
|
||||
// Search backwards from this line for a config-get commit_docs check
|
||||
const windowStart = Math.max(0, i - 10);
|
||||
const window = lines.slice(windowStart, i).join('\n');
|
||||
assert.ok(
|
||||
window.includes('config-get commit_docs'),
|
||||
`git add .planning/ at line ${i + 1} in execute-phase.md must be guarded by a commit_docs config check`
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
test('quick.md: every git add .planning/ has a commit_docs guard', () => {
|
||||
const content = fs.readFileSync(QUICK_PATH, 'utf-8');
|
||||
const lines = content.split('\n');
|
||||
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
if (/git add\b.*\.planning\//.test(lines[i])) {
|
||||
const windowStart = Math.max(0, i - 10);
|
||||
const window = lines.slice(windowStart, i).join('\n');
|
||||
assert.ok(
|
||||
window.includes('config-get commit_docs'),
|
||||
`git add .planning/ at line ${i + 1} in quick.md must be guarded by a commit_docs config check`
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
test('quick.md: git add ${file_list} has a commit_docs guard for .planning/ filtering', () => {
|
||||
const content = fs.readFileSync(QUICK_PATH, 'utf-8');
|
||||
const lines = content.split('\n');
|
||||
|
||||
// Find the line(s) that do `git add ${file_list}` — this variable
|
||||
// includes .planning/STATE.md so it needs a commit_docs guard too
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
if (/git add\s+\$\{?file_list/.test(lines[i])) {
|
||||
const windowStart = Math.max(0, i - 10);
|
||||
const window = lines.slice(windowStart, i + 1).join('\n');
|
||||
assert.ok(
|
||||
window.includes('config-get commit_docs'),
|
||||
`git add \${file_list} at line ${i + 1} in quick.md must be guarded by a commit_docs check ` +
|
||||
`because file_list includes .planning/ files`
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
test('no raw git add .planning/ without commit_docs guard in any workflow', () => {
|
||||
const workflows = [
|
||||
{ name: 'execute-phase.md', path: EXECUTE_PHASE_PATH },
|
||||
{ name: 'quick.md', path: QUICK_PATH },
|
||||
];
|
||||
|
||||
for (const wf of workflows) {
|
||||
const content = fs.readFileSync(wf.path, 'utf-8');
|
||||
|
||||
// Find all occurrences of git add that reference .planning/
|
||||
const regex = /git add\b[^\n]*\.planning\//g;
|
||||
let match;
|
||||
while ((match = regex.exec(content)) !== null) {
|
||||
// Get the 500-char window before this match
|
||||
const before = content.slice(Math.max(0, match.index - 500), match.index);
|
||||
assert.ok(
|
||||
before.includes('config-get commit_docs'),
|
||||
`${wf.name}: found unguarded git add .planning/ near offset ${match.index}. ` +
|
||||
`All raw git add .planning/ commands must check commit_docs config first.`
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user