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

@ -191,6 +191,20 @@ test('brief: a success criterion whose command the runner can never run is a war
assert.match(w[0].hint, /bash tests\//);
});
test('brief: a criterion whose first backticked span is not a command is a warning, as the runner reports it (trial 5, finding 1)', () => {
const r = validateBriefContent(brief([
'- The contract holds, now that the usage line names `--stdin` and the README too: `/bin/bash tests/contract.sh` exits 0',
'- `npm test` exits 0.',
]));
assert.equal(r.valid, true, JSON.stringify(r.errors));
const w = r.warnings.filter((x) => x.code === 'BRIEF_SC_NOT_RUNNABLE');
assert.equal(w.length, 1, JSON.stringify(r.warnings));
assert.match(w[0].message, /SC1/);
assert.match(w[0].message, /--stdin/);
assert.match(w[0].message, /not-a-command/);
assert.match(w[0].hint, /first backticked span/);
});
test('brief: runnable and prose-only criteria raise no runnability warning', () => {
const r = validateBriefContent(brief(['- `npm test` exits 0.', '- `bash tests/e2e.sh` exits 0.', '- The output reads well.']));
assert.ok(!codes(r.warnings).includes('BRIEF_SC_NOT_RUNNABLE'), JSON.stringify(r.warnings));