Commit graph

45 commits

Author SHA1 Message Date
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
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
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
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
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
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
f19474acc6 fix(storm-measure): check BOTH halves of the activation SC, not just the count delta
The SC asks two things of an effort: high run: that it discovered at least
one dimension AND that the dimension list in the output brief is a TRUE
SUPERSET of the interview-derived ones. activationCheck computed
dimensions - dimensions_baseline >= 1 and returned ok on that alone. A
count delta says nothing about membership: a run that dropped two
interview dimensions and appended three discovered ones is +1 and passed
the check while violating the second half outright. Supersetness was
asserted only by Phase 4.5's prose contract that discovery appends -
nothing read it.

The stats record cannot carry the dimension names that would show it
directly: names are free prose, and lib/exporters/field-allowlist.mjs
denies prose by omission (question, project_dir, brief_path are already
excluded for that reason). So the run attests membership with
dimensions_baseline_preserved, a boolean set in a new Phase 4.5 step 5,
and the gate refuses to call activation OK without it. An ABSENT
attestation fails rather than falling back to the old count-only rule -
otherwise legacy rows would keep passing on the defect.

Producer side wired end to end: the record format, the measurement-fields
prose (five fields -> six), the exporter allowlist, the jsonl-schemas
fixture row, and the --activation-check comment in
docs/storm-measurement.md.

Review finding d2786604458207a5a73478cdcb6a54bbdb92141d (MINOR).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LuGhWAbWyRFBFeemfhxoVv
2026-08-12 23:01:59 +02:00
f3874946ad feat(trekresearch): emit and export the five STORM measurement fields 2026-08-09 15:01:15 +02:00
9a38500a63 fix(exporters): allowlist trekresearch engine field and pin schema fixture agreement
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-09 13:04:47 +02:00
8b7a849a76 test(validators): cover fable accept + unknown-model reject in both gate layers 2026-07-02 16:56:26 +02:00
581489a513 test(trekresearch): pin deep-research adapter output contract 2026-06-30 13:38:09 +02:00
440594f1b2 feat(eval): SKAL-1·4b offline gold-scored output eval
Scores committed agent-run fixtures against the golden corpus at
(file, rule_key) granularity, building on the deterministic coordinator
contract (4a). Offline: committed reviewer payloads, no live agent spawn,
no LLM, no network (the LLM-in-the-loop grading is the separate 4c tier).

- lib/review/gold-scorer.mjs: scoreFindings (precision/recall/f1 at
  (file,rule_key) granularity, line+severity ignored) + scoreVerdict; pure,
  with documented vacuous-set conventions.
- tests/fixtures/bakeoff-rich/runs/run-perfect.json: committed run that
  reproduces all 5 seeded gold findings through runContract.
- tests/lib/gold-eval.test.mjs: the scoring RUN (precision/recall/f1 = 1.0,
  verdict == expected_verdict BLOCK, nothing suppressed/skipped).
- lib/util/test-census.mjs: third census category (goldEval) — a scoring run
  is neither behavior coverage nor a doc-pin; honest-count invariant now 3-way.
- docs/eval-corpus/README.md: 4b moved from Future hardening to implemented.

Suite 809 -> 822 (820/0/2). gold-scorer covers TP+FP+FN+degenerate paths.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BJQYC5vpkJWxndS55vQQZ6
2026-06-30 09:00:33 +02:00
7e78d076f9 docs(observability): document token-usage schema + main-context v1 scope 2026-06-26 14:47:24 +02:00
708ba04571 feat(stats): add pure token-usage parser + cache-aware cost derivation 2026-06-26 14:32:10 +02:00
e374a7c0ff feat(eval): add gold.json golden corpus + loader/validator test 2026-06-26 11:51:46 +02:00
6b30483304 feat(voyage): S12 — NW3 synthesis-agent built + measured → declined per measurement [skip-docs]
NW3 (CC-26 §6 PoC): delegate trekplan Phase 7 synthesis to a synthesis-agent,
adopt only if Δ main-context ≥30% with no quality loss. Operator chose the
deterministic-proof path (live ≥3-run bake-off was env-blocked: no API key;
installed plugin is a cache copy so a new agent is invisible to `claude -p`).

Decisive structural finding: trekplan Phase 5 runs the swarm FOREGROUND, so its
outputs are already resident in main before Phase 7. Delegating only Phase 7
evicts nothing → Δ_faithful = 0% (BASE-independent). The ≥30% saving needs an
out-of-scope Phase-5 redesign (swarm-writes-to-disk / nested orchestrator).
VERDICT: DECLINED per measurement.

- agents/synthesis-agent.md — dormant, schema-conformant deliverable (NOT wired)
- lib/plan/synthesis-digest-schema.mjs — digest output contract (+ tests)
- scripts/synthesis-measure.mjs — deterministic Δ-accounting core (+ tests)
- tests/fixtures/synthesis/ — 7 exploration outputs + representative digest
- docs/T1-synthesis-poc-results.md — measurement + verdict (reproducible)
- CLAUDE.md — agent table row (doc-consistency: 24 agents)

Tests 670 → 695 (693 pass / 2 skip / 0 fail). `claude plugin validate` clean
(only the pre-existing root-CLAUDE.md warning). commands/trekplan.md untouched.

[skip-docs] rationale: no user-facing feature ships (NW3 declined; agent dormant
and unwired). The substantive doc is docs/T1-synthesis-poc-results.md; the
README/CHANGELOG roll-up for NW1–NW3 is the S13 coordinated release per
docs/W1-narrow-wins-plan.md §S13.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LqBYc8Ltrk7LipyJmGxXiB
2026-06-18 17:58:39 +02:00
f7c8aa45ab feat(voyage): S10 part B — NW2 full bake-off (rich fixture) → verdict POSITIVE
Run the full T2 §5 prose-vs-Workflow /trekreview bake-off (operator GO,
choice "a"): 3 runs/arm on a rich-finding JWT-auth fixture, resolving the
smoke's 0-finding limitation.

Deliverables:
- tests/fixtures/bakeoff-rich/ — JWT-auth brief + diff with 5 seeded blatant,
  brief-traceable issues (varied severity/rule_key, one dual-flaggable).
- scripts/bakeoff-armA-merge.mjs — Arm A (prose) validate (NW1) + triplet-dedup,
  matching Arm B's dedup exactly.
- scripts/bakeoff-fidelity.mjs — cross-arm + within-arm + granularity-ladder
  fidelity analysis over the structured arm outputs.
- docs/T2-bakeoff-results.md §Full run — the T2 §5 verdict.

Result (3 runs/arm, both arms ran the coordinator):
- Verdict fidelity EQUIVALENT — all 6 runs BLOCK, cross-arm verdict-match 1.0.
- Finding-set: substrate is fidelity-neutral. Cross-arm jaccard 0.41 (triplet)
  → 0.71 (file,rule_key) → 1.0 (file); cross-arm ≈ within-arm at every
  granularity. Issue coverage 5/5 in 6/6 runs. Low triplet jaccard is
  line-citation noise shared by both arms, not a substrate effect.
- Token +4.4% (Arm B vs A; <=+15%). Classifier interference 0 at 9-agent
  concurrency. JSON-robustness: Arm B schema-forced; Arm A 6/6 valid via NW1.
- VERDICT POSITIVE → S11 proceeds with opt-in --workflow flag.

Caveat (per plan posture): strict triplet-jaccard>=0.7 flag is 0/9, a
metric-calibration artifact (both arms sub-0.7 against themselves), not a
regression. Residual: F4 auto/bypass explicit-mode check (mode not settable
in-session).

Suite green (662/660 pass, 2 skip); plugin validate clean (modulo the
pre-existing root-CLAUDE.md warning). No production code changed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LqBYc8Ltrk7LipyJmGxXiB
2026-06-18 15:44:24 +02:00
869bf318d2 feat(voyage): S10 — NW2 part A (Workflow port + fidelity harness + smoke)
Build the prose-vs-Workflow bake-off machinery for /trekreview Phase 5-6 and
run a 1-run/arm smoke to de-risk before the full measurement (operator posture:
build + smoke, then pause for go/no-go on the full >=3-runs/arm run).

New:
- lib/review/fidelity-diff.mjs (+ tests) — the PRIMARY metric: parse two
  review.md (or two structured arm outputs) and compare verdict + jaccard over
  (file,line,rule_key)-IDs + per-finding severity/rule_key. Reuses jaccard +
  frontmatter + NW1 findings-schema + finding-id. fidelityDiffStructured avoids
  rendering review.md per run.
- scripts/trekreview-armB.workflow.mjs — Arm B: Phase 5-6 as a Workflow
  (parallel([conformance, correctness]) schema-forced -> JS dedup-by-triplet ->
  agent(review-coordinator) verdict schema). Path-based input via args (reviewers
  carry Read). Inlines dedup + the 12-key rule_key enum (scripts have no imports).
