From c051e718516eb7d85636dcc262c2c5edb0ff5924 Mon Sep 17 00:00:00 2001 From: Tom Boucher Date: Wed, 15 Apr 2026 08:58:13 -0400 Subject: [PATCH] test(docs): add command-count sync test; fix ARCHITECTURE.md drift (#2257) (#2259) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add tests/command-count-sync.test.cjs which programmatically counts .md files in commands/gsd/ and compares against the two count occurrences in docs/ARCHITECTURE.md ("Total commands: N" prose line and "# N slash commands" directory-tree comment). Counts are extracted from the doc at runtime — never hardcoded — so future drift is caught immediately in CI regardless of whether the doc or the filesystem moves. Fix the current drift: ARCHITECTURE.md said 69 commands; the actual committed count is 73. Both occurrences updated. Closes #2257 Co-authored-by: Claude Sonnet 4.6 --- docs/ARCHITECTURE.md | 4 +- tests/command-count-sync.test.cjs | 93 +++++++++++++++++++++++++++++++ 2 files changed, 95 insertions(+), 2 deletions(-) create mode 100644 tests/command-count-sync.test.cjs diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index acfd20828..83c7c0600 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -113,7 +113,7 @@ User-facing entry points. Each file contains YAML frontmatter (name, description - **Copilot:** Slash commands (`/gsd-command-name`) - **Antigravity:** Skills -**Total commands:** 69 +**Total commands:** 73 ### Workflows (`get-shit-done/workflows/*.md`) @@ -409,7 +409,7 @@ UI-SPEC.md (per phase) ─────────────────── ``` ~/.claude/ # Claude Code (global install) -├── commands/gsd/*.md # 69 slash commands +├── commands/gsd/*.md # 73 slash commands ├── get-shit-done/ │ ├── bin/gsd-tools.cjs # CLI utility │ ├── bin/lib/*.cjs # 19 domain modules diff --git a/tests/command-count-sync.test.cjs b/tests/command-count-sync.test.cjs new file mode 100644 index 000000000..2ae7ad3bd --- /dev/null +++ b/tests/command-count-sync.test.cjs @@ -0,0 +1,93 @@ +/** + * Regression test: command count in docs/ARCHITECTURE.md must match + * the actual number of .md files in commands/gsd/. + * + * Counts are extracted from the doc programmatically — never hardcoded + * in this test — so any future drift (adding a command without updating + * the doc, or vice-versa) is caught immediately. + * + * Related: issue #2257 + */ +'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 ROOT = path.resolve(__dirname, '..'); +const COMMANDS_DIR = path.join(ROOT, 'commands', 'gsd'); +const ARCH_MD = path.join(ROOT, 'docs', 'ARCHITECTURE.md'); + +/** + * Count .md files that actually live in commands/gsd/. + * Does not recurse into subdirectories. + */ +function actualCommandCount() { + return fs + .readdirSync(COMMANDS_DIR) + .filter((f) => f.endsWith('.md')) + .length; +} + +/** + * Extract the integer from the "**Total commands:** N" prose line in + * ARCHITECTURE.md. Returns null if the pattern is not found. + */ +function docProseCount(content) { + const m = content.match(/\*\*Total commands:\*\*\s+(\d+)/); + return m ? parseInt(m[1], 10) : null; +} + +/** + * Extract the integer from the directory-tree comment line: + * ├── commands/gsd/*.md # N slash commands + * Returns null if the pattern is not found. + */ +function docTreeCount(content) { + const m = content.match(/commands\/gsd\/\*\.md[^\n]*#\s*(\d+)\s+slash commands/); + return m ? parseInt(m[1], 10) : null; +} + +describe('ARCHITECTURE.md command count sync', () => { + const archContent = fs.readFileSync(ARCH_MD, 'utf8'); + const actual = actualCommandCount(); + + test('docs/ARCHITECTURE.md contains a "Total commands:" prose count', () => { + const count = docProseCount(archContent); + assert.notEqual(count, null, 'Expected "**Total commands:** N" line not found in ARCHITECTURE.md'); + }); + + test('docs/ARCHITECTURE.md contains a directory-tree slash-command count', () => { + const count = docTreeCount(archContent); + assert.notEqual(count, null, 'Expected "# N slash commands" tree comment not found in ARCHITECTURE.md'); + }); + + test('"Total commands:" prose count matches actual commands/gsd/ file count', () => { + const prose = docProseCount(archContent); + assert.equal( + prose, + actual, + `ARCHITECTURE.md "Total commands:" says ${prose} but commands/gsd/ has ${actual} .md files — update the doc`, + ); + }); + + test('directory-tree slash-command count matches actual commands/gsd/ file count', () => { + const tree = docTreeCount(archContent); + assert.equal( + tree, + actual, + `ARCHITECTURE.md directory tree says ${tree} slash commands but commands/gsd/ has ${actual} .md files — update the doc`, + ); + }); + + test('"Total commands:" prose count and directory-tree count agree with each other', () => { + const prose = docProseCount(archContent); + const tree = docTreeCount(archContent); + assert.equal( + prose, + tree, + `ARCHITECTURE.md has two mismatched counts: "Total commands: ${prose}" vs tree "# ${tree} slash commands"`, + ); + }); +});