Claude Code's positional-argument shorthand replaces $N in a command's
text; awk's $(0) is the same field without that collision.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Operator decision 2026-10-02: keep the test set that a person runs
through Anthropic's plugin eval tool. Voyage starts nothing; the gate
counts that entry exactly, and the binding rule and the gate header now
say so instead of claiming historical mentions only.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Seven defects from an independent review of the 6.0 session-order flow,
all verified against the text before fixing:
1. Step 3 branched only on pending/failed/stopped/unreadable, so a session
still writing in-progress (or in_progress) next to a failed one had its
live worktree force-removed. status now classifies each session as
completed, running (no file, pending, in-progress, in_progress, or any
unknown status) or ended-bad (failed, stopped, partial, blocked,
unreadable), keeping the raw status; Step 3 stops on any running FIRST
and touches no worktree, branch or wave.json.
2. The Phase 2.5 wave-in-flight guard was cwd-relative; from a subdirectory
it missed wave.json and Check 4 force-removed the wave. The guard is now
anchored at the git toplevel, and Check 4 opens with a block that refuses
to clean while wave.json exists.
3. Step 6 read progress files from worktrees that Step 3/5 had removed. A
new archive subcommand copies each to orders/wave-W-session-N.progress.json
before any worktree goes; Step 6 and Phase 8 read the copies.
4. NO_WAVE always prepared the lowest wave, so a re-run after a wave-2
failure re-ran wave 1. record-merged writes orders/merged-waves.json
after each merged wave (Step 5 keeps it); next-wave returns the lowest
unmerged wave or none (then Step 4). Phase 8 clears the orders dir only
after a completed report; the record refuses another plan's path.
5. Phase 7.6 named a deleted worktree and promised an unwired re-run. The
order now names the main checkout and says the recovery session's own
audit is the check.
6. (test side, previous commit) progress paths are asserted literally.
7. Hard rule 12 now carries the awaiting-sessions exception.
npm test: 1262 tests, 1260 pass, 0 fail, 2 skipped.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An independent review of the 6.0 session-order flow found seven defects.
These tests pin the fixed behavior:
- status classifies each session as completed, running (no file, pending,
in-progress, in_progress, or any unknown status) or ended-bad (failed,
stopped, partial, blocked, unreadable), keeping the raw status; a failed
session next to a running one reports running, and Step 3 must branch on
running before any failure cleanup
- archive copies each progress file into orders/ before worktrees go
- a merged-waves record drives next-wave, survives Step 5, and clear
resets it (refused while wave.json exists, and for another plan)
- the Phase 2.5 guard is anchored at the git toplevel; Check 4's guard is
executed from a subdirectory and must refuse while wave.json exists
- Phase 7.6 names no deleted worktree and promises no unwired re-run
- Hard rule 12 carries the awaiting-sessions exception
- progress paths are asserted literally; the old self-referential check
stayed green under a basename(planPath) mutation, the new one goes red
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
README claimed four security layers including scoped per-child tool
access (gone with the launched children), an auto-dispatched Phase 7.6
recovery session and a launch template; architecture.md still pointed at
launch.sh. They now describe what 6.0.0 does: session specs plus
orders.md, a recovery order for a session the user opens, three layers.
operations.md gets a status note instead of a rewrite (decision record).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Valgt the literal ${CLAUDE_PLUGIN_ROOT} because that is the form Claude
Code substitutes in a command's text (recorded sessions ran
node <cache>/voyage/5.11.1/lib/...); a :- default is never replaced and
the Bash env is empty. The fallback now picks the highest installed
version under cache/<marketplace>/voyage/<version>/ by version number
(mtime ranked 5.11.0 above 5.11.1 on the measuring machine). Without
this, /trekexecute's multi-session Step 0, plan verification (Phase 7),
/trekreview's criteria checks and /trekresearch's loop gate all exit 2.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The four blocks in trekexecute (x2), trekresearch and trekreview read
${CLAUDE_PLUGIN_ROOT:-}, which Claude Code never substitutes (it replaces
the literal ${CLAUDE_PLUGIN_ROOT}), and the fallback returns the
version-less cache/<marketplace>/voyage directory, so the guard after it
exits 2 every time. Red on both conditions.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The operator decision of 2026-09-27 stands where every session reads it:
a short binding rule in CLAUDE.md that names the replacement (order plus
starter, subagents inside one session) and the gate that enforces it.
CHANGELOG gets an Unreleased entry; the version is left for the release
round to set.
npm test 1250 (1245 pass, 0 fail, 5 skipped). The gate is unchanged:
the new changelog text adds no hit line.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Operator decision 27.09.2026: Voyage works the PM way, so nothing in it
starts Claude Code. Work that needs another session becomes an order plus
a starter line the user gives a session they open themselves; Voyage
never starts the session.
/trekexecute's multi-session path (Phase 2.6) now prepares one wave at a
time: worktree per session, then lib/util/session-orders.mjs writes one
order per session (worktree, steps, done-condition, starter) plus
orders/wave.json, and the run stops. Running the same command again reads
each session's progress file (status mode), merges sequentially with
push-before-cleanup as before, and prepares the next wave. Phase 2.5 skips
the destructive pre-flight while a wave is in flight. Phase 7.6 recovery is
handed over the same way. Hard rule 21: never start Claude Code.
Removed with it, because they existed only for launched children: the
ANTHROPIC_API_KEY billing check (and its dry-run line and Hard rule 1
exception), the hooks-fire version precheck, the safety preamble, the
scoped-tool/turn/budget flags, templates/headless-launch-template.md, the
decomposer's launch.sh (now orders.md), /trekplan's "launch", the README
billing and MCP tuning sections, and scripts/q3-cache-prefix-experiment.mjs
(an experiment that spawned the binary; its results stay in docs/).
/trekresearch's deep-research pre-gate no longer runs the binary for its
version; the post-hoc check was already the authoritative guard.
Chose orders + starters over in-session subagents because the order's done
criterion asks for exactly that, and because /trekexecute disallows the
Agent tool on purpose (Hard rule 10); reopening that is a separate change.
Gate: 54 -> 0 hit lines outside EXEMPT. npm test 1250 (1245 pass, 0 fail,
5 skipped: 3 are the retired-reference scan without its local list).
verify.sh 7/0. The fixture run (tests/lib/session-orders.test.mjs) writes
two orders and wave.json for wave 1 and leaves a trap binary untouched.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A node:test gate that scans every tracked file (the tree a release tag
publishes) for any way to start a Claude Code session or model run: the
CLI with a flag or prompt, `claude plugin eval`, spawn/exec of the binary,
the Agent SDK / claude-code package / GitHub Action, and the permission
bypass flags. Plugin management (install, validate, marketplace add)
starts no session and is not a hit.
The scanner is proven on a known-positive fixture that exercises every
pattern, and on look-alikes that must not hit. Historical mentions (class
c) stand only in EXEMPT, each with a reason and its exact hit count, so a
new hit in an exempt file fails as well. Two entries are the operator's
own eval run through Anthropic's tool; they are listed as their own class
so the decision stays visible.
Red on 57e620b: 54 hit lines in 14 files outside EXEMPT (a: 34 in 6 files,
b: 18 in 7 files, a descriptive comment: 2 in 1 file). EXEMPT holds 16
files and 46 hit lines, and matches exactly.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
5.11.0 ships from the base commit before this branch lands, so the
reference cleanup and its hygiene check go out as 5.11.1: plugin.json,
package.json, package-lock.json (both fields), the README badge and a new
CHANGELOG entry. No tag.
Suite after staging: 1240 tests, 1238 pass, 0 fail, 2 skipped;
verify.sh 7 pass, 0 fail.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The annotation surface's provenance is now described as the pattern it
follows (pencil toggle, intent buttons, popover form, structured export)
rather than by naming a repository that no longer exists. Touches the
plugin/package descriptions, README, docs/architecture.md, the v5.0.3
CHANGELOG entry, and two comments in scripts/annotate.mjs and its test.
Retired-reference check: 12 hits in 7 files -> 0 (content and path names).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A node:test pin that scans every tracked text file and path for the terms
in a gitignored local list (tests/.excluded-terms.local.txt), so the terms
never live in tracked text. The scanner is validated against a planted
known-positive (content and path name), an empty scan fails closed, the
named EXEMPT list is empty and rot-checked, and without the local list the
scan is skipped with an explicit message.
Red on 0be152a with the list present: 12 hits in 7 files.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
New § Stability section: the PM's 8/8 and 7/8 on ea66a98, the cause (correct
lower-case verdicts, 3 of 34 case-8 and 2 of 22 case-6 recorded replies), the
grader changes and why, and the measured result on 7f7161b: case 8 12 of 12
(10 alone + 2 full), case 6 8 of 8 (6 + 2), two full runs 8 of 8 each. The
status block and "every full run gave 8 of 8" now name whose sample that was.
Correction to 687710a's message: it said a grader file carries only
`pattern`. The docs list `flags` for the regex grader. The letter classes
stay, for the reason now written in the doc: a flag applies to the whole
pattern, and the opposite-verdict refusal must stay case-sensitive.
Suite after git add: 1236 tests, 1234/0/2. No version bump: plugin code
is untouched.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Both patterns now start at the beginning of the reply ((?<![\s\S]), which
holds with or without a multiline flag) and refuse the whole reply when it
carries the opposite verdict in capitals (FAIL/FAILED for says-pass,
PASS/PASSED for says-fail), unless negated. The opposite word in lower case
does not refuse: correct case-6 replies say "did not pass" and, once on
687710a, "The other checks passed".
Chosen: refuse only the capitalised opposite, because refusing it in any
case would fail correct replies again, the instability this change removes.
Measured on 22 case-8 and 14 case-6 recorded replies (PM re-measurement +
this session): old says-pass 19/22, new 22/22; old says-fail 13/14, new
14/14; each grader on the other case's replies 0.
Suite after git add: 1236 tests, 1234/0/2.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Measured on 687710a: a correct case-6 reply said "**FAIL**: ... did not pass
... The other checks passed." With lower case accepted, says-pass matches that
reply, so a wrong case-8 reply of the same shape would pass the case. The test
pins that a reply whose capitalised verdict is the opposite one is refused,
while the opposite word in lower case (a side clause) or negated is not.
Red on 687710a: 11 of 12 in tests/lib/proevesett.test.mjs.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Case 8 failed 2 of 12 in the PM re-measurement of ea66a98 on correct replies
that said "passed" without PASS. The patterns now spell each letter as a
class ([Pp][Aa][Ss][Ss]...) instead of relying on a regex flag, since a grader
file carries only `pattern`. The verb forms pass/passes/passed and
fail/fails/failed are verdicts; a preceding "not " or "n't " still refuses.
Measured on the PM's recorded replies (distinct runs): old says-pass 10/12 on
case 8 (reproduces the finding), new 12/12; says-pass on case 6 replies 0/8
both, says-fail 8/8 on case 6 and 0/12 on case 8 both.
Suite after git add: 1235 tests, 1233/0/2.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The PM re-measurement of ea66a98 had case 8 pass 10 of 12. Both failures were
correct replies that gave the verdict only in lower case ("passed"), and
\bPASS(?:ED)?\b is case-sensitive. The test pins lower-case pass/passes/passed
and fail/fails/failed as verdicts, keeps "not passed", "doesn't pass", "failed"
refused by says-pass, and checks that each case's recorded reply does not
satisfy the other case's grader.
Red on ea66a98: 10 of 11 in tests/lib/proevesett.test.mjs.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Version 5.11.0 in every file that carries it: .claude-plugin/plugin.json,
package.json, package-lock.json (both entries; they said 5.10.0, drift
from before 5.10.1), the README badge, and a CHANGELOG entry for
everything since v5.10.1. Chose a minor bump because /trekplan gains a
halt (a new capability, additive schema). Handover 1 changes for
downstream producers: their briefs stop at /trekplan until approved.
No tag and no catalog bump here: release-plugin.mjs runs in the operator's
push round.
Suite after git add: 1234 tests, 1232 pass / 0 fail / 2 skip in 2 of 3
runs. The third failed only the known timing test otel SC #14 tail-latency
(877 ms against an 800 ms ceiling), which this order leaves alone.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
New § Stability: why case 4 was unstable (it could halt on the intent gate,
5 of 6 here before the fix, 6 of 12 and 7 of 10 in the PM re-measurement),
the three changes, the case-4 known-positive (3 of 3 passed with the gate's
code off until names-code; then 0 of 3), and the says-fail cause left
unmeasured (the failing trace was deleted; 0 of 13 kept replies used
another spelling).
After the fix (a384ce6): three full runs (one --runs 3, two --runs 1),
every case 5 of 5, each full run 8 of 8; case 4 alone 10 of 10, 15 of 15
in total. The status line no longer lets 8 of 8 read as a stable state.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
New grader names-code (regex, trace, code\W{1,12}BRIEF_V51_MISSING_SIGNALS),
the same form as the intent case's names-code. It checks what the case's
expected_outcome promised before any run: the validator reports the code.
Checked on this session's recorded traces before the commit: it matches
10 of 10 green runs and 0 of 3 runs with the check disabled.
Known-positive re-run in the scratch copy (validator check 'if (false)'):
0 of 3 cases pass, names-code fails in each, the other 5 graders pass.
Green: tests/lib/proevesett.test.mjs 10 of 10.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Known-positive for case 4, in a scratch copy (never this tree):
brief-validator.mjs with the BRIEF_V51_MISSING_SIGNALS check made
'if (false)'. The case still passed 3 of 3: the child read the rule in
the command prose, halted, named phase_signals, and even found the
planted 'if (false)'. So the halt alone does not show the validator
fired, and no grader checked what expected_outcome already promised
('brief-validator ... reports BRIEF_V51_MISSING_SIGNALS').
New test: case 4 needs a names-code grader reading the validator's JSON
code from the trace (same form as the intent case). Red: 1 of 10.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The scaffold writes a current intent_approved_hash (sha256 over ## Intent +
## Goal) into the case-4 brief. Chose a literal marker over running
intent-approval.mjs --stamp in the scaffold because --stamp emits a
brief-approved record into the live stats the yardstick reads: every eval
run would count as an approval.
New grader no-intent-halt (regex, trace, not_contains
code\W{1,12}BRIEF_INTENT_): a halt at the intent gate fails the case
instead of passing it for the wrong gate.
says-fail / says-pass take FAILED / PASSED as the verdict word. v2's word
boundary dropped them. The one says-fail failure (PM, 1 of 7) is not
explained: its trace was deleted, and 0 of 13 kept replies used another
spelling than FAIL.
Case 4 alone, 10 runs: 10 of 10. In 3 of 10 the child ran the intent gate
first, passed it and went on to the brief-validator. That order halted the
case before. Before the fix, same day: 5 of 6.
Green: tests/lib/proevesett.test.mjs 9 of 9.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Case 4 (plan-halts-without-phase-signals) measured on dc9b480, 6 runs:
5 of 6. The failing run called only intent-approval.mjs, halted on
BRIEF_INTENT_NOT_APPROVED, and so failed validator-ran and
halt-names-signals: the fixture has no intent marker, so the case can
measure the wrong gate. PM: 6 of 12 on dc9b480, 7 of 10 on 66e1fa1.
New tests: the case-4 fixture must pass intent-approval --check and fail
only BRIEF_V51_MISSING_SIGNALS; a not_contains grader must fail the case
on any BRIEF_INTENT_* code in the trace. says-fail / says-pass must take
FAILED / PASSED as the verdict word (v2's word boundary dropped them).
Red: 3 of 9 in tests/lib/proevesett.test.mjs fail, each for its own reason.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
v2 = eight cases, expectations committed in 3892a83 before the run.
Run 1: 8/8 cases, 36/36 graders, 65 s, $1.97 (list-price estimate).
Known-positive for the intent-gate case, in a scratch copy: --check made
to accept everything → 0.75 (names-code fails). Removing only the gate's
command line from trekplan.md did NOT fell it (1.00): the child rebuilt the
call from the surrounding prose — the case measures the gate's effect; the
exact line is pinned by the unit test that executes it. One green run of
case 5 does not prove the instability gone; that needs more runs.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
New case plan-halts-without-intent-approval (expectation committed before
its first run): a 2.1 brief WITH phase_signals and no intent marker.
Graders: intent-approval.mjs --check ran; the trace carries the gate's own
JSON code for BRIEF_INTENT_NOT_APPROVED; no Agent; no plan.md.
review-requires-project: the arg parser now has a CLI that checks the
required flag and prints 'Error: --project <dir> is required.' + usage
(exit 1, code ARG_REQUIRED_MISSING). trekreview.md runs it with
$ARGUMENTS (it passed "$@", which the Bash tool never has, so the parser
never ran — 0 Bash calls in the PM's case-5 traces) and relays its stderr
instead of composing the line. The grader project-required (reply regex,
unstable on backticks) is replaced by parser-ran + names-missing-project
(trace, the parser's JSON code), the same form as names-rule.
Graders: no-error-code is bound to the validator's JSON output form and
covers every REVIEW_* code (WRONG_TYPE and VERSION_FORMAT were missing);
says-pass/says-fail reject a preceding NOT and a longer word; every case
with no-write also gets no-bash-write (redirect, tee, touch, cp, mv, rm,
sed -i in the Bash command). Checked on the PM's 10 recorded traces: no
false positive; known-positive/negative still split.
Red 348aa95 6/7 → green 7/7. Suite 1231: 1229 pass / 0 fail / 2 skip.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Offline structure tests for evals/ (the suite itself needs headless runs):
- no case reaches the steg 1 intent gate (PM: 0 traces touched it). The
new case's scaffold is in: its brief passes brief-validator --soft
(2.1 WITH phase_signals) and --check gives BRIEF_INTENT_NOT_APPROVED —
that test is green, so the fixture reaches the gate.
- review-requires-project: trekreview.md composes 'Error: --project <dir>
is required.' in prose (8/10 in the PM run, backticks broke the regex),
and its arg-parser line passes "$@", which the Bash tool never has.
- no-error-code misses REVIEW_WRONG_TYPE; PASS/FAIL graders are raw
substrings; no-write cannot see a write through Bash.
7 tests, 6 red.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
M3: the header claimed "reflowing a paragraph keeps the approval"; joining
lines gives STALE (PM probe iii-c). It now says what keeps the approval
(indentation, in-line spacing, blank lines, CRLF) and that joining or
splitting lines makes it stale — erring safe. Also states the one-section
and code-block/comment rules from M1.
M7: the trekreview exemption is read from the brief's own frontmatter, so a
relabelled brief walks past the gate — the same trust class as self-stamping.
Now said in the module header, HANDOVER-CONTRACTS and /trekplan's gate prose.
HANDOVER-CONTRACTS also names both brief modes the --approve path reaches.
Red 097bae8 (M3, M7) → green. Suite 1224: 1222 pass / 0 fail / 2 skip.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
M4: the halt regex matched any **halt** in a 2500-char window, so the PM's
mutant (first **halt** → "continue anyway") survived. Two new tests pin the
sentence right after the gate command and the could-not-run halt. Mutant
run: old test ok, both new tests not ok.
M5: nothing guarded 2045432's allowlist line. New test: trekbrief keeps
intent_approved, drops task/project_dir. Mutant (line removed): not ok.
M3 (red): the header claims a reflow keeps the approval; joining lines is
STALE. Pins the honest wording + the STALE behaviour.
M7 (red): the trekreview exemption is not documented as a bypass of the
same trust class as self-stamping (header, HANDOVER-CONTRACTS, trekplan).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
M1: headings inside fenced code blocks and HTML comments are text, not
headings, and ## Intent / ## Goal must each appear exactly once. A second
section, an example section in a code block or comment, or a '## ' line in
a code block inside Intent no longer carries the old approval: check gives
INVALID (duplicate) or STALE, stamp refuses a duplicate. A plain brief
hashes exactly as before (pinned), so no stamped brief goes stale.
Re-run of the PM's 29 probes: vi-a, vi-b, vii-a, vii-b now stop; the rest
unchanged. The PM's p7c probe was a no-op (its replace string is not in the
base brief); vii-c is covered by its own test, red on 66e1fa1.
M2: /trekbrief --approve takes <project-dir | brief-file>. New
intent-approval.mjs --resolve decides: a directory → <dir>/brief.md and
/trekplan --project; a file → itself and /trekplan --brief. Phase 4h and the
stamp line use {BRIEF_PATH}; /trekplan's halt table names
/trekbrief --approve {brief_path}, which works on both brief modes, and so
does the gate's hint. HANDOVER-CONTRACTS' "open to every producer's brief"
is now true. Chose fixing the path over rewording the contract because two
real briefs (docs/*-brief.md) had no approval path at all.
Red ff760ed 9/29 → green 29/29. Suite 1219: 1217 pass / 0 fail / 2 skip.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
M1 (PM probes vi-a, vi-b, vii-a, vii-b, vii-c): extractSection takes the first
## Intent and does not see code blocks or HTML comments, so an added, hidden
or cut-off section rides on the old approval. 5 red. vii-d (comment inside
Intent) and the plain-brief hash are pinned green so the fix cannot move them.
M2: --approve resolves only <dir>/brief.md; a brief planned with
--brief docs/x-brief.md has no approval path but a raw --stamp. 4 red.
The stamp helper now substitutes {BRIEF_PATH} as well as {PROJECT_DIR}.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
New lib/stats/stats-append.mjs: one record on stdin, appended to
<data dir>/<kind>-stats.jsonl. The data dir is CLAUDE_PLUGIN_DATA, else
~/.claude/plugins/data/voyage-ktg-plugin-marketplace (the yardstick's
default). A failed write exits 1 with a reason: reported, never silent.
/trekbrief Phase 7 and /trekplan Phase 12 now run it instead of
"append to ${CLAUDE_PLUGIN_DATA}/…; skip silently". intent-approval's
resolveApprovalDataDir delegates to the same resolver.
Chose a stdin heredoc over a --json argument because a JSON record in a
shell argument breaks on quotes in task text. Only trekbrief and trekplan
are rewired (økt 2's countable form needs exactly those two); the other
commands' stats prose is unchanged and docs/architecture.md says so.
Red a358059 7/7 → green 7/7. Suite 1208: 1206 pass / 0 fail / 2 skip.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Phase 7 of /trekbrief and Phase 12 of /trekplan say "skip silently" when
CLAUDE_PLUGIN_DATA is unset, and the Bash tool env never carries it — the
same silent skip that left brief-approved at 0 records. Økt 2's countable
form (brief-approved + intent_approved: true + a trekplan record with the
same slug) cannot be counted until those records land where the yardstick
reads. 7 tests, 7 red: no stats-append line in either command.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
docs/proevesett.md records what the suite covers and what it does not, how to
run it, the verbatim pass/fail definition with its source, and the first run:
6 of 7 cases (graders 25 of 26), 7 claude -p children, 54 s.
The red case, review-requires-project, lost only its text grader: the child
paraphrased a guard that lives in prose. A diagnostic re-run passed, so it is
unstable, not broken, and it stays red - loosening the pattern after the run
would fit the grader to the result.
Known-positive shown in a scratch copy: with the finding-ID check disabled in
review-validator.mjs, review-validate-flags-bad-finding-id drops from 1.0 to 0.6.
Also recorded, measured by probe: ${CLAUDE_PLUGIN_ROOT} is substituted in the
expanded command text but empty in the child's Bash env, and the plugin
directory is readable from the child's sandbox.
Veikart step 4 uses the suite as its yardstick: a rule change that lowers n stops.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The trekbrief-stats record gained intent_approved in f5dc08f (prose +
jsonl-schemas row) but not lib/exporters/field-allowlist.mjs, so the
exporter would drop it silently. Boolean, low-cardinality — safe label.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Seven `claude plugin eval` cases under evals/, runs: 1, deterministic graders
only (regex, tool_used, file_exists), no ablation. Each case tests one thing
Voyage promises and stops within minutes, headless:
- plan-requires-brief, plan-project-not-initialized, plan-rejects-unknown-export,
review-requires-project: argument guards stop before any Agent or Write.
- plan-halts-without-phase-signals: a brief_version 2.1 brief without
phase_signals halts /trekplan at the sequencing gate, before the swarm.
- review-validate-flags-bad-finding-id (known-positive) and
review-validate-passes-clean-review (known-negative): /trekreview --validate
names REVIEW_BAD_FINDING_ID on a planted bad ID and stays clean on a valid file.
Each case's expected_outcome is committed here, before the suite has run.
Fixtures are written by an inline scaffold.sh (needs --scaffold): chose inline
heredocs because a run cannot read the eval directory, and the add_dirs path
mapping is not documented. evals/results/ is ignored.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Veikart steg 1, økt 1 av 2. The three holes measured in the grounds:
(1) ## Intent / ## Goal were only checked for existence, (2) no approval
marker existed, (3) brief-approved was emitted only on the auto path.
- lib/validators/intent-approval.mjs: --check / --stamp. The marker is
intent_approved_hash (sha256 over normalized ## Intent + ## Goal) +
intent_approved_at. Editing either section after approval → STALE.
- /trekplan Phase 1 (Read the brief, both --brief and --project): runs
--check and HALTS on BRIEF_INTENT_NOT_APPROVED / _STALE / _INVALID with
the remedy spelled out; an unrunnable check halts too.
- /trekbrief Phase 4h (before the Phase 5 fork, asked even in --quick):
shows Intent + Goal verbatim, AskUserQuestion Approve / Revise / Leave;
only "Approve" runs --stamp. --stamp emits brief-approved, so the manual
(default) path records it; the auto path's own emission is removed.
New mode /trekbrief --approve <project-dir> = Phase 4h alone.
- README, CLAUDE.md, command-modes, HANDOVER-CONTRACTS §Handover 1,
jsonl-schemas (trekbrief-stats gains intent_approved).
Valgt ingen brief_version-bump fordi skjemaendringen er rent additiv (to
valgfrie felt) og kravet sitter i /trekplan — enhver produsents brief kan
godkjennes via /trekbrief --approve uten produsentendring. Valgt eget
--check-kall i stedet for et flagg på brief-validator fordi --brief-stien
i dag ikke kjører validatoren i det hele tatt; et nytt validatorkall der
ville også stoppe på andre feil. Valgt fallback-datamappe = målestokkens
(plugins/data/voyage-…) fordi CLAUDE_PLUGIN_DATA er tom i Bash-miljøet og
event-emits egen fallback er stille skip — slik ble brief-approved 0 records.
What the marker does NOT prove (module header, command prose, contract):
same user, same machine — any session can stamp. A trace, not a signature.
Suite 1183 → 1201 (1199/0/2). Mutants M1–M5 (stale check off, check
always valid, gate line removed, stamp emits nothing, no normalization)
each fell ≥ 1 test. yardstick unchanged: RED, 1 of 3 countable.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Veikart steg 1, økt 1 av 2. 18 tests, all failing today:
(a) a brief without an approval marker → the gate line in
commands/trekplan.md exits non-zero (today: no gate, planning proceeds);
(b) marker + ## Intent or ## Goal edited after approval → stale, stops;
(c) a valid marker passes (known negative), and edits outside the two
sections or whitespace-only reflow keep it valid;
(d) the stamp runs before the Phase 5 manual/auto fork and emits
brief-approved, so the manual (default) path records it too.
The gate and stamp lines are extracted from the command prose and
executed, so removing the line or stubbing the module fells the test.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
One command, `node scripts/yardstick.mjs`, prints use, trust and
interrupts. Each row gives countable YES/NO, the value, the denominator and
the source it was read from. A fourth row asks whether the end-state gate can
be fooled by a mutant that keeps the module's signature. Exit is non-zero
while any number is not countable, the mutant is not felled, or the verdict
over the first five deliveries is not judged. Nothing counts a delivery yet,
so today it is RED for the right reasons:
use NO no order in the queue carries work-class
trust YES current ALLOW streak 0 of 37 (BLOCK 14, WARN 12, ALLOW 11,
last 2026-09-05 BLOCK, ordered by ts, read as JSON)
interrupts NO 0 user_input records of ~141k lines in 5 files, 0 emitter
call sites outside lib/stats/ and tests/
mutants 0 of 1 signature-preserving mutants felled: on M9 (every export
returns {status:'PASSED'}, every named test only checks typeof)
the gate closes both D-03 and D-04
It only measures. The gate, the emitter and the order writer are untouched.
The mutant row is meant to stay red until the gate learns to fell M9.
Choices, and why:
- Use counts over the order queue (pending, archive/ and claimed/), because
an order is the unit of work. The denominator is orders dated on or after a
cutoff (default 2026-09-21) that carry `work-class: new`. They count as
having gone through Voyage when a Voyage run in the stats has the order's
`slug:`. Both fields are the contract the order writer must meet. No order
carries them today, so the number is NOT COUNTABLE, not 0 %.
- Mailboxes whose names start with a dot are mailboxes too. A shell `*`
glob skips them, which is why a hand count of `coord/*/orders` comes out
lower. The row names them so a re-measure can reconcile.
- M9 must be LIVE before its row can count. Its typeof tests have to pass on
the M9 tree, checked by a runner independent of the gate, so a broken
fixture can never read as "felled".
Also: tests/fixtures/red-first/02243c6-always-allow-shim.mjs, the shim that
makes the red-first claim of 02243c6 reproducible (6 of 55). The recipe is in
its header.
Red first: the new test file failed to load before scripts/yardstick.mjs
existed, and a dot-mailbox test was red before the census learned about
dot-mailboxes. Suite 1161 -> 1183 (1181/0/2), also green on a clean tree
exported from the index.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An earlier pass scored the same 16 rows from README.md and CLAUDE.md alone and
landed on full 4 / partial 5 / none 7. Re-reading the rows in commands/, agents/,
lib/, hooks/ and tests/ moves three of them and corrects five underlying claims:
- P3 full -> partial. plan-critic is prose ("if blockers are found: revise the
plan"), not a gate. The plan is never committed by the pipeline, and execution
starts on the word `execute`. What is hard is the plan_version 1.7 manifest.
- P9 full -> partial. Exit-code-as-truth and the Phase 7.5 audit are real, but
trekexecute says verbatim "do not block on test-first failures", and nothing
protects a test file from being rewritten during a fix.
- P10 none -> partial. The docs-only pass missed tests/: the config regression
suite and a scored gold eval were there all along. The honest remaining gap is
CI (zero workflow files) and a live-agent eval, not "no evals".
Corrected without moving a verdict: --gates is boolean; /trekreview's input is a
SHA-range diff, not a PR; no hook anywhere returns an "ask" decision; nothing in
the pipeline writes to a memory file.
Result: full 2 of 16, partial 8 of 16, none 6 of 16. Every row carries a file
pointer so a later claim of "we closed that" can be checked against a denominator.
Also records what is worth borrowing from a third-party MIT implementation of the
same playbook, with credit, and what is not: its eval runner and CI example both
invoke Claude Code from code, which this repo prohibits.
No behaviour change. Suite unchanged at 1161 (1159/0/2).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`origin` is a public mirror. Comments in tracked test files named the internal
review mechanism by its role rather than the measurement they came from; the
same class as the frozen file's overclaim fixed in 83d82f1. Replaced with what
they actually are: a re-measurement on a named commit, with the date.
The one remaining occurrence is the assertion that FORBIDS those phrases in
tests/fixtures/end-state-frozen.json - a guard has to spell out what it guards.
Suite 1161 (1159/0/2). Comment-only change.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Measured 2026-09-18: nine ordinary shapes of a plan's `## Verification`
section parsed to zero criteria - an untagged fence, a ```text fence, a
markdown table, `## Verification (acceptance)`, `## Verification:`,
`### Verification`, an unclosed fence earlier in the document. Every one came
out as `0 of 0`, NOT OK, exit 1, and Phase 7 then forbade `result: completed`
without anyone being told that the FORMAT, not the code, was the problem.
"The section is empty" and "I cannot read this format" are different facts.
Three changes, one hole:
- The runner reports `NO_CRITERIA` with a source line (`plan.md:NN`) when the
section is there and nothing in it parsed, and names the two forms it does
read. Same for a brief's `## Success Criteria`, so the evidence block the
conformance reviewer gets says which of the two it is looking at rather than
showing an empty table.
- Phase 7 says it out loud instead of failing silently: report the source line
and the two forms, and say that the plan is what failed there, not the run.
- `/trekplan` now pins what it produces to what the runner reads: the heading
is exactly `## Verification`, the criteria are a bullet whose first
backticked span is the command or a shell-tagged fence, and the command must
be one the allowlist runs. A doc-consistency test holds the writer and the
reader together, so a runner that learns a new form must update the source.
Honest about the round trip: the two round-trip tests were GREEN on arrival -
the template already writes the bullet form the runner reads. What was missing
was not the format but the PIN: `/trekplan` mandated neither the heading string
nor the format, so a plan could satisfy the command's own instructions and
still parse to nothing. The tests now hold that.
Red first: 5 of the 7 new tests failed before the change (3 NO_CRITERIA, 2
doc-consistency); the 2 round-trip tests are guards, and said so above.
Suite 1161 (1159/0/2). Gate unchanged: defects 0 of 7, intact, exit 1.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The cosmetic close came back one level up. The probe reads the test file from
the same tree it measures, so a four-line file holding two EMPTY tests with the
two named names closed D-03 and D-04 on a tree where `lib/verification/` did
not exist at all - "defects 0 of 7, registry intact" (measured 2026-09-18).
First a two-line stub exporting the right symbols; then an empty test with the
right name. A name is always forgeable.
So the named test is now run twice. Once on the tree, as before - and once in a
sandbox where the module the condition declares in `stubs` is replaced by a
stub exporting the same names, all inert. If the test still passes there, it
binds the name and not the behaviour, and the condition THROWS: NOT FELLABLE,
counted open. A condition that declares no `stubs`, or names a module that is
not there, cannot fire either.
The sandbox is a symlink overlay: every entry of the tree is symlinked, and
only the test file and the stubbed module are materialised for real - Node
resolves an ESM import through the realpath, so a symlinked test file would
import the original module and never see the mutant. Nothing is ever written
inside the measured tree, and the only directory removed is the one this code
made under the system temp dir (pinned by a test).
M7 is now a permanent mutant beside M6, in two forms: the checkpoint's own
reproduction (unfixed tree + empty named tests) and the harder one (the real
module present, so the stub can be built and the empty test passes against it).
Both report `defects 2 of 7`. A positive control pins that D-03/D-04 still
CLOSE on the real tree, so "not closed" everywhere cannot read as a working
probe.
The frozen denominator moves a third time, deliberately, and its `why` no
longer claims authority it does not have: the second and third amendments were
maintenance decisions by the maintainer, not operator decisions, and the
tracked file now says exactly that.
Measured after:
real tree node scripts/end-state-gate.mjs -> defects 0 of 7, intact, exit 1
M6 (stub) 8d1669e + current gate/registry/frozen + lib/cosmetic/stub.mjs -> 2 of 7
M7 (empty) 8d1669e + current gate/registry/frozen + 3-line
tests/lib/criteria-runner.test.mjs with the two named tests -> 2 of 7
Red first: 4 of the new tests failed before the change. Suite 1154 (1152/0/2).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A denylist in front of /bin/sh is whack-a-mole. Measured 2026-09-18, end to
end through both screens: 5 of 11 named evasions ran with real effect - a
`command` prefix reached git, an escaped `rm` inside a shell fence deleted a
directory, `find -delete` deleted a file, `>|` and `tee` wrote outside the
working tree, a python one-liner deleted the whole tree - and 19 of 28 got
past the refusal list on its own. Every quoting, aliasing and indirection form
of the shell is another mole.
So the screen is now an ALLOWLIST. A criterion runs only when its first word
is a known test runner (npm test, npm run <script package.json declares>,
node --test, vitest, jest, pytest, python -m pytest, uv run pytest,
cargo test, go test, make test, bash <script under tests/>, a read-only git
subcommand) AND the command carries no shell operator and no newline.
Everything else is NOT RUN with the reason said out loud: never run, and never
reported as a failure either - an absent measurement is not a finding. That
also closes the smaller hole in the same file: a bare word a sentence merely
names (`whoami`, `login`, `package.json`) is no longer executed, because it is
not a runner.
REFUSED_BY_POLICY is gone with the list that produced it; a command outside
the allowlist is `unrunnable`, which in plan mode still fells the run and in
brief mode is reported to the reviewer as an absent measurement.
What the allowlist deliberately does NOT do, said in the file and in the
reviewer's rubric: it is not a sandbox. `npm test`, `npm run <script>` and
`make test` run whatever the repo's own package.json/Makefile says they run,
including a script that pushes - that is the repo's responsibility. And it
rejects honest commands too: an env prefix, a project's own binary, anything
piped. A check that needs one of those is declared through
`bash tests/<script>.sh`, the documented way in.
Red first: 6 of the new tests fail against the previous runner (measured with
an always-allow shim so the module still loads), including the end-to-end one
where the canary directory was deleted and files were written outside the
tree. The fixtures move from `true`/`false` to two allowlisted shell fixtures,
because `false` is no longer a runner - the fail case must still be a real
non-zero exit, not an unrun criterion.
Suite 1148 (1146/0/2).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
/trekreview passed --cwd and trekexecute Phase 7 did not, so the same criterion
could resolve two ways in the two phases (PM checkpoint 2026-09-18, MINOR).
It stopped being cosmetic with the refusal list: --cwd is the boundary a write
is measured against, so leaving it unset silently moves that boundary to
whatever the process cwd happens to be. Both phases now resolve
CRITERIA_CWD="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
- one form, with a fallback, so a repo-less checkout falls back instead of
passing an empty --cwd. Pinned in tests/lib/doc-consistency.test.mjs.
Red first: the new pin failed on both phases before this change.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
/trekreview now runs the commands a BRIEF declares, and a brief is an artifact
that can arrive from outside the repo. Measured 2026-09-18 on 6cafb4c: the
executor denylist stopped a download piped into a shell, but the remote-writing
git subcommand and a recursive delete of a path both RAN. The denylist screens
catastrophe (root deletion, fork bombs, mkfs); it was never meant to screen an
artifact under review.
A second screen, in the runner and ahead of the denylist, refuses four classes:
- a remote-writing git subcommand. The subcommand is found by walking git's own
options (`-C`, `-c`, `--git-dir`, ... take a value), so `git status` and
`git log` still run and `git -C sub push` does not.
- a recursive delete: any `rm` carrying `-r`/`-rf`/`--recursive`. A plain
`rm build/artifact.txt` still runs.
- a download piped straight into a shell (also caught by the denylist; pinned
here so the runner does not depend on another file for it).
- a write outside the working tree. `/dev/null`-class devices are fine, and so
is anything under the working tree; `~/...`, an absolute path elsewhere, and
a target carrying an unexpanded `$VAR` are refused - the runner cannot know
where a variable points, and guessing is how a screen stops screening.
A refusal is its own outcome, REFUSED_BY_POLICY: the command never reaches a
shell, and `summary.ok` is false in both plan and brief mode. For the reviewer,
REFUSED is like NOT RUN - the absence of a measurement, never on its own a
finding - and the rubric and the evidence block both say so.
Chosen deliberately, and it is stricter than today's habit: writing scratch to
/tmp is refused too. The repo's own example plan does `> /tmp/out`. Verification
output belongs in the working tree; exempting the whole system temp dir would
have made the rule unstatable, since a working tree created under /tmp then
contains its own escape hatch.
NOT covered, stated rather than implied:
- other writing git subcommands (tag, remote, config, gc) - only push is listed
- writes through a wrapper: `sh -c '...'`, `xargs`, `find -exec`, a Makefile
target, a script the criterion invokes. The screen reads the command it is
given, not what that command goes on to do.
- `>` inside a quoted string reads as a redirect, so a criterion echoing a
literal `>` is refused. Fail-closed, on purpose.
- the whole surface still runs with the invoking process's permissions; this is
a refusal list, not a sandbox.
The denylist-layer test now uses a stand-in command with a screen double: the
refusal list catches a recursive delete first, so naming one there would have
stopped exercising the denylist layer at all.
Red first: the 6 new tests failed before this change (`refuseCommand` did not
exist), and the fixture brief's four writes ran.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Measured 2026-09-18 (PM checkpoint on 6cafb4c, mutant M6): a two-line file
export function runPlanVerification() { return { ok: true }; }
export function formatCriteriaEvidence() { return ''; }
dropped into an otherwise UNFIXED tree closed both D-03 and D-04, and the gate
reported "defects 0 of 7, registry integrity: intact". The same checkpoint felled
5 of 5 mutants with the SUITE and 0 of 5 with the GATE. Both probes called
themselves `behaviour` while asking a grep whether an export name was present -
a phrase probe pointed at code.
A check condition may now name a test instead of a pattern:
{ "test": "tests/lib/criteria-runner.test.mjs", "name": "<exact test name>",
"expect": "fails" }
The gate spawns the test runner on that one test and reads its TAP line.
`expect: "fails"` holds - the defect stays open - while the test does not pass.
A missing file, a name that matches nothing, a runner that will not start: all
throw, which counts as NOT FELLABLE and therefore open. A check that cannot fire
is never "fixed".
The child's `NODE_TEST_*` env is stripped. The gate usually runs UNDER the test
runner, and an inherited `NODE_TEST_CONTEXT` makes the grandchild report over
the parent's IPC channel instead of stdout - the TAP line would never arrive and
the probe would silently stop felling anything.
M6 is now a permanent mutant in the gate's own test, so the cosmetic close
cannot come back. Verified end to end against a real stub tree:
git archive 8d1669e | tar -x -C $TMP
cp scripts/end-state-gate.mjs scripts/end-state-registry.json $TMP/scripts/
cp tests/fixtures/end-state-frozen.json $TMP/tests/fixtures/; cp STATE.md $TMP/
printf 'export function runPlanVerification…' > $TMP/lib/cosmetic/stub.mjs
node scripts/end-state-gate.mjs --root $TMP
-> defects 2 of 7, both NOT FELLABLE, exit 1
The gate also now states the limit out loud, as its own line above the table:
a behaviour probe proves the capability WORKS; that Phase 7 and Phase 4.5 CALL
it is pinned by TEXT in doc-consistency, not proven deterministically - real
proof is a headless plugin-eval run against a fixture plan (week 40).
FROZEN DENOMINATOR AMENDED, second time in one day, by the work order that
carries this change: D-03 and D-04 signatures moved because their checks
changed on purpose. tests/fixtures/end-state-frozen.json and the FROZEN literal
in tests/scripts/end-state-gate.test.mjs are updated together, both dated, both
saying why. No entry was removed.
Gate after this change: defects 0 of 7, registry integrity intact, exit 1.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
"The first backtick span is the command" is right for a plan, whose template
puts the command first, and wrong for a brief, whose criterion usually opens
by NAMING the thing under discussion. Measured 2026-09-18 on the repo's own
example brief: 5 of 6 criteria FAILED, 3 of them parse artifacts - `--verbose`
run as a command gave exit 2 ("invalid option"), `tests/` gave exit 126 ("is a
directory"). The rubric reads a FAILED result as decisive, so each one became
a BROKEN_SUCCESS_CRITERION BLOCKER about prose.
looksLikeCommand() screens the span by SHAPE only - no filesystem lookup, so a
span parses the same everywhere. Refused: a leading flag, a directory, a token
carrying quotes/braces/prose, and a lone relative path with a slash (an
explicit ./, ../, / or ~/ still runs, as do env-var prefixes). A refused span
is `unrunnable` with reason `not-a-command` - its own outcome, never FAILED,
and it never reaches a shell.
It deliberately does NOT scan on to a later span. "The first span that LOOKS
like a command" invents commands out of prose: in that same example brief it
would have run `whoami` and `login`, two real binaries a sentence happens to
name. An absent measurement is honest; a guessed one is not.
The shape check applies to prose spans only. Inside a shell-tagged fence the
author has already declared shell, so `[ -f x ] || exit 1` still runs.
The rubric follows: a NOT RUN result is never on its own a finding. The
Partial row now describes half-built DELIVERED CODE, and the reviewer gets a
table of the three reason strings - no-command, placeholder, not-a-command -
with what each says about the sentence rather than about the code.
Not covered, stated for the record: a multi-token span whose first token is a
non-executable file (`tests/golden/login.stdout --check`) still runs, and a
criterion whose command is real but whose binary is absent still reports the
shell's exit 127 - that is a true measurement of a missing binary, not a
parse artifact.
Red first: 4 runner tests + 1 doc-consistency pin failed before this change.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>