* test: forbid stale /gsd-intel references in workflow/reference docs (#3258) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * docs: replace retired /gsd-intel with /gsd-map-codebase --query (#3258) Fixes 5 stale references across the two primary source files called out in the issue. PR #2790 folded /gsd-intel into /gsd-map-codebase --query; these prose surfaces were not updated at that time. Fixes #3258 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * docs: fix additional stale /gsd-intel references found in adversarial sweep (#3258) Sweep found 7 more occurrences in docs/INVENTORY.md (x2), docs/USER-GUIDE.md (x4), docs/FEATURES.md (x2), and agents/gsd-intel-updater.md (x2). All replaced with /gsd-map-codebase --query. The gsd-intel-updater agent name itself (without leading slash) is intentionally preserved. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * changeset: pr=3260 for #3258 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test: fail loudly on unreadable files in bug-3258 regression scan (CR finding) Replace silent early-return on readFileSync failure with an explicit throw so unreadable files surface as test failures rather than skipped coverage gaps. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
109
tests/bug-3258-no-stale-gsd-intel-references.test.cjs
Normal file
109
tests/bug-3258-no-stale-gsd-intel-references.test.cjs
Normal file
@@ -0,0 +1,109 @@
|
||||
// allow-test-rule: source-text-is-the-product — workflow, reference, and docs .md files
|
||||
// ARE what the runtime loads and what users read; asserting their text content
|
||||
// tests the deployed skill surface contract, not implementation internals.
|
||||
|
||||
'use strict';
|
||||
|
||||
// Regression tests for bug #3258.
|
||||
//
|
||||
// PR #2790 folded `/gsd-intel` into `/gsd-map-codebase --query`. After that
|
||||
// consolidation, five prose occurrences in two source files continued to
|
||||
// reference the retired `/gsd-intel` slash command. Users invoking the wizard
|
||||
// were directed to a command that no longer exists.
|
||||
//
|
||||
// Fix: replace each `/gsd-intel` (the retired user-facing slash command) with
|
||||
// `/gsd-map-codebase --query` in:
|
||||
// - get-shit-done/references/planning-config.md
|
||||
// - get-shit-done/workflows/settings.md
|
||||
// - docs/INVENTORY.md
|
||||
// - docs/USER-GUIDE.md
|
||||
// - docs/FEATURES.md
|
||||
//
|
||||
// Allowed: `gsd-intel-updater` (still-valid agent name, no leading slash),
|
||||
// `intel.cjs` / `intel.enabled` / `intel.*` (internal backend, not user command),
|
||||
// CHANGELOG.md (historical record), test files themselves.
|
||||
//
|
||||
// This test distinguishes `/gsd-intel` (the retired slash command, leading slash)
|
||||
// from `gsd-intel-updater` (still-valid agent) by grepping for the literal
|
||||
// string `/gsd-intel` and then asserting no match survives after excluding
|
||||
// the `-updater` suffix.
|
||||
|
||||
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, '..');
|
||||
|
||||
/** Walk a directory recursively and return absolute paths of all .md files. */
|
||||
function walkMd(dir) {
|
||||
const results = [];
|
||||
if (!fs.existsSync(dir)) return results;
|
||||
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
||||
const abs = path.join(dir, entry.name);
|
||||
if (entry.isDirectory()) {
|
||||
results.push(...walkMd(abs));
|
||||
} else if (entry.isFile() && entry.name.endsWith('.md')) {
|
||||
results.push(abs);
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all lines in `src` that contain `/gsd-intel` (the retired slash
|
||||
* command) but are NOT the agent name `gsd-intel-updater`.
|
||||
* We match the literal substring `/gsd-intel` (with leading slash) and then
|
||||
* exclude any line where the match is immediately followed by `-updater`.
|
||||
*/
|
||||
function staleLinesIn(src) {
|
||||
return src.split('\n').filter((line) => {
|
||||
if (!line.includes('/gsd-intel')) return false;
|
||||
// Remove all occurrences of the valid agent name; if nothing remains, skip.
|
||||
const stripped = line.replace(/\/gsd-intel-updater/g, '');
|
||||
return stripped.includes('/gsd-intel');
|
||||
});
|
||||
}
|
||||
|
||||
const SOURCE_DIRS = [
|
||||
path.join(ROOT, 'commands', 'gsd'),
|
||||
path.join(ROOT, 'get-shit-done', 'workflows'),
|
||||
path.join(ROOT, 'get-shit-done', 'references'),
|
||||
path.join(ROOT, 'agents'),
|
||||
path.join(ROOT, 'docs'),
|
||||
];
|
||||
|
||||
describe('#3258: no stale /gsd-intel slash-command references in product source dirs', () => {
|
||||
for (const dir of SOURCE_DIRS) {
|
||||
const files = walkMd(dir);
|
||||
for (const file of files) {
|
||||
const rel = path.relative(ROOT, file);
|
||||
|
||||
// Allowed exclusions:
|
||||
// - CHANGELOG.md is a historical record; /gsd-intel appears in release notes
|
||||
// - test files (under tests/) are excluded automatically by SOURCE_DIRS scope
|
||||
if (rel === 'CHANGELOG.md') continue;
|
||||
|
||||
test(`${rel} has no stale /gsd-intel references`, () => {
|
||||
let src;
|
||||
try {
|
||||
src = fs.readFileSync(file, 'utf8');
|
||||
} catch (err) {
|
||||
throw new Error(`failed reading ${rel}: ${err.message}`);
|
||||
}
|
||||
|
||||
const staleLines = staleLinesIn(src);
|
||||
assert.strictEqual(
|
||||
staleLines.length,
|
||||
0,
|
||||
[
|
||||
`${rel} contains ${staleLines.length} stale /gsd-intel reference(s).`,
|
||||
'Replace with /gsd-map-codebase --query (retired by PR #2790).',
|
||||
'Stale lines:',
|
||||
...staleLines.map((l) => ` ${l.trim()}`),
|
||||
].join('\n'),
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user