Commit graph

462 commits

Author SHA1 Message Date
57e620b028
chore(release): bump to 5.11.1 v5.11.1
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>
2026-09-23 12:32:24 +02:00
d0dee914be
docs: remove dead references to a retired repository (green)
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>
2026-09-23 12:28:05 +02:00
e1637c2530
test(hygiene): no tracked file may carry a retired-repository reference (red)
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>
2026-09-23 12:25:43 +02:00
0be152ad29
docs(proevesett): case 8 12 of 12 after the verdict graders read any letter case v5.11.0
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>
2026-09-23 11:41:13 +02:00
7f7161b2b2
fix(proevesett): a capitalised opposite verdict refuses the says-* graders
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>
2026-09-23 11:34:29 +02:00
f838e3c527
test(proevesett): a capitalised opposite verdict refuses says-pass/says-fail (red)
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>
2026-09-23 11:33:19 +02:00
687710a975
fix(proevesett): says-pass/says-fail read the verdict in any letter case
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>
2026-09-23 11:29:39 +02:00
aee9b42dda
test(proevesett): says-pass/says-fail must take the verdict in any letter case (red)
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>
2026-09-23 11:27:16 +02:00
ea66a98c60
chore(release): 5.11.0 — the intent approval gate is a new capability
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>
2026-09-23 11:05:16 +02:00
e7687650c4
docs(proevesett): stability with its denominator; v2's 8 of 8 was one sample
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>
2026-09-23 10:59:59 +02:00
a384ce6cab
fix(proevesett): case 4 reads the sequencing gate's own code from the trace
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>
2026-09-23 10:50:48 +02:00
63b74bdaa4
test(proevesett): red — case 4 passes with the sequencing gate's code switched off
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>
2026-09-23 10:49:48 +02:00
a0ee9e863c
fix(proevesett): case 4's fixture is intent-approved, so only the sequencing gate can stop it
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>
2026-09-23 10:49:05 +02:00
afb2745cd0
test(proevesett): red — case 4 can halt on the intent gate; says-fail rejects FAILED
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>
2026-09-23 10:45:23 +02:00
dc9b480780
docs(proevesett): v2 first run is 8 of 8; the new case falls on a planted code fault
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>
2026-09-23 09:32:37 +02:00
3892a835dd
test(proevesett): a case for the intent gate; case 5's error comes from code; graders see what they claim
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>
2026-09-23 09:28:38 +02:00
348aa95639
test(proevesett): red — no case for the intent gate, case 5 composed in prose, weak graders
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>
2026-09-23 09:26:31 +02:00
4592b40873
docs(intent): M3 + M7 — the header says only what is true
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>
2026-09-23 09:24:16 +02:00
097bae8a02
test(intent): M3–M7 — halt pinned where it acts, allowlist guarded, header claims pinned
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>
2026-09-23 09:23:04 +02:00
20f32bb06b
fix(intent): M1 + M2 — the parser fails closed; --approve reaches any brief file
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>
2026-09-23 09:22:03 +02:00
ff760edba2
test(intent): M1 + M2 red — hidden/extra Intent sections pass; --approve only finds <dir>/brief.md
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>
2026-09-23 09:18:49 +02:00
d906999036
fix(stats): M6 — trekbrief and trekplan records land where the yardstick reads
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>
2026-09-23 09:17:52 +02:00
a358059997
test(stats): M6 red — trekbrief/trekplan stats records must land with CLAUDE_PLUGIN_DATA empty
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>
2026-09-23 09:15:52 +02:00
66e1fa1937
merge: Voyage's fixed test set as a plugin-eval suite (0eb18f3) onto step 1's intent gate 2026-09-23 09:05:06 +02:00
0eb18f39e6
docs(proevesett): the eval suite's first run is 6 of 7, and the known-positive falls on a planted fault
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>
2026-09-23 07:56:32 +02:00
2045432ce7
fix(stats): intent_approved reaches the OTel allowlist like every other trekbrief field
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>
2026-09-23 07:52:45 +02:00
d311f3b49a
test(evals): Voyage's fixed test set as a plugin eval suite, expectations written before the first run
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>
2026-09-23 07:52:34 +02:00
f5dc08f660
feat(intent): /trekplan halts without an approved intent; the approval is stamped on both paths
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>
2026-09-23 07:50:51 +02:00
f39e7ccf39
test(intent): /trekplan must stop without an approved intent — RED
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>
2026-09-23 07:47:17 +02:00
1779411b49
feat(measure): the yardstick prints Voyage's three numbers with denominators from the source, and it is RED
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>
2026-09-22 21:50:06 +02:00
5f1ae4b4c2
docs(sdlc): measure Voyage against the playbook's 16 practices, from code
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>
2026-09-20 16:20:07 +02:00
3b3f06dd45
docs(tests): tracked comments name the measurement, not the coordination
`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>
2026-09-18 03:01:09 +02:00
f90e1cf02d
fix(verification): a section the runner cannot read SAYS so, and trekplan is pinned to the format it reads
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>
2026-09-18 03:00:13 +02:00
83d82f121d
fix(end-state): a behaviour probe must FELL a stub, not just name a test
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>
2026-09-18 02:56:22 +02:00
02243c6365
fix(verification): the criteria runner screens with an ALLOWLIST, not a denylist
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>
2026-09-18 02:51:40 +02:00
e1e7bdfaf1
fix(execute): Phase 7 pins the criteria runner to the working tree, like Phase 4.5
/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>
2026-09-18 02:14:57 +02:00
52b87978cb
fix(verification): the runner refuses writes a brief may not perform
/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>
2026-09-18 02:14:01 +02:00
a7af76e5ff
fix(end-state): the D-03/D-04 probes RUN a named test instead of grepping for a symbol
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>
2026-09-18 02:08:40 +02:00
106dcb0091
fix(verification): a backticked span is only run when it IS a command
"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>
2026-09-18 02:04:46 +02:00
0013d292d6
fix(verification): the criteria runner reads the repo's own plan format
The runner read bullet lines only. The repo's own example plan writes its
whole acceptance run as a fenced bash block, so `## Verification` parsed to
ZERO criteria, the runner exited 1, and Phase 7 forbade `result: completed`
- a correct plan felled every single-session run. Measured 2026-09-18 on
6cafb4c: none of the repo's plan artifacts exited 0.

