feat(trekexecute): couple the executor to the review gate, the plan approval and the step checks
Wave 2, order 6. /trekexecute now: - refuses a plan whose logged approval does not cover its current sha256 (K2, approval-log verify --kind plan), before step 1; - runs the step check (K9) and the step's optional Regression: (K10, new lib/verification/regression-check.mjs through the criteria runner) at step end, before the checkpoint; exit 2 is "not measured", never green; - runs the architecture rules (K5) and the plan-file sweep (K6) after the last step; a violation or a file outside the plan turns completed into partial; - reviews by itself in the closing run (Phase 7.2): run the thing, /simplify, /trekreview, verdict-gate; a BLOCK adds the review's remediation steps after the plan validator accepts them and the operator re-approves the plan, capped at TREKEXECUTE_MAX_REVIEW_ROUNDS (3); - makes the main-merge gate a lock on the verdict: BLOCK or undecided keeps the merge closed; when open, the executor merges and the guard's question is the one question (operator decision 2026-10-08). The --gates pause before the wave merge-back is gone for the same reason. The guard refuses a merge into main and a push in the permission mode that shows no prompts, since no one can be asked (operator decision 2026-10-09; two mode-read exemptions approved by the operator). progress.json gains optional coupling fields (Handover 5); the validator rejects the runner's exitCode in plan_verification.failed_criteria (C15). Pointer lines name the skills test-first and debugging (S2). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
4ee711a6ac
commit
920fbd3c47
15 changed files with 1310 additions and 64 deletions
|
|
@ -22,6 +22,8 @@
|
|||
// tag/release, destructive git (hooks/lib/git-approval.mjs). The prompt is
|
||||
// labelled [plugin:voyage] and also appears in auto mode (hooks reference
|
||||
// section PreToolUse decision control). Advisories are added to the prompt.
|
||||
// In the permission mode that shows no prompts, a merge into main and a
|
||||
// push are BLOCKED instead: there is no one to ask (operator, 2026-10-09).
|
||||
// - WARN (exit 0): risky but recoverable operations — advisory to stderr
|
||||
// - Allow (exit 0): everything else
|
||||
//
|
||||
|
|
@ -271,6 +273,24 @@ try {
|
|||
approvals = [{ name: `a command the approval guard could not read (${e.message})`, kind: null, way: 'approve only if you have read the command yourself.' }];
|
||||
}
|
||||
|
||||
// In the permission mode where Claude Code shows no prompts, an "ask" reaches
|
||||
// no one and the call runs unattended. A merge into main and a push are refused
|
||||
// there instead, with the way forward (operator decision 2026-10-09: nothing
|
||||
// goes to main without a person, in any mode). /trekexecute relies on this: its
|
||||
// main-merge gate is a lock, and this guard's question is the one question.
|
||||
const NO_PROMPT_MODE = 'bypassPermissions';
|
||||
const unattended = input.permission_mode === NO_PROMPT_MODE
|
||||
? approvals.filter((m) => m.cls === 'merge' || m.cls === 'push') : [];
|
||||
if (unattended.length > 0) {
|
||||
process.stderr.write(
|
||||
`[voyage] BLOCKED: ${unattended.map((m) => m.name).join(', ')} with no one to ask\n` +
|
||||
` Command: ${normalized.slice(0, 200)}${normalized.length > 200 ? '...' : ''}\n` +
|
||||
' This session runs in the permission mode where Claude Code asks nothing, so the approval guard ' +
|
||||
'cannot ask a person. Way forward: run the command yourself, or switch to a mode that asks, then retry.\n'
|
||||
);
|
||||
process.exit(2);
|
||||
}
|
||||
|
||||
if (approvals.length > 0) {
|
||||
// What the person is asked is what post-approval-log.mjs logs once the call ran.
|
||||
const loggable = approvals.filter((m) => m.kind).map(({ kind, name, subject }) => ({ kind, name, subject }));
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue