fix: the trial's mechanical faults -- approval label, import path, stats, annotate path, red kind

voyage-lekerepo end-to-end trial (2026-10-06), report points 8, 10, 12, 15, 16:

- approval-log: «Approve (Recommended)» / «Godkjenn (anbefalt)» is the
  approval it labels; the suffix never turns a hollow answer into a yes.
- trekexecute Phase 8: the state-file block imports atomic-write through
  ${CLAUDE_PLUGIN_ROOT}; a relative import only ran with cwd = plugin root
  (the test runs the block from another directory).
- trekreview, trekresearch, trekcontinue: stats go through stats-append like
  the other commands, and a failed write is reported, never skipped
  silently (closes the open STATE finding for all three). The schema
  fixture's stale line refs for these and trekplan are re-measured.
- annotate prints an absolute path, so file://{$ANNOT_HTML} is a live link.
- red-proof: `red_kind` = load-error | test-failure on a proven red.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Kjell Tore Guttormsen 2026-10-08 06:53:15 +02:00
commit 81dc5af680
Signed by: ktg
SSH key fingerprint: SHA256:JakMjO6FTBBzN0Bhfj9saOoEjaFxlSdYuZQQpM/lF9Q
9 changed files with 191 additions and 24 deletions

View file

@ -242,8 +242,15 @@ const HOLLOW = [
];
/** @returns {{ approved: boolean, reason: 'explicit'|'empty'|'hollow'|'not-explicit' }} */
// AskUserQuestion's own guidance puts «(Recommended)» after the label it
// recommends, so the operator's real «Approve (Recommended)» was refused in the
// voyage-lekerepo trial (2026-10-06). The suffix is stripped; what is left must
// still be an explicit approval on its own.
const RECOMMENDED_SUFFIX = /\s*\((?:recommended|anbefalt)\)\s*$/u;
export function isExplicitApproval(answer) {
const t = String(answer ?? '').trim().toLowerCase().replace(/[\s.!]+$/u, '').replace(/\s+/g, ' ');
const t = String(answer ?? '').trim().toLowerCase().replace(/[\s.!]+$/u, '')
.replace(RECOMMENDED_SUFFIX, '').replace(/[\s.!]+$/u, '').replace(/\s+/g, ' ');
if (!t) return { approved: false, reason: 'empty' };
if (EXPLICIT.has(t)) return { approved: true, reason: 'explicit' };
if (HOLLOW.some((h) => t === h || (h.includes(' ') && t.includes(h)))) return { approved: false, reason: 'hollow' };