Fences are now read twice over, for two opposite reasons:

- a `## ` heading INSIDE a fence is quoted text and no longer opens a
  section. examples/02-real-cli/REGENERATED.md is a report that quotes a
  plan outline inside one fence; it used to yield an empty section that
  read as "0 of 0", and now yields the honest NO_VERIFICATION_SECTION.
- a shell-tagged fence inside the section holds the commands. The tag list
  is closed (bash/sh/shell/zsh/console/shell-session): an untagged fence is
  more often expected OUTPUT than input, and inventing a criterion from
  output is the failure this file exists to prevent.

Blank and comment-only lines inside the block declare nothing. A `$`/`>`
console prompt is stripped; a `#` root prompt is NOT, because it cannot be
told from a comment and running a comment is the worse mistake.

Measured after the fix (parse only - one example names a fictional CLI):
examples/01 6 criteria, plan-template 2 (both placeholders, correctly NOT
RUN), the two runner fixtures 2 each, plan-run-C 1, REGENERATED.md 0 with
NO_VERIFICATION_SECTION.

Divergence from the order's premise, stated for the record: it said 2 of 3
example plans write `## Verification` as a fenced bash block. Ground truth
is 1 of 3 - REGENERATED.md has no section of its own at all - and the
example plan's block holds 6 command lines, not 5.

Red first: 4 of the 6 new tests failed before this change.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-18 02:02:14 +02:00
6cafb4c04c
fix(end-state): D-03 and D-04 measure behaviour, and the frozen denominator moves with them
The operator decided on 2026-09-18 that the two checks must stop measuring a
phrase and start measuring behaviour. The reason is the gate's own caveat: a
phrase probe closes on rewording, so "Phase 7 no longer says 'Skip for
trekplans'" and "the rubric no longer says 'exists and passes'" could both have
been satisfied by an edit that changed no behaviour at all. Both entries now
point at code a test exercises:

  D-03  lib/**/*.mjs exports runPlanVerification
  D-04  lib/**/*.mjs exports formatCriteriaEvidence

