gen-inventory-manifest.cjs's cli_modules family did a flat readdirSync of gsd-core/bin/lib/, invisible to anything shipped in a subdirectory. Found while registering this phase's 8 health-diagnostic-rules/*.cjs files in docs/INVENTORY.md (Standards-axis review) — the automated manifest cross-check couldn't see them even though the manual INVENTORY.md rows were correct. Adds collectOneLevelSubdirs (mirrors the existing collectNested's defensive statOrNull style) and merges flat + one-level-subdirectory results into cli_modules's single sorted array, using the same <subdir>/<file>.cjs key format INVENTORY.md's rows already use. Regenerating the manifest surfaced that three OTHER existing subdirectories (installer-migrations/, host-integration-adapters/, observability/ — pre-existing, unrelated to this phase) were equally invisible and had zero docs/INVENTORY.md rows at all. Added all 15 missing rows rather than leave a gap the fix itself just exposed. Also fixes 3 pre-existing lint-legacy-dir-name violations in the installer-migrations rows (legitimate references to the historical get-shit-done -> gsd-core rename these migrations clean up — marked with the guard's own gsd-allow-legacy-name exemption) and a stale health-diagnostic.cjs row that still said "RULES ships empty."
67 lines
2.9 KiB
JavaScript
67 lines
2.9 KiB
JavaScript
'use strict';
|
|
|
|
/**
|
|
* Asserts docs/INVENTORY-MANIFEST.json is in sync with the filesystem.
|
|
* A stale manifest means a surface shipped without updating INVENTORY.md.
|
|
* Fix by running: node scripts/gen-inventory-manifest.cjs --write
|
|
* then adding the corresponding row(s) in docs/INVENTORY.md.
|
|
*/
|
|
|
|
const { 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 MANIFEST_PATH = path.join(ROOT, 'docs', 'INVENTORY-MANIFEST.json');
|
|
|
|
// #2996: FAMILIES and NESTED_FAMILIES are IMPORTED, never redeclared. This file used to
|
|
// carry its own copy of the family table — the `DEFECT.GENERATIVE-FIX` divergence class:
|
|
// a family added to the generator but not here left this test silently verifying a
|
|
// subset while still reporting green. Importing makes divergence impossible rather than
|
|
// merely detectable.
|
|
const { FAMILIES, NESTED_FAMILIES, collectNested, collectOneLevelSubdirs } = require('../scripts/gen-inventory-manifest.cjs');
|
|
|
|
test('docs/INVENTORY-MANIFEST.json matches the filesystem', () => {
|
|
const committed = JSON.parse(fs.readFileSync(MANIFEST_PATH, 'utf8'));
|
|
const additions = [];
|
|
const removals = [];
|
|
|
|
for (const { name, dir, filter, toName } of FAMILIES) {
|
|
const flat = fs.readdirSync(dir)
|
|
.filter((f) => fs.statSync(path.join(dir, f)).isFile() && filter(f))
|
|
.map(toName);
|
|
// `cli_modules` also ships one level of subdirectory modules (#3309); mirror
|
|
// buildManifest's special-case merge exactly, or this test would report every
|
|
// subdirectory file as a phantom removal.
|
|
const nested = name === 'cli_modules' ? collectOneLevelSubdirs({ dir, filter }) : [];
|
|
const live = new Set([...flat, ...nested]);
|
|
const recorded = new Set((committed.families || {})[name] || []);
|
|
|
|
for (const entry of live) {
|
|
if (!recorded.has(entry)) additions.push(name + '/' + entry);
|
|
}
|
|
for (const entry of recorded) {
|
|
if (!live.has(entry)) removals.push(name + '/' + entry);
|
|
}
|
|
}
|
|
|
|
for (const family of NESTED_FAMILIES) {
|
|
const live = new Set(collectNested(family));
|
|
const recorded = new Set((committed.families || {})[family.name] || []);
|
|
for (const entry of live) {
|
|
if (!recorded.has(entry)) additions.push(family.name + '/' + entry);
|
|
}
|
|
for (const entry of recorded) {
|
|
if (!live.has(entry)) removals.push(family.name + '/' + entry);
|
|
}
|
|
}
|
|
|
|
const msg = [
|
|
additions.length ? 'New surfaces not in manifest (run node scripts/gen-inventory-manifest.cjs --write):\n' + additions.map((e) => ' + ' + e).join('\n') : '',
|
|
removals.length ? 'Manifest entries with no matching file:\n' + removals.map((e) => ' - ' + e).join('\n') : '',
|
|
].filter(Boolean).join('\n');
|
|
|
|
assert.ok(additions.length === 0 && removals.length === 0, msg);
|
|
});
|