Files
msd-core/tests/adr-15-progress-converge.test.cjs
Jakub Zych 6cfa0c55d2 refactor: drop 12 runtimes, keep Claude, Codex, OpenCode, Cursor, ZCode, Antigravity
Removes kilo, kimi, kimi-code, copilot, windsurf, augment, trae, qwen, hermes,
cline, codebuddy and pi end to end: capability descriptors, installer branches
and converters (bin/install.js 14.9k -> 11.2k lines), TypeScript converters,
hook surfaces and runtime homes, review lanes qwen/kimi-code, the two pi
migrations, Kimi payload normalization in the hook guards, dead hostBehaviors
vocabulary, launcher home probes, fixtures, runtime-specific tests and the
prose that presented them as supported.

Installer output for the six kept runtimes is byte-identical to before the
prune. The Kimi tool-vocabulary tests in workflow-guard, read-guard and
read-injection-scanner are left in place pending a decision.
2026-10-06 20:02:40 +02:00

217 lines
8.6 KiB
JavaScript

// allow-test-rule: source-text-is-the-product #1190
// The progress command and next workflow markdown are runtime-loaded contracts.
// Checking their text verifies the shipped slash-command behavior.
'use strict';
const { describe, test } = require('node:test');
const assert = require('node:assert/strict');
const fs = require('node:fs');
const path = require('node:path');
const { runNode } = require('./helpers/process-seam.cjs');
const { throwIfFailed } = require('./helpers/git-fixture.cjs');
const { PROBE_TIMEOUT_MS } = require('./helpers/timeouts.cjs');
const REPO_ROOT = path.join(__dirname, '..');
const COMMAND_PATH = path.join(REPO_ROOT, 'commands', 'msd', 'progress.md');
const WORKFLOW_PATH = path.join(REPO_ROOT, 'msd-core', 'workflows', 'next.md');
const FULL_MD_PATH = path.join(REPO_ROOT, 'msd-core', 'workflows', 'help', 'modes', 'full.md');
const COMMANDS_DOC_PATH = path.join(REPO_ROOT, 'docs', 'COMMANDS.md');
const HOW_TO_PATH = path.join(REPO_ROOT, 'docs', 'how-to', 'run-phases-autonomously.md');
const TOOLS = path.join(REPO_ROOT, 'msd-core', 'bin', 'msd-tools.cjs');
function read(filePath) {
return fs.readFileSync(filePath, 'utf8');
}
describe('ADR-15: /msd:progress --next --auto --converge (#1190)', () => {
test('progress command advertises --converge, --auto, and notes --cross-ai alias', () => {
const command = read(COMMAND_PATH);
assert.match(
command,
/^argument-hint:.*--converge/m,
'progress command should advertise --converge in argument-hint',
);
assert.match(
command,
/^argument-hint:.*--auto/m,
'progress command should advertise --auto in argument-hint',
);
assert.match(command, /--cross-ai/, 'progress command should document --cross-ai alias');
assert.match(
command,
/workflow\.plan_review_convergence=true/,
'progress command should mention the convergence feature gate',
);
});
test('next workflow parses converge aliases into a plan strategy', () => {
const workflow = read(WORKFLOW_PATH);
assert.match(workflow, /PLAN_STRATEGY="local"/, 'workflow should default to local planning');
assert.match(workflow, /PLAN_STRATEGY="converge"/, 'workflow should opt into converge planning');
assert.match(workflow, /converge\|cross-ai/, 'workflow should accept --converge and --cross-ai');
});
test('next workflow fails fast when convergence is requested but disabled', () => {
const workflow = read(WORKFLOW_PATH);
assert.match(
workflow,
/config-get workflow\.plan_review_convergence/,
'workflow should check workflow.plan_review_convergence before planning',
);
assert.match(
workflow,
/msd config-set workflow\.plan_review_convergence true/,
'workflow should print the enable command instead of silently downgrading',
);
});
test('next workflow routes Route 3 through plan-review-convergence when PLAN_STRATEGY=converge', () => {
const workflow = read(WORKFLOW_PATH);
assert.match(
workflow,
/\/msd:plan-review-convergence/,
'next workflow should reference /msd:plan-review-convergence for the converge route',
);
assert.match(
workflow,
/PLAN_STRATEGY=converge/,
'next workflow should check PLAN_STRATEGY for the converge override',
);
assert.match(
workflow,
/msd:plan-phase/,
'local planning path should remain available for default next runs',
);
// Args-forwarding contract: Route 3 invocation must pass ${CONVERGENCE_ARGS} to the convergence
// command — not just route to the command name but actually forward the built args variable.
assert.match(
workflow,
/\/msd:plan-review-convergence[^\n]*\$\{CONVERGENCE_ARGS\}/,
'Route 3 convergence invocation must include ${CONVERGENCE_ARGS} on the same line as the command',
);
});
test('next workflow forwards reviewer flags and max cycles to convergence', () => {
const workflow = read(WORKFLOW_PATH);
// Non-lane convergence controls remain hand-written literals in the workflow.
const convergenceControls = ['--all', '--text'];
// Reviewer lane flags that were formerly hand-enumerated in the workflow text.
// They must now be DERIVED at runtime via `msd_run review-lane flags`, not listed.
const formerlyHardcodedLaneFlags = [
'--codex',
'--claude',
'--opencode',
'--ollama',
'--lm-studio',
'--llama-cpp',
];
// The literal-absence guard below excludes '--claude': the runtime-launcher
// preamble legitimately contains an unrelated "npx ... --claude --local"
// install-runtime flag, so a substring match on '--claude' would false-positive
// against that literal, not against a re-added reviewer-flag list.
const antiParityLaneFlags = formerlyHardcodedLaneFlags.filter((flag) => flag !== '--claude');
assert.match(workflow, /CONVERGENCE_ARGS/, 'workflow should build convergence pass-through args');
assert.match(
workflow,
/msd_run review-lane flags/,
'workflow should derive reviewer flags from the review-lane roster instead of hand-listing them',
);
for (const flag of convergenceControls) {
assert.ok(workflow.includes(flag), `workflow should pass through ${flag}`);
}
assert.match(workflow, /--max-cycles/, 'workflow should pass through --max-cycles N');
// Anti-parity guard (deliberately inverted polarity): the whole point of the
// review-lane-flags derivation is that reviewer lane flags are declared ONCE
// (in the review-lane roster) and never hand-listed again in workflow prose.
// If a future edit re-adds a hardcoded reviewer-flag list here, that is the
// regression this test exists to catch — so this assertion must FAIL when
// any of these flags reappear as literals in the workflow text.
for (const flag of antiParityLaneFlags) {
assert.ok(
!workflow.includes(flag),
`workflow should NOT hand-enumerate reviewer lane flag ${flag}; it must be derived via review-lane flags`,
);
}
// Behavioral coverage: prove the roster the workflow derives from actually
// yields the flags this test used to hardcode, so the derivation is not vacuous.
const laneFlagsResult = runNode([TOOLS, 'review-lane', 'flags'], { timeoutMs: PROBE_TIMEOUT_MS });
throwIfFailed(laneFlagsResult, `node ${TOOLS} review-lane flags`);
const laneFlags = laneFlagsResult.stdout.split('\n').filter(Boolean);
for (const flag of formerlyHardcodedLaneFlags) {
assert.ok(laneFlags.includes(flag), `review-lane flags should include ${flag}`);
}
});
test('next workflow preserves --auto re-invocation chaining', () => {
const workflow = read(WORKFLOW_PATH);
assert.match(
workflow,
/--auto/,
'workflow should document the --auto chaining behavior',
);
assert.match(
workflow,
/\/msd:progress --next --auto/,
'workflow should re-invoke /msd:progress --next --auto for chaining',
);
// Forwarding contract: the --auto re-invocation must explicitly state that --converge/--cross-ai
// and reviewer flags are forwarded — not just re-invoke --auto alone.
assert.match(
workflow,
/\/msd:progress --next --auto[^\n]*(forwarding|--converge)/,
'workflow --auto re-invocation should document forwarding --converge/--cross-ai and reviewer flags',
);
});
test('full.md documents --converge, --auto, and --cross-ai for /msd:progress', () => {
const fullMd = read(FULL_MD_PATH);
assert.match(
fullMd,
/\/msd:progress --next --auto --converge/,
'full.md should show /msd:progress --next --auto --converge usage',
);
assert.match(
fullMd,
/--auto/,
'full.md should document --auto for progress',
);
assert.match(
fullMd,
/--cross-ai/,
'full.md should mention --cross-ai alias for convergence',
);
});
test('COMMANDS.md documents progress convergence flags and usage', () => {
const commandsDoc = read(COMMANDS_DOC_PATH);
assert.match(
commandsDoc,
/\/msd-progress --next --auto --converge/,
'COMMANDS.md should show /msd-progress --next --auto --converge usage example',
);
assert.match(commandsDoc, /--converge/, 'COMMANDS.md should document --converge for progress');
assert.match(commandsDoc, /--cross-ai/, 'COMMANDS.md should document --cross-ai alias for progress');
});
test('how-to shows /msd-progress --next --auto --converge usage', () => {
const howTo = read(HOW_TO_PATH);
assert.match(
howTo,
/\/msd-progress --next --auto --converge/,
'how-to should show /msd-progress --next --auto --converge usage',
);
});
});