- tests/fixtures/bakeoff/ — committable fixture: real diff of b149538 (NW1) +
  brief reconstructed from plan S9. Both arms review the same pinned input.
- docs/T2-bakeoff-results.md — smoke results + verdict + go/no-go recommendation.

Smoke result: SMOKE PASS. Arm B runs the full pipeline (3 agents) with ZERO
classifier interference; fidelity EQUIVALENT to Arm A at the verdict level
(both ALLOW; jaccard 1.0). Caveat: the clean TDD'd fixture yielded ~0 findings,
so finding-SET fidelity was not stressed (only verdict fidelity proven). A
reviewer-level divergence appeared (Arm B raised 1 raw finding, coordinator
filtered it; Arm A raised 0) — to be quantified in the full run on a
richer-finding-surface fixture. NOT the T2 §5 POSITIVE/NEGATIVE verdict.

Suite 647 -> 662 (660 pass / 2 skip / 0 fail; +15 fidelity-diff). claude plugin
validate clean (known root-CLAUDE.md warning only). Plan: docs/W1-narrow-wins-plan.md S10.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LqBYc8Ltrk7LipyJmGxXiB
2026-06-18 14:40:19 +02:00
b5ad71d92c test(voyage): add 4 brief fixtures for v5.1.1 runtime scenarios 2026-05-14 21:34:51 +02:00
b43ad2e6fa feat(voyage): bump trekbrief-template to brief_version 2.1 + add phase_signals fixtures 2026-05-13 21:09:57 +02:00
3683ae0070 chore(voyage): release v5.0.0 — remove bespoke playground + /trekrevise + Handover 8; render produced artifacts to HTML + link, annotate via /playground
The v4.2/v4.3 bespoke playground SPA (~388 KB), the /trekrevise command,
Handover 8 (annotation → revision), the supporting lib/ modules
(anchor-parser, annotation-digest, markdown-write, revision-guard), the
Playwright e2e suite, and the @playwright/test / @axe-core/playwright
devDeps are removed. A browser walkthrough found the playground borderline
unusable, and it duplicated the official /playground plugin's
document-critique / diff-review templates.

In their place: scripts/render-artifact.mjs — a small, zero-dependency
renderer that turns a brief/plan/review .md into a self-contained,
design-system-styled, zero-network .html (frontmatter folded into a
<details> block). /trekbrief, /trekplan, and /trekreview call it on their
last step and print the file:// link; to annotate, run /playground
(document-critique) on the .md and paste the generated prompt back.

Resolves the v4.3.1-deferred findings as moot (their target files are
deleted). npm test green: 509 tests, 507 pass, 0 fail, 2 skipped.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-12 14:05:07 +02:00
f6a758a3e2 fix(voyage): inline screenshot gallery loads docs/screenshots/ PNGs (31d28f65) 2026-05-10 21:25:01 +02:00
37804c8d58 test(voyage): add Group B (structure) + Group C (annotation export schema) tests [skip-docs]
Step 29 of v4.3 plan — Wave 7:
- Group B (9 tests): DS-token classes (badge--scope-voyage, guide-panel,
  fleet-grid), theme-toggle wiring (data-action, wireThemeToggle,
  localStorage), sidebar-tab keyboard pattern (role=tablist,
  aria-selected, J/K/Esc), anchor-ID format mirror.
- Group C (7 tests, +1 vs original): export-bundle JSON parse, required
  keys, per-annotation field validation, empty-export edge case,
  annotation_digest order-independence, SHA-256 16-hex-char validity
  (SC6 / SC-GAP-3), fixture plan anchor format.
- Fixtures: tests/fixtures/playground/v43-export-bundle.json +
  v43-plan-pre-annotate.md (ANN-0001 + ANN-0002, revision: 0).

Test count: 689 → 705 pass / 0 fail / 2 skipped.
2026-05-10 18:18:24 +02:00
4562614513 test(voyage): add annotation roundtrip + rollback + source_annotations integration — v4.2 Step 7
Implements SC2/SC3/SC5b/SC7 + additive-field invariant for the v4.2
annotation pipeline:

Fixtures (tests/fixtures/annotation/):
  - annotation-brief.md           — brief-validator-clean fixture
  - annotation-plan.md            — plan-validator-clean (2 steps)
  - annotation-review.md          — review-validator-clean
  - annotation-plan-large.md      — 51 steps (SC3 scale fixture)

