docs(review): stop rule in the plan loop, review loop and critic prompts (decision 217, 220)

/trekplan Phase 9: when --record or the gate reports the stop rule there
is no new round; AskUserQuestion with the class and counts, "Known issue"
(--accept-open, a security finding refused) or "New mechanism" (stop, the
plan stays unapproved); the round-3 blocker path never offers
--extra-round after the stop rule. Phase 10 prints round of
max_rounds + extra_rounds.

/trekexecute Phase 7.2 step 4: after a round-2 BLOCK or fixed-pending,
before any R-step, review-rounds.mjs prepare --dry-run; review-stop-rule:
gives the same question ("Known issue" = /trekreview --answers, a BLOCKER
keeps the merge locked; "New mechanism" = stop). /trekreview Phase 3 tells
the stop rule's exit 1 from the cap's. plan-critic and scope-guardian:
rule_key names the class, never the instance.

New pins in review-loop-docs, critic-severity-rules and review-prose-pins.

Part 1, step 1.4 of the E plan.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Kjell Tore Guttormsen 2026-11-08 07:16:05 +01:00
commit 08e8aa1c66
Signed by: ktg
SSH key fingerprint: SHA256:JakMjO6FTBBzN0Bhfj9saOoEjaFxlSdYuZQQpM/lF9Q
8 changed files with 134 additions and 12 deletions

View file

@ -138,3 +138,32 @@ test('Handover 4 states the overlap match and the distinct id', () => {
assert.ok(h4.includes('only on overlapping text'), 'Handover 4 says the gate matches "only on overlapping text"');
assert.ok(h4.includes('`<rule_key>#n`'), 'Handover 4 names `<rule_key>#n`');
});
// ---- [STP] the stop rule (E plan, Part 1 step 1.4; decisions 217, 220) ----------------
const P10 = flat(phase(read('commands/trekplan.md'), 10));
/** Phase 9's stop-rule paragraph: from its bold lead to the next bold lead that opens a line. */
function stopPart() {
const at = P9.indexOf('**The stop rule**');
assert.ok(at >= 0, 'Phase 9 has a **The stop rule** paragraph');
const next = P9.indexOf('**After round 3', at);
return P9.slice(at, next < 0 ? undefined : next);
}
test('[STP] Phase 9: the stop rule gives no new round and asks Known issue or New mechanism, never one more round', () => {
const s = stopPart();
for (const want of ['stop_rule.fired', 'PLAN_GATE_STOP_RULE', 'no new round', 'AskUserQuestion',
'**Known issue**', 'plan-gate.mjs --accept-open', 'PLAN_GATE_SECURITY_OPEN',
'**New mechanism**', 'the plan stays unapproved', 'the direction goes to the operator']) {
assert.ok(s.includes(want), `the stop-rule paragraph says ${JSON.stringify(want)}`);
}
assert.doesNotMatch(s, /\*\*One more round\*\*/, 'the stop-rule question has no "One more round" option');
assert.ok(P9.includes('never offers `--extra-round` after the stop rule'),
'the round-3 blocker path says it never offers --extra-round after the stop rule');
});
test('[STP] Phase 10 prints the round of max_rounds + extra_rounds', () => {
assert.ok(P10.includes('**Critic rounds:** {round} of {max_rounds + extra_rounds}'), 'Phase 10 prints round of max_rounds + extra_rounds');
assert.ok(!P10.includes('**Critic rounds:** {N} of {max}'), 'the old "{N} of {max}" is gone');
});