fix: close what an independent review found in the trial 2 fixes

- The pre-compact hook read only a checkpoint that started with `git commit`,
  so every plan in the new `git add … && git commit` form rebuilt nothing, and
  an empty match also skipped the session-state refresh. It now finds the
  commit anywhere in the span and always refreshes; first test for the hook.
- A sub-bullet that carries its own command stays a criterion (folding it
  left the command unrun); the shallowest bullet sets the base indent.
- Only a MINOR/SUGGESTION /code-review finding outside the range is released
  from forbidding ALLOW: a MAJOR one may be a break cited at an unchanged
  caller.
- `reuse` is an improvement category; the session-spec template stages too;
  a negated bash 3.2 constraint, an env prefix and `bash -c` are handled.
- Stale comment, Declined-to-Judge wording and rule-key counts brought in line.

Tests fail on 7c11c2a first (9 of 24).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Kjell Tore Guttormsen 2026-10-08 15:21:56 +02:00
commit b0d96a2d53
Signed by: ktg
SSH key fingerprint: SHA256:JakMjO6FTBBzN0Bhfj9saOoEjaFxlSdYuZQQpM/lF9Q
13 changed files with 194 additions and 54 deletions

View file

@ -274,7 +274,9 @@ export function validateBriefContent(text, opts = {}) {
// on a Mac with a newer bash installed that is 5.x, so the constraint is
// never measured (voyage-lekerepo trial 2, 2026-10-08).
const constraints = (extractSection(body, 'Constraints') ?? '') + (extractSection(body, 'Non-Functional Requirements') ?? '');
const needsBash32 = /\bbash\s*3\.2\b/i.test(constraints);
// Line by line, and a line that negates it ("not required") does not count.
const needsBash32 = constraints.split('\n').some((l) =>
/\bbash\s*3\.2\b/i.test(l) && !/\b(?:not|no longer|never|no need)\b/i.test(l));
for (const c of parseSuccessCriteria(body)) {
if (c.subpoints && !c.command) {
warnings.push(issue(
@ -285,11 +287,15 @@ export function validateBriefContent(text, opts = {}) {
));
}
if (!c.command) continue;
if (needsBash32 && /^bash\s/.test(c.command)) {
const bare = c.command.replace(/^(?:[A-Za-z_][A-Za-z0-9_]*=\S*\s+)+/, '');
if (needsBash32 && /^bash\s/.test(bare)) {
const script = bare.split(/\s+/)[1] ?? '';
warnings.push(issue(
'BRIEF_SC_BASH_NOT_PINNED',
`Success criterion ${c.label} runs \`${c.command}\`, but the brief requires bash 3.2: \`bash\` is whichever bash comes first on PATH, which may be newer`,
`Write it as \`/bin/bash ${c.command.replace(/^bash\s+/, '')}\` (/bin/bash tests/<script>.sh) so the check runs under the system bash.`,
script.startsWith('tests/')
? `Write it as \`/bin/bash ${bare.replace(/^bash\s+/, '')}\` (/bin/bash tests/<script>.sh) so the check runs under the system bash.`
: 'Put the check in a script under tests/ and run it as `/bin/bash tests/<script>.sh`, so it runs under the system bash.',
));
}
const r = runnableCheck(c, { cwd: opts.cwd });