Replace the single 747-line /gsd-help reference with a progressive-disclosure dispatcher (#2551 pattern). Newcomers get a one-page tour; returning users get a 10-line refresher with --brief; the complete reference stays available behind --full; /gsd-help <topic> emits one section; and /gsd-help --brief <topic> is a compact scoped lookup (signature + one-line summary). - workflows/help.md becomes a small dispatcher routing on $ARGUMENTS - workflows/help/modes/{brief,default,full,topic}.md hold the tier bodies - commands/gsd/help.md passes $ARGUMENTS through, advertises composable form - docs/COMMANDS.md documents the new flags and topic form - existing tests that read help.md repointed at help/modes/full.md (bug-2836, bug-2950, bug-2954, cursor-reviewer, execute-phase-wave) - new feat-3039-help-tiered test enforces structure, size budgets, dispatcher routing, shim arg passthrough, topic→section coverage, orphan-heading detection, conflict-resolution rules, routing preamble, and compact-scope rule Trek-e review fixes (PR #3040): - topic.md output rules split into 5a/5b/5c — explicit handling for single sections, multi-section "plus" joins, and bold-line sub-block anchors; each rule also takes scope (full vs compact) into account - explicit resolved-routing preamble line emitted by topic.md before content ("**Topic:** `<alias>` → `<heading>` *(scope: full | compact)*") so the user sees which alias matched at which scope (review finding #3) - composable `--brief <topic>` invokes topic.md in compact scope: heading + first `**/gsd:*`** signature line + one-line summary. Dispatcher and topic.md cooperate via $ARGUMENTS pass-through (review finding #4) - full.md capped at LARGE-tier budget (FULL_BUDGET = 1500); the non-recursive workflow-size-budget test does not reach modes/ subdirs - structural <progressive_disclosure> table parse (5-row assertion) replaces substring-soup regex matching — 5 rows = 4 base tiers + composable scope - forward /gsd:* sub-block token coverage + reverse orphan-heading allowlist catch alias-table drift in both directions - four conflict-resolution tests guard dispatcher promises (--brief+--full without topic → --full; --brief <topic> → compact; --full <topic> or bare → full; dispatcher retains --brief when delegating to topic.md) - hardcoded topic lists removed from docs/COMMANDS.md and full.md (drift surfaces reduced from 5 to 2) - topic.md alias bloat trimmed (~75 → ~25 rows); cleanup/update split into distinct sub-block rows under ### Utility Commands - comment-rot ("~750 lines") removed from default.md and full.md - dispatcher size guard tightened from < 100 to <= 40 lines - commands/gsd/help.md <process> block trimmed to one line - MD040 fence languages added to all plain code blocks across mode files Main-merge conflict resolution: - workflows/help.md kept as dispatcher (body lives in help/modes/full.md) - /gsd-<cmd> → /gsd:<cmd> rename from #3452 reapplied to the mode files (full.md, default.md, brief.md, topic.md) — the six namespace routers (/gsd-context, /gsd-ideate, /gsd-manage, /gsd-project, /gsd-quality, /gsd-workflow) and wildcards (/gsd-*) preserved in hyphen form per main's convention - bug-2950 test combines branch's path repointing with main's namespaced replacement strings
145 lines
4.8 KiB
JavaScript
145 lines
4.8 KiB
JavaScript
/**
|
|
* Bug #2950: Stale deleted command references in workflow files
|
|
*
|
|
* Multiple workflow files referenced command names removed in #2790
|
|
* (gsd-add-phase, gsd-insert-phase, gsd-remove-phase, gsd-add-todo,
|
|
* gsd-set-profile, gsd-settings-integrations, gsd-settings-advanced,
|
|
* gsd-spike-wrap-up, gsd-sketch-wrap-up, gsd-code-review-fix).
|
|
*
|
|
* Fix: Update every occurrence to the new consolidated forms:
|
|
* /gsd:phase (no flag | --insert | --remove)
|
|
* /gsd:capture
|
|
* /gsd:config (--profile | --integrations | --advanced)
|
|
* /gsd:spike --wrap-up
|
|
* /gsd:sketch --wrap-up
|
|
* /gsd:code-review --fix
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
const { describe, test } = require('node:test');
|
|
const assert = require('node:assert/strict');
|
|
const fs = require('node:fs');
|
|
const path = require('node:path');
|
|
|
|
const WORKFLOWS_DIR = path.join(__dirname, '..', 'get-shit-done', 'workflows');
|
|
|
|
function read(filename) {
|
|
return fs.readFileSync(path.join(WORKFLOWS_DIR, filename), 'utf-8');
|
|
}
|
|
|
|
// Deleted command names that must not appear anywhere in the fixed files.
|
|
const DELETED_COMMANDS = [
|
|
'/gsd-add-phase',
|
|
'/gsd-insert-phase',
|
|
'/gsd-remove-phase',
|
|
'/gsd-add-todo',
|
|
'/gsd-set-profile',
|
|
'/gsd-settings-integrations',
|
|
'/gsd-settings-advanced',
|
|
'/gsd-spike-wrap-up',
|
|
'/gsd-sketch-wrap-up',
|
|
'/gsd-code-review-fix',
|
|
];
|
|
|
|
// Per-file assertions: [file, deletedCmd, newForm]
|
|
const FILE_ASSERTIONS = [
|
|
// help.md → moved to help/modes/full.md in #3039 tiered-help refactor
|
|
['help/modes/full.md', '/gsd-add-phase', '/gsd:phase "Add admin dashboard"'],
|
|
['help/modes/full.md', '/gsd-insert-phase', '/gsd:phase --insert 7 "Fix critical auth bug"'],
|
|
['help/modes/full.md', '/gsd-remove-phase', '/gsd:phase --remove 17'],
|
|
['help/modes/full.md', '/gsd-spike-wrap-up', '/gsd:spike --wrap-up'],
|
|
['help/modes/full.md', '/gsd-sketch-wrap-up', '/gsd:sketch --wrap-up'],
|
|
['help/modes/full.md', '/gsd-add-todo', '/gsd:capture'],
|
|
['help/modes/full.md', '/gsd-set-profile', '/gsd:config --profile budget'],
|
|
|
|
// do.md
|
|
['do.md', '/gsd-spike-wrap-up', '/gsd:spike --wrap-up'],
|
|
['do.md', '/gsd-sketch-wrap-up', '/gsd:sketch --wrap-up'],
|
|
['do.md', '/gsd-add-phase', '/gsd:phase'],
|
|
['do.md', '/gsd-add-todo', '/gsd:capture'],
|
|
|
|
// settings.md
|
|
['settings.md', '/gsd-code-review-fix', '/gsd:code-review --fix'],
|
|
['settings.md', '/gsd-settings-integrations', '/gsd:config --integrations'],
|
|
['settings.md', '/gsd-set-profile', '/gsd:config --profile'],
|
|
['settings.md', '/gsd-settings-advanced', '/gsd:config --advanced'],
|
|
|
|
// discuss-phase.md
|
|
['discuss-phase.md', '/gsd-spike-wrap-up', '/gsd:spike --wrap-up'],
|
|
['discuss-phase.md', '/gsd-sketch-wrap-up', '/gsd:sketch --wrap-up'],
|
|
|
|
// new-project.md
|
|
['new-project.md', '/gsd-spike-wrap-up', '/gsd:spike --wrap-up'],
|
|
['new-project.md', '/gsd-sketch-wrap-up', '/gsd:sketch --wrap-up'],
|
|
|
|
// plan-phase.md
|
|
['plan-phase.md', '/gsd-insert-phase', '/gsd:phase --insert'],
|
|
|
|
// spike.md
|
|
['spike.md', '/gsd-spike-wrap-up', '/gsd:spike --wrap-up'],
|
|
|
|
// sketch.md
|
|
['sketch.md', '/gsd-sketch-wrap-up', '/gsd:sketch --wrap-up'],
|
|
];
|
|
|
|
describe('bug #2950: stale deleted-command references removed from workflow files', () => {
|
|
// Build a map of file → content to avoid re-reading
|
|
const files = [...new Set(FILE_ASSERTIONS.map(([f]) => f))];
|
|
const contentMap = {};
|
|
for (const f of files) {
|
|
contentMap[f] = read(f);
|
|
}
|
|
|
|
// For each (file, deletedCmd) pair, assert the old name is absent
|
|
for (const [file, deletedCmd] of FILE_ASSERTIONS) {
|
|
test(`${file}: does not contain deleted command "${deletedCmd}"`, () => {
|
|
const content = contentMap[file];
|
|
assert.ok(
|
|
!content.includes(deletedCmd),
|
|
`${file} still contains deleted command "${deletedCmd}" — update to new form`
|
|
);
|
|
});
|
|
}
|
|
|
|
// For each (file, deletedCmd, newForm) triple, assert the new form is present
|
|
for (const [file, , newForm] of FILE_ASSERTIONS) {
|
|
test(`${file}: contains new form "${newForm}"`, () => {
|
|
const content = contentMap[file];
|
|
assert.ok(
|
|
content.includes(newForm),
|
|
`${file} is missing expected new form "${newForm}"`
|
|
);
|
|
});
|
|
}
|
|
|
|
// Blanket check: no affected workflow file contains any of the deleted command names
|
|
// (catches any we might have missed in per-file assertions above)
|
|
const affectedFiles = [
|
|
'help.md',
|
|
'help/modes/full.md',
|
|
'help/modes/default.md',
|
|
'help/modes/brief.md',
|
|
'help/modes/topic.md',
|
|
'do.md',
|
|
'settings.md',
|
|
'discuss-phase.md',
|
|
'new-project.md',
|
|
'plan-phase.md',
|
|
'spike.md',
|
|
'sketch.md',
|
|
];
|
|
|
|
for (const file of affectedFiles) {
|
|
const content = read(file);
|
|
for (const deleted of DELETED_COMMANDS) {
|
|
test(`${file}: blanket check — "${deleted}" not present`, () => {
|
|
assert.ok(
|
|
!content.includes(deleted),
|
|
`${file} contains deleted command "${deleted}"`
|
|
);
|
|
});
|
|
}
|
|
}
|
|
});
|