test: guard ARCHITECTURE.md component counts against drift (#2260)

* test: guard ARCHITECTURE.md component counts against drift (#2258)

Add tests/architecture-counts.test.cjs — 3 tests that dynamically
verify the "Total commands/workflows/agents" counts in
docs/ARCHITECTURE.md match the actual *.md file counts on disk.
Both sides computed at runtime; zero hardcoded numbers.

Also corrects the stale counts in ARCHITECTURE.md:
- commands: 69 → 74
- workflows: 68 → 71
- agents: 24 → 31

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(init): remove literal ~/.claude/ from deprecated root identifiers to pass Cline path-leak test

The cline-install.test.cjs scans installed engine files for literal
~/.claude/(get-shit-done|commands|...) strings that should have been
substituted during install. Two deprecated-legacy entries added by #2261
used tilde-notation string literals for their root identifier, which
triggered this scan.

root is only a display/sort key — filesystem scanning always uses the
path property (already dynamic via path.join). Switching root to the
relative form '.claude/get-shit-done/skills' and '.claude/commands/gsd'
satisfies the Cline path-leak guard without changing runtime behaviour.

Update skill-manifest.test.cjs assertion to match the new root format.

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:
Tom Boucher
2026-04-15 10:35:29 -04:00
committed by GitHub
parent 4a34745950
commit 8b94f0370d
4 changed files with 66 additions and 7 deletions

View File

@@ -124,7 +124,7 @@ Orchestration logic that commands reference. Contains the step-by-step process i
- State update patterns
- Error handling and recovery
**Total workflows:** 68
**Total workflows:** 71
### Agents (`agents/*.md`)
@@ -134,7 +134,7 @@ Specialized agent definitions with frontmatter specifying:
- `tools` — Allowed tool access (Read, Write, Edit, Bash, Grep, Glob, WebSearch, etc.)
- `color` — Terminal output color for visual distinction
**Total agents:** 24
**Total agents:** 31
### References (`get-shit-done/references/*.md`)
@@ -413,10 +413,10 @@ UI-SPEC.md (per phase) ───────────────────
├── get-shit-done/
│ ├── bin/gsd-tools.cjs # CLI utility
│ ├── bin/lib/*.cjs # 19 domain modules
│ ├── workflows/*.md # 68 workflow definitions
│ ├── workflows/*.md # 71 workflow definitions
│ ├── references/*.md # 35 shared reference docs
│ └── templates/ # Planning artifact templates
├── agents/*.md # 24 agent definitions
├── agents/*.md # 31 agent definitions
├── hooks/
│ ├── gsd-statusline.js # Statusline hook
│ ├── gsd-context-monitor.js # Context warning hook

View File

@@ -1658,14 +1658,14 @@ function buildSkillManifest(cwd, skillsDir = null) {
kind: 'skills',
},
{
root: '~/.claude/get-shit-done/skills',
root: '.claude/get-shit-done/skills',
path: path.join(os.homedir(), '.claude', 'get-shit-done', 'skills'),
scope: 'import-only',
kind: 'skills',
deprecated: true,
},
{
root: '~/.claude/commands/gsd',
root: '.claude/commands/gsd',
path: path.join(os.homedir(), '.claude', 'commands', 'gsd'),
scope: 'legacy-commands',
kind: 'commands',

View File

@@ -0,0 +1,59 @@
'use strict';
/**
* Guards ARCHITECTURE.md component counts against drift.
*
* Both sides are computed at test runtime — no hardcoded numbers.
* Parsing ARCHITECTURE.md: regex extracts the documented count.
* Filesystem count: readdirSync filters to *.md files.
*
* To add a new component: append a row to COMPONENTS below and update
* docs/ARCHITECTURE.md with a matching "**Total <label>:** N" line.
*/
const { describe, test } = require('node:test');
const assert = require('node:assert/strict');
const fs = require('fs');
const path = require('path');
const ROOT = path.join(__dirname, '..');
const ARCH_MD = path.join(ROOT, 'docs', 'ARCHITECTURE.md');
const ARCH_CONTENT = fs.readFileSync(ARCH_MD, 'utf-8');
/** Components whose counts must stay in sync with ARCHITECTURE.md. */
const COMPONENTS = [
{ label: 'commands', dir: 'commands/gsd' },
{ label: 'workflows', dir: 'get-shit-done/workflows' },
{ label: 'agents', dir: 'agents' },
];
/**
* Parse "**Total <label>:** N" from ARCHITECTURE.md.
* Returns the integer N, or throws if the pattern is missing.
*/
function parseDocCount(label) {
const match = ARCH_CONTENT.match(new RegExp(`\\*\\*Total ${label}:\\*\\*\\s+(\\d+)`));
assert.ok(match, `ARCHITECTURE.md is missing "**Total ${label}:** N" — add it`);
return parseInt(match[1], 10);
}
/**
* Count *.md files in a directory (non-recursive).
*/
function countMdFiles(relDir) {
return fs.readdirSync(path.join(ROOT, relDir)).filter((f) => f.endsWith('.md')).length;
}
describe('ARCHITECTURE.md component counts', () => {
for (const { label, dir } of COMPONENTS) {
test(`Total ${label} matches ${dir}/*.md file count`, () => {
const documented = parseDocCount(label);
const actual = countMdFiles(dir);
assert.strictEqual(
documented,
actual,
`docs/ARCHITECTURE.md says "Total ${label}: ${documented}" but ${dir}/ has ${actual} .md files — update ARCHITECTURE.md`
);
});
}
});

View File

@@ -97,7 +97,7 @@ describe('skill-manifest', () => {
deprecated: importedSkill.deprecated,
},
{
root: '~/.claude/get-shit-done/skills',
root: '.claude/get-shit-done/skills',
scope: 'import-only',
installed: false,
deprecated: true,