Files
msd-core/tests/feat-3251-command-aliases-manifest-coverage.test.cjs
Tom Boucher 04031244b8 feat(sdk): add NON_FAMILY_COMMAND_ALIASES to manifest — 14 missing commands (#3305)
* test(3251): red — assert 14 missing commands in command-aliases.generated.cjs

Parametrized test covering all 14 commands from issue #3251. Requires the
CJS manifest and asserts structurally (never greps source). Currently fails
because NON_FAMILY_COMMAND_ALIASES is not exported and all 14 are missing.

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

* feat(3251): add 14 missing commands to command-aliases.generated.cjs

Adds NON_FAMILY_COMMAND_ALIASES export to command-aliases.generated.cjs and
extends sdk/src/query/command-manifest.non-family.ts with the 10 commands that
were registered in static catalogs but absent from the manifest source-of-truth:
- check.decision-coverage-plan / check.decision-coverage-verify
- frontmatter.get
- phase.mvp-mode
- progress.bar
- stats.json
- task.is-behavior-adding
- todo.match-phase
- uat.render-checkpoint
- workstream.list

The other 4 (frontmatter.set, learnings.copy, milestone.complete,
requirements.mark-complete) were already in non-family.ts but unexported.

Generator (sdk/scripts/gen-command-aliases.ts) now produces both the TS and CJS
artifacts including the non-family section, sorted by canonical for determinism.
Freshness check (sdk/scripts/check-command-aliases-fresh.mjs) verifies TS and CJS
non-family parity against the manifest source-of-truth.

Fixes #3251

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

* chore(3251): add changeset and CHANGELOG entry for PR #3305

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

* fix(gen-command-aliases): preserve TS type interfaces and annotations on regen (#3251)

- Add FamilyCommandAlias and NonFamilyCommandAlias interface declarations to tsBody so regen never strips them
- Replace JSON.stringify with single-line compact serialisers for TS output (matches committed file format)
- Apply typed annotations (readonly FamilyCommandAlias[] / readonly NonFamilyCommandAlias[]) to all exported TS constants
- CJS path unchanged: remains untyped pure-JS with JSON.stringify multi-line format
- Regenerate sdk/src/query/command-aliases.generated.ts to sync with updated generator

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

* chore: drop redundant CHANGELOG.md edit (use .changeset/ fragment per CONTRIBUTING.md)

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 11:46:40 -04:00

129 lines
4.2 KiB
JavaScript

'use strict';
/**
* Regression guard for issue #3251:
* 14 commands used in workflows must be present in command-aliases.generated.cjs.
*
* Asserts structurally by requiring the manifest and checking each canonical
* command appears in either the family arrays or the non-family array.
* Never greps the source file — see feedback_no_source_grep_tests.md.
*/
const { describe, test } = require('node:test');
const assert = require('node:assert/strict');
const path = require('path');
const REPO_ROOT = path.join(__dirname, '..');
const COMMAND_ALIASES_FILE = path.join(
REPO_ROOT,
'get-shit-done',
'bin',
'lib',
'command-aliases.generated.cjs',
);
const MISSING_14 = [
'check.decision-coverage-plan',
'check.decision-coverage-verify',
'frontmatter.get',
'frontmatter.set',
'learnings.copy',
'milestone.complete',
'phase.mvp-mode',
'progress.bar',
'requirements.mark-complete',
'stats.json',
'task.is-behavior-adding',
'todo.match-phase',
'uat.render-checkpoint',
'workstream.list',
];
describe('feat-3251: command-aliases.generated.cjs manifest coverage', () => {
let manifest;
test('manifest file can be required without error', () => {
try {
manifest = require(COMMAND_ALIASES_FILE);
} catch (err) {
assert.fail(`Failed to require manifest: ${err.message}`);
}
assert.ok(manifest, 'manifest should be truthy');
});
test('manifest exports NON_FAMILY_COMMAND_ALIASES array', () => {
manifest = manifest ?? require(COMMAND_ALIASES_FILE);
assert.ok(
Array.isArray(manifest.NON_FAMILY_COMMAND_ALIASES),
'NON_FAMILY_COMMAND_ALIASES must be an exported array in command-aliases.generated.cjs',
);
});
test('all 14 missing commands are present in the manifest (family or non-family)', () => {
manifest = manifest ?? require(COMMAND_ALIASES_FILE);
const allCanonicalsInManifest = new Set();
// Collect from all family arrays
const familyArrayKeys = [
'STATE_COMMAND_ALIASES',
'VERIFY_COMMAND_ALIASES',
'INIT_COMMAND_ALIASES',
'PHASE_COMMAND_ALIASES',
'PHASES_COMMAND_ALIASES',
'VALIDATE_COMMAND_ALIASES',
'ROADMAP_COMMAND_ALIASES',
];
for (const key of familyArrayKeys) {
const arr = manifest[key];
if (!Array.isArray(arr)) continue;
for (const entry of arr) {
if (entry && entry.canonical) allCanonicalsInManifest.add(entry.canonical);
}
}
// Collect from non-family array
const nonFamily = manifest.NON_FAMILY_COMMAND_ALIASES;
if (Array.isArray(nonFamily)) {
for (const entry of nonFamily) {
if (entry && entry.canonical) allCanonicalsInManifest.add(entry.canonical);
}
}
const missing = MISSING_14.filter((cmd) => !allCanonicalsInManifest.has(cmd));
assert.deepStrictEqual(
missing,
[],
`${missing.length} command(s) still missing from manifest: ${missing.join(', ')}`,
);
});
test('each non-family entry has required fields: canonical, aliases, mutation', () => {
manifest = manifest ?? require(COMMAND_ALIASES_FILE);
const nonFamily = manifest.NON_FAMILY_COMMAND_ALIASES;
if (!Array.isArray(nonFamily)) return; // caught by earlier test
for (const entry of nonFamily) {
assert.ok(typeof entry.canonical === 'string' && entry.canonical.length > 0,
`entry missing canonical: ${JSON.stringify(entry)}`);
assert.ok(Array.isArray(entry.aliases),
`entry missing aliases array for canonical=${entry.canonical}`);
assert.ok(typeof entry.mutation === 'boolean',
`entry missing mutation boolean for canonical=${entry.canonical}`);
}
});
test('NON_FAMILY_COMMAND_ALIASES is sorted by canonical (deterministic output)', () => {
manifest = manifest ?? require(COMMAND_ALIASES_FILE);
const nonFamily = manifest.NON_FAMILY_COMMAND_ALIASES;
if (!Array.isArray(nonFamily)) return; // caught by earlier test
const canonicals = nonFamily.map((e) => e.canonical);
const sorted = [...canonicals].sort((a, b) => a.localeCompare(b));
assert.deepStrictEqual(
canonicals,
sorted,
'NON_FAMILY_COMMAND_ALIASES must be sorted by canonical for deterministic regeneration',
);
});
});