Integration tests:
  - tests/integration/annotation-roundtrip.test.mjs — 7 cases:
    SC2 byte-identical empty round-trip across brief/plan/review,
    SC3 scale (51 steps + 100 anchors) round-trip,
    SC7 parseAnchors(stripAnchors(addAnchors(...))) === [] per target.
  - tests/integration/schema-rollback.test.mjs — 4 cases:
    SC5b validator-FAIL -> revisionGuard rolls back byte-identical
    (sha256 invariant) for brief/plan/review + cross-target sweep.
    .local.bak deleted on rollback path (validator-PASS path tested
    in lib/util/revision-guard tests).
  - tests/lib/source-annotations.test.mjs — 6 cases mirroring
    tests/lib/source-findings.test.mjs additive-field pattern: each
    validator (brief/plan/review) accepts source_annotations as
    additive-optional, parser extracts as array of dicts, entries
    conform to documented shape, baseline forward-compat (artifacts
    without source_annotations still validate).

Verify: node --test tests/integration/annotation-roundtrip.test.mjs
       tests/integration/schema-rollback.test.mjs
       tests/lib/source-annotations.test.mjs -> 17 pass / 0 fail.
Full npm test: 577 pass / 0 fail / 2 skipped (Docker).

Refs plan.md Step 7 + plan-critic M4 + plan-critic B4.
2026-05-09 15:13:27 +02:00
d72536b81d feat(voyage): add anchor-parser.mjs with placement validation — v4.2 Step 3
lib/parsers/anchor-parser.mjs (~190 LoC):
- parseAnchors(md) -> Anchor[] (id, target, line, snippet?, intent?)
- addAnchors(md, anchors) -> md_with_anchors
- stripAnchors(md_with_anchors) -> md (byte-identical)
- validateAnchorPlacement(md, anchors) -> errors for list-item / fenced-block / indent

Format: <!-- voyage:anchor id="ANN-NNNN" target="<slug>" line="<N>" -->
Block-level only, on its own line (col 0), blank-line separation.

Test fixture annotation-example.md with single ANN-0001 anchor — referenced by SC12 quickstart.
14 tests pass (parseAnchors, addAnchors, stripAnchors, validateAnchorPlacement).
2026-05-09 12:52:46 +02:00
a1f256d000 feat(voyage): emit MANIFEST_PROFILE_DRIFT warning in plan-validator strict mode — brief assumption 7
Step 20 of v4.1 — implements drift detection in plan-validator.mjs per
brief Assumptions block 7: "Mismatch (e.g. korrupt manuell endring)
emitterer MANIFEST_PROFILE_DRIFT-warning fra plan-validator i --strict-modus."

Logic (after validateAllManifests in validatePlanContent):
  1. Strict-mode only — soft mode never emits drift warnings.
  2. Plan frontmatter must declare 'profile: <name>' to establish baseline.
  3. For each step manifest, if profile_used is set AND differs from plan
     profile, emit warning (NOT error) with code MANIFEST_PROFILE_DRIFT
     and location 'step N: profile_used = X, plan profile = Y'.

Forward-compat preserved: drift is a warning, plan remains valid:true.
Operators see the drift in --strict mode without parsing breaking.

New files:
  tests/validators/plan-validator-profile-drift.test.mjs — 4 tests
  tests/fixtures/plan-profile-drift.md                   — drift fixture

Tests verify:
  1. drift detected in strict mode → MANIFEST_PROFILE_DRIFT in warnings
  2. drift NOT detected in soft mode → strict gate honored
  3. matching profile → no drift warning
  4. no plan-level profile → drift detection silent (no baseline)

Tests: 479 pass + 2 skipped (Docker not installed).
2026-05-09 10:02:53 +02:00
96ac93cc3a feat(voyage): add 5 additive profile fields to JSONL stats — SC #11
Step 8 av v4.1-execute (Wave 3, Session 4).

5 nye additive felter er nå dokumentert i hver kommandos prose-stats-blokk
(via Profile-seksjonen fra Step 7 — felles overflate per kommando):
- profile           — string ('economy' | 'balanced' | 'premium' | <custom>)
- phase_models      — object form {brief: 'sonnet', ..., continue: 'opus'}
- parallel_agents   — number (snapshot av maksverdi som faktisk ble brukt)
- external_research_enabled — boolean
- profile_source    — 'flag' | 'env' | 'default' | 'inheritance'

Patcher trekresearch.md med eksplisitt profile_source-mention + alle 5 felter
(de andre 5 commands hadde dette allerede via Step 7 Profile-seksjon).

