Wave 5 Step 14. Threads the humanizer vocabulary through the remaining
six audit/analysis command templates and adds a shape test that locks
the structure plus a pair of anchor must-contains.
- commands/drift.md: --raw pass-through; new/resolved/changed-finding
rendering instructions reference userActionLanguage and
relevanceContext rather than raw severity.
- commands/plugin-health.md: --raw pass-through; finding rendering
groups by userImpactCategory and leads with userActionLanguage.
- commands/config-audit.md (router): replaces the 25-line A/B/C/D/F
prose ladder with a humanized stderr-scorecard reference + three
userActionLanguage-grouped "What you can do next" branches; --raw
threaded through both scan-orchestrator and posture invocations.
- commands/discover.md: --raw pass-through; finding-summary rendering
groups by userImpactCategory.
- commands/analyze.md: --raw pass-through; analyzer-agent prompt now
instructs grouping by userImpactCategory and leading with
userActionLanguage; humanized title/description/recommendation
strings rendered verbatim, no paraphrasing.
- commands/status.md: phase-label humanization table — current_phase
machine field name preserved, user-facing labels translated
("Looking at your config files", "Working out what to recommend",
"Asking what you'd like to focus on", "Putting together your action
plan", "Making the changes", "Double-checking everything worked");
--raw preserves verbatim machine field values.
tests/commands/group-b-shape.test.mjs (new, +8 tests, 772 → 780):
- structural: bash block + Read tool + --raw/$ARGUMENTS plumbing
across all 6 files
- findings-renderers: humanized field reference + no grade-prose
- anchor must-contains per plan: config-audit.md ⊇
userImpactCategory|userActionLanguage; drift.md ⊇ --raw|humanized
- status.md: current_phase preserved + ≥3 humanized phase labels
134 lines
4.8 KiB
JavaScript
134 lines
4.8 KiB
JavaScript
/**
|
||
* Wave 5 Step 14 — Group B command-template shape tests.
|
||
*
|
||
* Verifies that the 6 audit/analysis command templates in Group B have the
|
||
* correct structural shape after the humanizer integration:
|
||
*
|
||
* - All 6 files: contain a Bash invocation block, reference the Read tool,
|
||
* and contain the `--raw` flag (or the literal `"$ARGUMENTS"` string).
|
||
*
|
||
* - Findings-rendering files (drift.md, plugin-health.md, config-audit.md,
|
||
* discover.md, analyze.md): reference at least one of
|
||
* `userImpactCategory|userActionLanguage|relevanceContext`, and do NOT
|
||
* contain hardcoded grade-prose tables of the form `[ABCDF]\s+grade\s+is`.
|
||
*
|
||
* - status.md: phase-label table is present, the machine field name
|
||
* `current_phase` is preserved (machine contract), and at least one
|
||
* humanized phase label appears ("Looking at your config files",
|
||
* "Working out what to recommend", "Putting together your action plan",
|
||
* "Making the changes", "Double-checking everything worked").
|
||
*
|
||
* - Anchor must-contains from plan line 575–579:
|
||
* - config-audit.md: contains userImpactCategory|userActionLanguage
|
||
* - drift.md: contains --raw OR humanized
|
||
*/
|
||
|
||
import { test } from 'node:test';
|
||
import { strict as assert } from 'node:assert';
|
||
import { readFile } from 'node:fs/promises';
|
||
import { resolve, dirname } from 'node:path';
|
||
import { fileURLToPath } from 'node:url';
|
||
|
||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||
const COMMANDS_DIR = resolve(__dirname, '..', '..', 'commands');
|
||
|
||
const GROUP_B_FILES = [
|
||
'drift.md',
|
||
'plugin-health.md',
|
||
'config-audit.md',
|
||
'discover.md',
|
||
'analyze.md',
|
||
'status.md',
|
||
];
|
||
|
||
const FINDINGS_RENDERING_FILES = [
|
||
'drift.md',
|
||
'plugin-health.md',
|
||
'config-audit.md',
|
||
'discover.md',
|
||
'analyze.md',
|
||
];
|
||
|
||
const HUMANIZED_FIELD_REGEX = /userImpactCategory|userActionLanguage|relevanceContext/;
|
||
const RAW_OR_ARGUMENTS_REGEX = /--raw|"\$ARGUMENTS"/;
|
||
const HARDCODED_GRADE_PROSE_REGEX = /[ABCDF]\s+grade\s+is/;
|
||
const BASH_BLOCK_REGEX = /```bash\b/;
|
||
const READ_TOOL_REGEX = /\bRead\s+tool\b|allowed-tools:.*\bRead\b/;
|
||
|
||
const HUMANIZED_PHASE_LABELS = [
|
||
'Looking at your config files',
|
||
'Working out what to recommend',
|
||
'Asking what you',
|
||
'Putting together your action plan',
|
||
'Making the changes',
|
||
'Double-checking everything worked',
|
||
];
|
||
|
||
async function readCommand(name) {
|
||
return await readFile(resolve(COMMANDS_DIR, name), 'utf-8');
|
||
}
|
||
|
||
test('Group B: every file contains a Bash invocation block', async () => {
|
||
for (const name of GROUP_B_FILES) {
|
||
const content = await readCommand(name);
|
||
assert.match(content, BASH_BLOCK_REGEX, `${name} missing bash block`);
|
||
}
|
||
});
|
||
|
||
test('Group B: every file references the Read tool', async () => {
|
||
for (const name of GROUP_B_FILES) {
|
||
const content = await readCommand(name);
|
||
assert.match(content, READ_TOOL_REGEX, `${name} missing Read tool reference`);
|
||
}
|
||
});
|
||
|
||
test('Group B: every file contains --raw or "$ARGUMENTS" (pass-through plumbing)', async () => {
|
||
for (const name of GROUP_B_FILES) {
|
||
const content = await readCommand(name);
|
||
assert.match(content, RAW_OR_ARGUMENTS_REGEX, `${name} missing --raw / $ARGUMENTS plumbing`);
|
||
}
|
||
});
|
||
|
||
test('Group B findings-renderers: reference at least one humanized field', async () => {
|
||
for (const name of FINDINGS_RENDERING_FILES) {
|
||
const content = await readCommand(name);
|
||
assert.match(
|
||
content,
|
||
HUMANIZED_FIELD_REGEX,
|
||
`${name} must reference userImpactCategory, userActionLanguage, or relevanceContext`,
|
||
);
|
||
}
|
||
});
|
||
|
||
test('Group B findings-renderers: no hardcoded grade-prose tables', async () => {
|
||
for (const name of FINDINGS_RENDERING_FILES) {
|
||
const content = await readCommand(name);
|
||
assert.doesNotMatch(
|
||
content,
|
||
HARDCODED_GRADE_PROSE_REGEX,
|
||
`${name} contains a hardcoded "[grade] grade is..." prose table — humanizer owns grade vocabulary now`,
|
||
);
|
||
}
|
||
});
|
||
|
||
test('Group B anchor: config-audit.md references userImpactCategory|userActionLanguage', async () => {
|
||
const content = await readCommand('config-audit.md');
|
||
assert.match(content, /userImpactCategory|userActionLanguage/);
|
||
});
|
||
|
||
test('Group B anchor: drift.md references --raw or humanized', async () => {
|
||
const content = await readCommand('drift.md');
|
||
assert.match(content, /--raw|humanized/);
|
||
});
|
||
|
||
test('status.md: preserves current_phase machine field and adds humanized phase labels', async () => {
|
||
const content = await readCommand('status.md');
|
||
// Machine contract preserved
|
||
assert.match(content, /\bcurrent_phase\b/, 'status.md must keep current_phase as machine field');
|
||
// At least 3 of the 6 humanized phase labels appear
|
||
const present = HUMANIZED_PHASE_LABELS.filter(label => content.includes(label));
|
||
assert.ok(
|
||
present.length >= 3,
|
||
`status.md must include at least 3 humanized phase labels; found ${present.length}: ${present.join(', ')}`,
|
||
);
|
||
});
|