Verified open against the pre-fix tree (8d1669e) and closed against this one —
a check that cannot be seen to fire is not evidence.

What the probes can and cannot prove is now stated by the gate itself rather
than left to the reader. `PROBE_NOTES` carries one note per kind, the detail
line prints a note for every kind PRESENT in the row (and none for a kind no
entry declares), and the open/closed tags name the kind instead of labelling
`phrase` alone and leaving every other kind unlabelled. A behaviour probe
proves the capability exists; it does not prove a prose phase calls it — the
wiring is pinned by tests/lib/doc-consistency.test.mjs, and the gate says so.

THE DENOMINATOR MOVED ON PURPOSE. `tests/fixtures/end-state-frozen.json` and
the `FROZEN` literal in `tests/scripts/end-state-gate.test.mjs` carry new
signatures for D-03 and D-04, amended together and dated in both places. That
is the freeze mechanism working, not a bypass: the integrity check went
VIOLATED the moment the checks changed and stayed red until the amendment was
written down. The list may change — only as a decision said out loud. Nothing
else about the denominator moved: seven defect ids, three experiment ids, and
the agents/decisions/freeze configuration are unchanged, and no entry was
removed.

Gate after: defects 0 of 7, registry intact, still RED and exit 1 on the other
four rows — which is correct.

Suite: 1120 (1118/0/2), up 3.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-18 01:36:44 +02:00
c23b009738
fix(review): run the success-criteria commands and hand the reviewer the result (D-04)
The rubric required `brief-conformance-reviewer` to classify a Success
Criterion as Full only when "its verification command/test exists and passes".
Its tools are `Read`, `Glob`, `Grep`. It cannot run anything, so "passes" was
either guessed from the command's mere existence or quietly downgraded to
"exists" — a BLOCKER-tier rule key resting on an impression.

The reviewer stays read-only — a reviewer that executes the code it reviews is
not an independent reviewer. The command does the running instead:

- `/trekreview` Phase 4.5 runs the brief's `## Success Criteria` commands
  through `lib/verification/criteria-runner.mjs --brief --evidence` and captures
  the block as `sc_evidence_block`, pasted verbatim into the reviewer prompt in
  Phase 5. The exit code does not stop the review — a failing criterion is
  exactly what the review exists to find.
- `formatCriteriaEvidence` builds that block in code: one row per criterion with
  the command, the exit code and the first output line. Chose a code-built block
  over an orchestrator-written summary so the orchestrator cannot narrate a pass
  that never happened.
- The rubric now judges the supplied result: `PASS` supports Full, `FAILED` /
  `BLOCKED` is `Broken` with the exit code cited, and `NOT RUN` is the absence
  of a measurement — never evidence in either direction.
- Phase 4.5 is skipped in `quick` mode: that mode does not launch the
  conformance reviewer, so there is nobody to hand the result to.

Red first: seven tests in `tests/lib/criteria-runner.test.mjs` against a
committed brief fixture whose three criteria pass, fail, and are prose-only.
The two doc pins were verified red against the pre-fix files (rubric asked
"exists and passes"; no Phase 4.5; the block reached nobody).

Suite: 1117 (1115/0/2), up 9.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-18 01:34:18 +02:00
e55ca9fc89
fix(execute): run the plan's Verification on the single-session path (D-03)
A trekplan's `## Verification` section is where the brief's success criteria
land. Phase 7 opened with "**Skip for trekplans.**", and only the multi-session
wave path (Phase 2.6 Step 3) ran master verification. A plan executed in ONE
session therefore reported `completed` without ever running the criteria it was
measured against — the executor's own belief was the only evidence.

The check now exists as code, not as an instruction:

