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:
59
tests/architecture-counts.test.cjs
Normal file
59
tests/architecture-counts.test.cjs
Normal 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`
|
||||
);
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user