SC #11 contract-test design (per brief):
(a) Fixture-records valideres som JSONL-contracts → tests/fixtures/stats-with-profile.jsonl
    (5 simulerte stats-rader, én per kommando-overflate)
(b) Command-prose contains field-names → kompenserer for plan-critic Major 4
    false-confidence (faktisk runtime-emission er LLM-prose-driven, ikke
    testbart i node:test alene).

Tester (12 nye, baseline 445 → 457):
- Fixture parses som JSONL (5 records)
- Hver record har profile + profile_source
- profile_source-verdier i {flag, env, default, inheritance}
- Fikstur dekker alle 4 profile_source-verdier
- 6 commands × prose contains profile + profile_source
- trekplan.md prose contains phase_models + parallel_agents
- trekresearch.md prose contains external_research_enabled

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-09 09:40:21 +02:00
4e6b026104 feat(voyage): add lib/exporters/textfile-format.mjs — Prometheus text-format pure transform SC #12
Step 9 av v4.1-execute (Wave 2, Session 3).

Pure function transformToPrometheus(records) → Prometheus text-format 0.0.4.

Hard rules:
- NO client-side timestamps (research/01 node_exporter#1284 mitigation)
- Allowlist-redacted records ONLY (caller responsibility — Step 11 enforces)
- UTF-8 metric names normalized: lowercase, [.\\-\\s] → _, voyage_ prefix
- Empty input → empty string output
- Sorted output for determinism (snapshot-test-friendly)

Heuristic metric typing:
- counter: *_total, *_count, *_passed, *_failed, *_skipped
- histogram: *_ms, *_duration, *_p\\d+, *_seconds
- gauge: everything else (Prometheus convention)

Snapshot: tests/fixtures/expected.prom byte-for-byte match.
Regenerate: node scripts/gen-expected-prom.mjs > tests/fixtures/expected.prom

Tester (6 nye, baseline 400 → 406):
- Snapshot byte-for-byte match (SC #12)
- Empty input handling (null, undefined, [])
- Allowlist-redaction sanity (post-bash-stats uten command_excerpt)
- NO client-side timestamps (token-count-assertion per linje)
- normalizeMetricName edge-cases
- Determinism (identisk input → identisk output)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-09 09:30:58 +02:00
f8fc0bb1c2 feat(voyage): add lib/profiles/resolver.mjs — locked interface SC #5-#9
Step 6 av v4.1-execute (Wave 2, Session 2).

Implementer locked interface contract fra brief Preferences:

- loadProfile(name, opts) → ProfileObject
  Leser lib/profiles/<name>.yaml (built-in) eller custom fra
  <cwd>/voyage-profiles/ > ~/.claude/voyage-profiles/. Throws Error med
  cause: PROFILE_NOT_FOUND. Returnerer parsed object med phase_models
  flattened til {brief: 'sonnet', research: 'opus', ...} (object form
  for downstream JSON-stats).

- resolveProfile(argv, env) → {profile, profile_source}
  Ordre: --profile flag > VOYAGE_PROFILE env > 'premium' default.

- resolveTrekcontinueProfile(planPath, argv, opts) → {profile, profile_source}
  --profile flag wins ('flag'); ellers leser plan.md frontmatter
  ('inheritance'); v4.0-stil plan uten profile-felt → 'default' premium
  (backward-compat). Flag overstyrer arv → console.error advisory.

- validateProfileFile(path) → Result
  Tynn re-eksport av validateProfile fra profile-validator.mjs.

- findProfilePath(name, opts) → {path, attempted}
  Lookup-helper. attempted-array brukes i error-melding for HIGH-risk-
  mitigering (ENOENT-diagnose).

Tester (13 nye, baseline 387 → 400):
- SC #5 x4 (loadProfile economy/balanced/premium + PROFILE_NOT_FOUND)
- SC #6 (flag > env > default ordre)
- SC #7 (performance: 1000-iter < 50ms gjennomsnitt; faktisk ~0.055ms)
- SC #8 x2 (cwd > home precedence + error-msg attempted-paths)
- SC #9 x2 (inheritance + flag-override-advisory)
- Backward-compat x2 (v4.0 plan + non-existent plan)
- validateProfileFile re-export sanity

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-09 09:29:01 +02:00
b56ba13b04 feat(voyage): add lib/validators/profile-validator.mjs — SC #1, #2, #3
Step 5 av v4.1-execute (Wave 2, Session 2).

Profile-validator etter brief-validator-mønsteret eksakt: validateProfileContent
(pure), validateProfile (file-reader), CLI shim med --json flag. Eksporter
PROFILE_REQUIRED_FIELDS (frozen), PROFILE_REQUIRED_PHASES (frozen).

Validerer:
- Required frontmatter fields (name, phase_models, parallel_agents_min/max,
  external_research_enabled, brief_reviewer_iter_cap)
- phase_models = list-of-dicts med phase + model
- 6 required phases (brief, research, plan, execute, review, continue)
- parallel_agents_max ≥ parallel_agents_min
- Allowed model values: ['sonnet', 'opus']; haiku tillatt KUN ved
  VOYAGE_ALLOW_HAIKU=1 (per global CLAUDE.md modellvalg-prinsipp)

Issue codes: PROFILE_MISSING_FIELD, PROFILE_INVALID_MODEL, PROFILE_INVALID_ENUM,
PROFILE_READ_ERROR, PROFILE_NOT_FOUND.

Field-path-reporting i error-location: phase_models[N].model for SC #2.

Tester (10 nye, baseline 377 → 387):
- SC #1 x3 (innebygde profiler grønne)
- SC #2 (PROFILE_INVALID_MODEL med location phase_models[2].model)
- SC #3 (PROFILE_INVALID_ENUM for external_research_enabled: "yes" string)
- VOYAGE_ALLOW_HAIKU env-var deny/allow
- PROFILE_MISSING_FIELD når name fraværende
- PROFILE_NOT_FOUND for ikke-eksisterende fil
- 2 export drift-pins

Fixturer: profile-invalid-model.yaml (gpt-4 i phase_models[2]),
profile-invalid-enum.yaml (external_research_enabled som string "yes").

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-09 09:26:23 +02:00
43a3e4f11d docs(voyage): jsonl schema audit — field-allowlist input for v4.1 otel exporter
Step 1 av v4.1-execute (Wave 1, Session 1).

Audit alle 6 trek*-stats.jsonl-skjemaer + lib/stats/event-emit.mjs autonomy
events + hooks/scripts/post-bash-stats.mjs PostToolUse Bash records. Produser
markdown-tabell {schema_id, fields[], writer_path, line_ref, v4.1 additive,
PII} som load-bearing input til Step 11 (field-allowlist) og Step 8 (stats
plumbing).

Spesielle merker:
- command_excerpt fra post-bash-stats.mjs flagget CWE-212 (improper cross-
  boundary removal of sensitive data) — eksporten MÅ hard-ekskludere uten
  eksplisitt VOYAGE_EXPORT_INCLUDE_COMMAND_EXCERPT=1 (deferred til v4.2)
- v4.1 additive fields enumerert per skjema: profile, phase_models,
  parallel_agents, external_research_enabled, profile_source
- EXPORT_ALLOWLIST + EXPORT_DENYLIST utdrag i bunnen som forhåndsdefinisjon
  av Step 11 inline static consts

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-09 09:20:54 +02:00
c909466c99 feat(voyage)!: bulk content rewrite ultra -> voyage/trek prose [skip-docs]
Sed-pipeline (16 patterns, longest-match-first) sweeper residuelle ultra*-treff
i prose, command-narrativ, agent-prompts, hook-kommentarer, doc-prosa.

Pipeline-utvidelser fra V4-prompten:
- BSD-syntax [[:<:]]ultra[[:>:]] istedenfor \bultra\b (BSD sed mangler \b)
- 6 compound-patterns for ultraplan/ultraexecute/ultraresearch/ultrabrief/
  ultrareview/ultracontinue uten -local-suffiks
- ultra*-stats glob -> trek*-stats glob
- Linje-eksklusjon redusert til ultra-cc-architect (Q8); session-state-
  eksklusjonen var over-protektiv
- File-eksklusjon utvidet til settings.json, package.json, plugin.json,
  hele .claude/-treet (gitignored + V5-territorium)

Q8-undantak holdt: architecture-discovery.mjs + project-discovery.mjs urort.
Filnavn-konvensjon holdt: .session-state.local.json + *.local.* preservert.

Manuell narrative-fix: tests/lib/agent-frontmatter.test.mjs linje 10
mangled "/ultra*-local" til "/voyage*-local" (ingen slik kommando finnes);
korrigert til "/trek*".

Residualer utenfor scope (V5 handterer): package.json + .claude-plugin/
plugin.json (Step 12-14 versjons-bump). .claude/* er gitignored
spec-historikk med tilsiktet BEFORE/AFTER-narrativ.

Part of voyage-rebrand session 3 (Wave 4 / Step 10).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-05 15:08:20 +02:00
12f4391dca feat(voyage)!: rename type discriminators across validators + fixtures [skip-docs]
- brief-validator: BRIEF_TYPE_VALUES ['ultrabrief','ultrareview'] -> ['trekbrief','trekreview'] + dependent branches
- research-validator: 'ultraresearch-brief' -> 'trekresearch-brief'
- review-validator: 'ultrareview' -> 'trekreview'
- 3 templates frontmatter type:
- 4 synthetic fixtures: ultraplan-synthetic/ultrareview-synthetic -> trek* (frontmatter only; bodies untouched, Jaccard floor preserved)
- 2 trekreview fixtures: type: trekreview
- 6 validator-test fixtures + asserts
- agents/review-coordinator.md frontmatter example

Atomic: validator + fixtures committed together — partial state would cause vacuous
test passes or hard validator rejection.

Part of voyage-rebrand session 2 (W3.3 / Step 5).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-05 14:40:25 +02:00
4f8d9c70e5 feat(voyage)!: rename commands, templates, fixtures for v4.0.0 [skip-docs] 2026-05-05 14:13:44 +02:00
Kjell Tore Guttormsen
9a285208d2 test(ultraplan-local): add session-state fixtures
Two fixtures for session-state-validator (Step 4):
- valid-in-progress.json — well-formed schema-v1 object
- malformed.json — truncated JSON for negative tests

Step 3 of /ultracontinue v3.3.0.
2026-05-01 20:21:50 +02:00
Kjell Tore Guttormsen
600bde6a41 test(ultraplan-local): add SC3(b) source_findings structural test
Synthetic plan.md fixture with source_findings: block-style YAML list of 3
40-char hex IDs in frontmatter, plus minimal plan structure (Title +
Implementation Plan + 1 Step + Manifest). 3 tests verify:

1. plan-validator accepts a plan with source_findings (additive optional field)
2. frontmatter parser extracts source_findings as array of strings
3. each ID matches the 40-char lowercase hex format from finding-id.mjs

Closes the SC3(b) gap flagged by adversarial review (scope-guardian Gap 2).
LLM-level behavior (planner emitting source_findings) remains non-testable
without live invocation; this covers the structural contract.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-01 17:23:30 +02:00
Kjell Tore Guttormsen
68a0e5aa3b test(ultraplan-local): add synthetic ultrareview determinism fixtures
review-run-A.md (5 findings) and review-run-B.md (6 findings, A ⊂ B) form a
known-Jaccard fixture pair: |A ∩ B| = 5, |A ∪ B| = 6, Jaccard = 5/6 = 0.833,
above the SC4 threshold of 0.70. IDs are real 40-char SHA1s computed via
lib/parsers/finding-id.mjs from realistic (file, line, rule_key) triplets.

Both fixtures pass review-validator --strict (frontmatter + body sections +
findings shape). Real-LLM determinism measurement deferred to v1.1.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-01 17:21:02 +02:00
Kjell Tore Guttormsen
b787213ba8 chore(release): ultraplan-local v3.1.0
Quality program release. Spor 0+1+2+3 all delivered.

- 109 zero-dep tests gate fork-readiness
- 5 validators wired into 4 commands as CLI shims
- HANDOVER-CONTRACTS.md: single source of truth for 5 pipeline handovers
- PreCompact-hook (P0) closes progress.json drift; --resume now works
- Semantic plan-critic catches paraphrased deferred decisions
- examples/01-add-verbose-flag/: hand-calibrated end-to-end pipeline demo
- 4 hooks total (pre-bash, pre-write, session-title, post-bash-stats, pre-compact-flush)
- SECURITY.md + Extending-the-plugin docs

CC v2.1.x feature adoption: F8 (MCP_CONNECTION_NONBLOCKING),
F9 (sessionTitle), F3 (duration_ms), F12 (disableSkillShellExecution).
F2 (hook 'if'-field) deferred — universal protection wins.

Pre-flight verification:
- npm test → 109 pass
- plan-validator --strict templates/plan-template.md → READY
- plan-validator --strict tests/fixtures/plan-fase-narrative.md → FAIL (expected)
- grep smallCodebase|mediumCodebase|largeCodebase → 0 hits

Version bumped: package.json, plugin.json, README badge, root README,
root CLAUDE.md.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-01 06:31:42 +02:00
Kjell Tore Guttormsen
3c2bda87db refactor(marketplace): split cc-architect from ultraplan-local into its own plugin
Extract `/ultra-cc-architect-local` and `/ultra-skill-author-local` plus all 7
supporting agents, the `cc-architect-catalog` skill (13 files), the
`ngram-overlap.mjs` IP-hygiene script, and the skill-factory test fixtures
from `ultraplan-local` v2.4.0 into a new `ultra-cc-architect` plugin v0.1.0.

Why: ultraplan-local had drifted into containing two distinct domains — a
universal planning pipeline (brief → research → plan → execute) and a
Claude-Code-specific architecture phase. Keeping them together forced users
to inherit an unfinished CC-feature catalog (~11 seeds) when they only
wanted the planning pipeline, and locked the catalog and the pipeline into
the same release cadence. The architect was already optional and decoupled
at the code level — only one filesystem touchpoint remained
(auto-discovery of `architecture/overview.md`), which already handles
absence gracefully.

Plugin manifests:
- ultraplan-local: 2.4.0 → 3.0.0 (description + keywords updated)
- ultra-cc-architect: new at 0.1.0 (pre-release; catalog is thin, Fase 2/3
  of skill-factory unbuilt, decision-layer empty, fallback list still
  needed)

What stays in ultraplan-local: brief/research/plan/execute commands, all
19 planning agents, security hooks, plan auto-discovery of
`architecture/overview.md` (filesystem-level contract, not code-level).

What moved (28 files via git mv, R100 — full history preserved):
- 2 commands, 8 agents, 1 skill catalog (13 files), 2 scripts, 8 fixtures

Documentation updates: plugin CLAUDE.md and README.md for both plugins,
root README.md (added ultra-cc-architect section, updated ultraplan-local
section), root CLAUDE.md (added ultra-cc-architect to repo-struktur),
marketplace.json (registered ultra-cc-architect), ultraplan-local
CHANGELOG.md (v3.0.0 entry with migration guidance).

Test verification: ngram-overlap.test.mjs passes 23/23 from new location.

Memory updated: feedback_no_architect_until_v3.md now points at the new
plugin and reframes the threshold around catalog maturity rather than an
ultraplan-local milestone.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-30 17:18:47 +02:00
Kjell Tore Guttormsen
d59dd84377 fix(ultraplan-local): v2.3.2 — skill-drafter slug-collision hint
skill-drafter now reads {catalog_root}/<slug>.md before writing its
draft and prepends a warning block to its confirmation output when
an existing skill would be overwritten during manual `mv` promotion.
The draft is still written to .drafts/<slug>.md — the check is a
hint, not a block.

Closes v2.3.0 dogfood finding (post_dogfood_findings[0]): the
drafter produced .drafts/hooks-pattern.md when an approved
hooks-pattern.md seed already existed, giving no signal that `mv`
during promotion would silently overwrite the seed. v2.3.1
introduced the qualified-slug mechanism to resolve such collisions;
v2.3.2 surfaces them at the right moment — before promotion.

Changes:
- agents/skill-drafter.md — new Step 2 between slug computation and
  source reading. Reads {catalog_root}/<slug>.md, inspects
  review_status, derives a kebab-case qualifier from the concept
  handle (or source basename fallback). Subsequent steps renumbered
  3→7. Output format gains Collision: field and optional warning
  block. New Hard Rule.
- tests/fixtures/skill-drafter/slug-collision-expected.md — reference
  fixture documenting expected confirmation shape across four
  scenarios (no collision, approved collision, soft pending
  collision, collision with no good qualifier). Skill-drafter is
  prompt-driven; fixture anchors shape for human verification and
  downstream parsers.
- CHANGELOG [2.3.2], plugin.json 2.3.1→2.3.2, README badge, plugin
  CLAUDE.md slug-convention Collision-hint bullet, marketplace root
  README summary, marketplace root CLAUDE.md plugin table.

Non-breaking. No frontmatter/drafts-layout/tool-scope/regex changes.
Existing pipelines see one extra field and an optional warning —
both purely additive.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-18 18:06:37 +02:00
Kjell Tore Guttormsen
ce04869d12 test(ultraplan-local): add skill-factory calibration fixtures
3 source/draft pairs that pin n-gram-overlap verdicts to representative prose:
- accepted (containment 0.014 / longestRun 3)
- needs-review (containment 0.211 / longestRun 12)
- rejected (containment 0.676 / longestRun 74)

Topics: session-start hooks, subagent delegation, output styles — proximate to
the production source material the skill-factory will ingest.

Plan: .claude/projects/2026-04-18-skill-factory-fase-1-mvp/plan.md (step 5)
2026-04-18 15:16:28 +02:00