- `lib/verification/criteria-runner.mjs` parses the criteria an artifact
  DECLARES (a plan's `## Verification`, a brief's `## Success Criteria`), runs
  each command, and returns a verdict built from exit codes. Fail-closed
  throughout: a placeholder, a prose-only criterion, or an unavailable screen
  is `unrunnable`/`blocked`, never `passed`. A plan with no `## Verification`
  section exits 1 — a plan that promises no end-to-end check cannot be reported
  as verified.
- Every command is screened through the plugin's own PreToolUse denylist
  (`hooks/scripts/pre-bash-executor.mjs`) before it reaches a shell. Chose
  invoking that hook over its documented stdin protocol rather than copying its
  rules, because a command spawned from node never passes through the Bash tool
  and so the hook cannot fire by itself — this keeps exactly one denylist.
- Phase 7 is now "Exit / verification check": session specs run the exit
  condition, trekplans run the criteria runner. Phase 4's entry-condition skip
  for trekplans stands — a plan carries no entry condition; the exit side is
  not symmetrical.
- A failing criterion FELLS the run: `plan_verification.status != "passed"`
  forbids `result: completed`. That is clause 2 of the stop-signal contract,
  now enforced on the single-session path too.

Red first: `tests/lib/criteria-runner.test.mjs` (26 tests) against two committed
fixture plans, one of which declares a criterion that fails on purpose. The
doc pin in `tests/lib/doc-consistency.test.mjs` guards the wiring — a capability
no phase calls is the same defect wearing a lib/ file; verified red against the
pre-fix Phase 7 (skip present, runner absent, no fell-the-run clause).

Suite: 1108 (1106/0/2), up 27 from 1081.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-18 01:30:57 +02:00
8d1669ef51 docs(t1): record the operator's decline of the section 5 head-to-head
The delegated-orchestration bake-off (T1 section 5) was specified in June and never run, and
nothing said whether it should be. The operator declined it on 2026-09-18, so section 5 now
carries a STATUS block in the same form section 6 got when its PoC was run and declined.

Why it is a decline and not a postponement: the measurement's positive branch is "adopt
delegated orchestration", which the balance analysis lists under Explicitly NOT recommended
(CC-26 lean-NO). Running it would have reopened a settled decision without saying so. The
end-state direction chosen 2026-09-17 is to finish and freeze the present form. The cheaper
synthetic instrument proposed alongside it is not run either.

Nothing is rewritten: the design stays as the record of what was specified, and reviving it
is a new operator decision. This is the same mechanism the end-state gate measures, so the
experiment closes by the decision being written down rather than by code changing.

End-state gate E-03: open -> closed. Experiments 3 -> 2 of 3 (STORM and the deep-research
opt-in remain). Registry integrity intact. Suite 1081 (1079/0/2), run on a clean export of
the index.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-18 00:07:42 +02:00
e1d4425053 fix(docs): no doc claims an opus orchestrator any more; research spawns use the resolved model
D-07 had been closed on CLAUDE.md alone. The same false claim lived on elsewhere:
- README.md's "Switch the planning model" said four commands default to opus.
- The same README section offered a sed recipe for `model: opus` frontmatter lines, which
  0 of 7 command files have.
- commands/trekresearch.md said "the orchestrator runs on Opus".
Since v5.9.0 no command pins model:, so the orchestrator follows the session model.

