fix(brief-validator): warn on a criterion whose first backticked span is not a command

The runner reads the first backticked span and reports such a criterion unrunnable, and voyage:writing-criteria says the validator uses the same allowlist; the validator skipped it (voyage-lekerepo trial 5, finding 1).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Kjell Tore Guttormsen 2026-10-18 19:50:15 +02:00
commit 16d1bb7dd5
Signed by: ktg
SSH key fingerprint: SHA256:JakMjO6FTBBzN0Bhfj9saOoEjaFxlSdYuZQQpM/lF9Q
2 changed files with 26 additions and 0 deletions

View file

@ -286,6 +286,18 @@ export function validateBriefContent(text, opts = {}) {
'or write each sub-point as its own criterion with its own command.',
));
}
// A backticked span that is not a command (`--stdin` named before the
// command) is what the runner reads first and reports unrunnable; only
// a criterion with no backticked span at all is prose (trial 5, finding 1).
if (!c.command && c.reason === 'not-a-command' && !c.subpoints) {
const span = (c.text.match(/`([^`]+)`/) ?? [])[1] ?? '';
warnings.push(issue(
'BRIEF_SC_NOT_RUNNABLE',
`Success criterion ${c.label} opens with \`${span}\`, which is not a command, so the review's criteria runner will never run it (not-a-command)`,
'The runner runs the first backticked span: put the command first and name anything else in plain words ' +
'(voyage:writing-criteria), or it is reported NOT RUN at review.',
));
}
if (!c.command) continue;
const bare = c.command.replace(/^(?:[A-Za-z_][A-Za-z0-9_]*=\S*\s+)+/, '');
if (needsBash32 && /^bash\s/.test(bare)) {