- README: the section now says how it actually works. You pick the orchestrator's model with
  the session model (`claude --model sonnet` or `/model`). Sub-agent models are resolved at
  the spawn sites: brief phase_signals, then profile phase_models, then the agents/*.md pins
  as fallback. `--profile economy` still moves the sub-agents to Sonnet.
- trekresearch Launch rules: "Use model: "opus" for all sub-agents" contradicted the same
  file's own three-layer resolution, which v5.9.0 wired into the spawn sites and the file
  documents in Phase 1 and Hard rules -> Cost. The rule now injects
  `phase_signal_result.model` with agent frontmatter as the fallback, and says the
  orchestrator follows the session model. Nothing changes under the default premium
  profile: it still resolves to opus. The engine=swarm note now names the resolved-model
  injection as the anchor, instead of the removed literal.
- Two doc-consistency pins were written first and failed before this change:
  - no opus-orchestrator claim in README/CLAUDE.md/trekresearch, and no `^model: opus$`
    recipe;
  - the Launch rules name `phase_signal_result.model` and carry no blanket opus rule.
- Not touched: the follow-up agent in the default-off STORM phase still says model: "opus".
  That is part of an experiment whose fate is an operator decision.

End-state gate D-07: open -> closed. Defects 3 -> 2 of 7 (D-03, D-04 remain, by design);
registry integrity intact. Suite 1081 (1079/0/2), run on a clean export of the index.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 17:00:30 +02:00
09feb415b3 fix(end-state): the gate can no longer be turned green by editing its own ledger
An independent adversarial review of the end-state gate reproduced a GREEN result, exit 0,
with the gate's 18 tests passing. Only the registry and the STATE markers were edited; every
other file stayed byte-identical. The judged party owned the denominator, and nothing
guarded it. This change closes that finding and three more, and reopens a defect the gate
had closed too early.

Registry integrity (the blocker):
- tests/fixtures/end-state-frozen.json freezes the denominator. It holds the 7 defect ids
  and 3 experiment ids, each with a sha256 signature of its check (canonical JSON, key
  order irrelevant), plus the exact agents / decisions / freeze configuration.
- The rule: an entry may close; it may never disappear or have its check changed. New
  entries are allowed. spawnSites may never include agents/.
- The test file pins the manifest literally, with a comment that the denominator was
  frozen on 2026-09-17 and that changing it is a decision. The gate verifies the registry
  against the manifest on every run.
- A violation makes the gate red and names what moved. A missing manifest makes integrity
  n/a, which is also red.

Dormant means "never spawned", not "never mentioned":
- An agent counts as spawned only when a command names it in a spawn instruction: a row of
  a table headed `Agent`, a line that starts with Launch/Spawn, or a **name** block followed
  by a Prompt: line.
- Prose, negations, HTML comments and fenced code never count. Block quotes cannot match
  either, because every form is anchored at the start of the line.
- Re-measured under this definition: still 1 of 20 (synthesis-agent). Per agent, every other
  spawnable agent has a spawn instruction in at least one command.

A fifth tally, feat-after-freeze:
- It counts commits after the `end-state-freeze` tag whose subject matches
  ^feat(\(|!|:).
- The row is n/a, and therefore red, when there is no tag yet, the root is not a git work
  tree, or the root is not the top of its work tree. It is never 0.

Honest output:
- Every registry entry now declares its probe kind (phrase or byte).
- Open phrase probes are labelled as such, and closed ids are listed. The output states that
  a closed phrase probe is evidence, not proof of behaviour. D-03 and D-04 stay open and
  labelled; how they are fixed is still to be decided.
- The decisions row now says it counts ticks, not verified decisions.

D-07 reopened:
- Its check now also covers README.md and commands/trekresearch.md, where the "orchestrator
  runs on opus" claim still lives. README even ships a sed recipe for `model: opus` lines no
  command has.
- Defects are 3 of 7 until the separate docs fix lands.

Three surviving review mutants were killed with tests:
- an empty glob over an existing dir is silent;
- the header counts n/a rows as zero;
- a dormant-row error reads as 0.

Measured on a clean export of the index:
- Mutation harness, adapted from the review's: review code mutants 18 of 18 killed. Four of
  them were re-targeted at the equivalent new code. M02 survived until a test with an
  intact registry plus a missing freeze tag was added.
- New-logic mutants: 12 of 12 killed. One equivalent mutant (block-quote stripping) was
  removed together with the dead clause it targeted.
- Registry attacks: 7 of 7 killed.
- The review's combined green attack (registry + STATE only) is now RED, exit 1:
  "defects: D-03 is frozen but missing from the registry".
- Gate tests 38/38. Suite 1059 -> 1079 (1077/0/2).
- Gate: RED, defects 3 of 7, experiments 3 of 3, dormant 1 of 20; decisions and
  feat-after-freeze are n/a in a clean export.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 16:57:57 +02:00
e43153c3c4 fix(claude-md): the command table says what is true - orchestrators follow the session model
Since v5.9.0 no command in commands/ pins `model:`, so each /trek* orchestrator runs on
the session model. CLAUDE.md's command table still gave "opus" for all seven commands,
which is wrong in an always-loaded file. The column is now "Orchestrator model" with the
value `session`, and a one-line note under the table explains it and points to Model &
effort for sub-agent models. README already said this correctly; it is unchanged.

End-state gate D-07: open -> closed (defects 3 -> 2 of 7; D-03 and D-04 remain open by
design). Suite 1059 (1057/0/2), run on a clean export of the index.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 15:37:12 +02:00
f1c2c9eb4a fix(review): write the gold-scorer key separator as an escape, not a raw NUL byte
lib/review/gold-scorer.mjs held its (file, rule_key) separator as a literal NUL byte
inside a string literal. Git's binary heuristic therefore classed the whole file as
binary. Every diff of the scorer read "Binary files differ", `--numstat` printed "-  -",
and plain grep matched nothing in it. The separator is now written as the escape
sequence '\x00', which is the same one-character string at runtime.

Proof that the change is behaviour-neutral:
- A snapshot script recorded every number the scorer produces, before and after:
  - the committed run-perfect fixture through the coordinator contract;
  - empty, half, spurious (keys containing spaces and colons), duplicated and empty-gold
    runs;
  - all 9 verdict pairs.
  `diff` of the two outputs is empty (141 lines each).
- tests/lib/gold-scorer.test.mjs + tests/lib/gold-eval.test.mjs: 13/13 before and after.
- `git diff --no-index --numstat /dev/null <file>`: the new file reads "89 0" (text); the
  old blob, as a control, reads "- -" (binary).

End-state gate D-06: open -> closed (defects 4 -> 3 of 7). Suite 1059 (1057/0/2), run
on a clean export of the index.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 15:36:18 +02:00
529d2db852 fix(trekplan): drop TaskCreate/TaskUpdate from the tool lists - measured dead weight
Claude Code 2.1.233 stopped offering the task/todo tools on Opus 4.8, Sonnet 5, Fable 5
and newer unless CLAUDE_CODE_ENABLE_TODO_TOOLS=1 is set. /trekplan still listed
TaskCreate and TaskUpdate in allowed-tools. So did the two orchestrator reference docs,
which mirror the command but are never spawned. No prose in any command calls either tool.

Runtime effect, measured BEFORE removal on CC 2.1.274, claude-opus-5, with
CLAUDE_CODE_ENABLE_TODO_TOOLS unset:
- Probe: a throwaway project command whose frontmatter is
  `allowed-tools: Read, TaskCreate, TaskUpdate`, run headless
  (`claude -p --output-format stream-json --verbose`).
- The session had 151 tools. The only Task* names among them were Task, TaskOutput and
  TaskStop; neither TaskCreate nor TaskUpdate was there.
- The command loaded (listed in slash_commands) and completed with subtype=success,
  is_error=false and 0 tool calls. The model reported the tool as not available.
- `claude plugin validate` does not flag the unknown names either: only the one
  accepted warning appears, before and after.
Verdict: harmless dead weight, no error anywhere, so this is a correctness-of-docs fix
rather than a runtime fix.

End-state gate D-05: open -> closed (defects 5 -> 4 of 7). Suite 1059 (1057/0/2), run
on a clean export of the index.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 15:34:50 +02:00
03e443f667 fix(trekresearch): the swarm engine points only at the External agents block
The engine = swarm instruction told the session to run the `### External agents` and
`### Bridge agent` blocks. The Bridge agent heading was removed in v5.10.1 with the
second-opinion bridge, so half of the instruction pointed at nothing. It now names the
one block that exists (`### External agents`, still present). Nothing replaces the
removed bridge pass.

End-state gate D-02: open -> closed (defects 6 -> 5 of 7). Suite 1059 (1057/0/2), run
on a clean export of the index.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 15:33:08 +02:00