Commit graph

8 commits

Author SHA1 Message Date
20cdc22803 fix(review): an anonymous invalid payload is unattributable, not a reviewer named "unnamed reviewer"
Follow-up defect in the reviewer accounting added by e2aec01, found by review
and confirmed by probe before fixing.

validateFindings only WARNS on a missing `reviewer` field, so a payload can
fail schema while carrying no name. ingest then records `reviewer: null`, and
runContract turned that null into the literal reviewer name "unnamed reviewer".

MEASURED before the fix:
  runContract([{findings:[{file:'x.mjs',line:1,rule_key:'NOPE',severity:'MAJOR'}]}],
              {expectedReviewers:['code-correctness-reviewer']})
  -> missing_reviewers = ["unnamed reviewer", "code-correctness-reviewer"]
One failure, two entries, one of them an agent nobody launched. The
`reported.delete(s.reviewer)` line was also inert for that case, since a null
name was never in the set to begin with.

Fix: skipped payloads are split by whether they carry a name. Named ones go to
missing_reviewers as before; anonymous ones increment the new
`unattributable_payloads` count, which forbids ALLOW on its own - so stripping
a reviewer name from a payload cannot restore ALLOW, and the floor does not
depend on the caller passing expectedReviewers. `allow_blocked_by` reports the
two facts separately: `missing-reviewer:<name>` and `unattributable-payload (n)`.

The old behaviour never produced a false ALLOW - it failed in the safe
direction - but it named a reviewer that did not exist, which is the kind of
output an operator would chase.

Iron Law: two failing tests first (double entry; anonymous-payload-alone must
forbid ALLOW), then the fix.

Also verified in this pass, by temporarily adding a fake reason to
UNVERIFIED_REASONS: the prose-vocabulary pin does go red when a reason is
declared in the lib but missing from agents/review-coordinator.md. A pin that
cannot fail is not a pin.

Suite 1034 (1032/0/2) -> 1036 (1034/0/2), 0 failures.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-01 22:54:31 +02:00
e2aec019ac fix(review): fail-closed verdicts - an unsubstantiated finding can no longer yield ALLOW
computeVerdict counted only the findings handed to it (reasoned.kept), so a
finding removed by Pass 2 or Pass 3, and a reviewer whose payload was thrown
away or never arrived, were arithmetically identical to a finding that never
existed. All three pushed the verdict toward ALLOW.

Measured before the fix (probes, 2026-09-01):
  - a BLOCKER with a 101-character title -> ALLOW (Pass 2 succinctness)
  - a payload with one ad-hoc rule_key is skipped WHOLE at ingest, taking a
    valid BLOCKER sibling with it -> ALLOW
  - a reviewer that never reported -> ALLOW
Pass 3's own no-citation / unknown-rule_key branches turned out unreachable
through runContract (validateFindings rejects those payloads first), so the
reachable exposure was Pass 2 plus the skipped/absent reviewer.

THE OPEN DESIGN DECISION, and why it went against the order's default.
The order proposed: indeterminate file-existence YES, plain succinctness NO
("a too-long finding is not an uncertain finding"). I kept the first and
overrode the second, on one principle:

  A removal is `dropped` only when the test REFUTED the finding as a claim
  about this codebase. Every other removal is `unverified`.

Succinctness and actionability read a `.length`. They never examine the claim,
so they cannot establish the finding is unreal - and dropping a BLOCKER for a
101-character title is precisely the fail-open shape being fixed. Three things
settled it:

1. Under the order's default the fix would have been almost inert. Pass 3's
   drop branches are unreachable via runContract, so leaving Pass 2 out would
   have left the only reachable finding-level exposure open.
2. Cost asymmetry, priced rather than asserted: the verdict is not a gate.
   Handover 6 feeds `findings` filtered to BLOCKER+MAJOR into /trekplan
   (commands/trekplan.md:218); `verdict` is optional metadata
   (docs/HANDOVER-CONTRACTS.md:353). Nothing loops or re-plans on WARN. So a
   false `unverified` costs WARN plus a printed reason; a false drop costs a
   silent ALLOW over a live BLOCKER.
3. unknown-rule_key joins them for the same reason: an ad-hoc key is a real
   defect wearing the wrong label, and v5.1.1 high-effort mode already KEEPS
   those, normalised to PLAN_EXECUTE_DRIFT. Refuting them at normal effort
   while keeping them at high effort would be incoherent.

no-citation stays a drop: a finding whose file is empty or whose line is
negative names no location, so it makes no checkable claim at all - the one
deterministic refutation, and what the Pass 3 prose already said it was.

Iron Law: tests/lib/coordinator-contract.test.mjs first, red (missing export +
the three measured ALLOWs), then production code. Two existing assertions were
updated AFTER implementation as contract changes, not to make the red pass.
A known-positive control pins that ALLOW is still reachable - without it,
"no ALLOW" is not a fail-closed contract, only a broken one.

lib/review/coordinator-contract.mjs
  + classifySuppression / REFUTING_REASONS / UNVERIFIED_REASONS - one
    vocabulary owned by the lib, including the tokens only the LLM
    coordinator emits (accuracy:refuted, file-existence:refuted/indeterminate),
    so prose and lib cannot drift. Unclassified reasons default to unverified:
    the default fails closed.
  ~ judgeFilter / reasonablenessFilter return {kept, dropped, unverified}
  ~ computeVerdict(findings, {unverified, missingReviewers}) -> + allow_blocked_by.
    Never raises a verdict, only withholds ALLOW. Unverified findings are NOT
    counted into a severity tier: their severity was never substantiated, and
    counting it would be invention.
  ~ runContract(payloads, {expectedReviewers}) -> + unverified,
    missing_reviewers, allow_blocked_by. `suppressed` stays the union of
    dropped + unverified, so existing consumers (gold-eval) keep their meaning.

agents/review-coordinator.md - Pass 2/3 tables gain a fate column, new
  "Suppression is two-valued" section, Pass 4 threshold table gains the two
  fail-closed rows, Executive Summary must state a withheld ALLOW, Suppressed
  Findings tags each line [dropped]/[unverified]. Pass 3's unknown-rule_key
  bullet explicitly says high-effort does not reach that branch, so the same
  input never has two documented fates.

commands/trekreview.md - Phase 5 "Reviewer accounting": the expected set is
  written down before the spawn, a silent reviewer gets one re-ask and then
  STOP. That extends the pattern already in the file (schema failure -> 2
  bounded re-asks -> "do not feed unvalidated findings to the coordinator") to
  the other two ways a reviewer goes missing, rather than softening it to WARN.
  The lib's missing_reviewers stays as belt-and-braces for direct callers.

docs/agent-return-channel-defect.md - the "inferred, not observed" caveat on
  the unnamed arm above 66 lines is struck: akashic-intelligence S27
  (f168630) measured 2/2 unnamed agents returning against a 4370-line plan,
  30449 B and 10989 B, both valid JSON. Recorded with akashic's own two
  caveats intact - the measurer owns the finding, and byte-identity between
  the returned string and the file on disk was not proven. The separate S25
  named-arm figures are left standing; these are two measurements, not a
  correction of one by the other.

No release, no version bump, no tag, no catalogue ref, no Workflow port.
Suite 1025 (1023/0/2) -> 1034 (1032/0/2), 0 failures.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-01 22:46:39 +02:00
fdd3ad80d7 feat(voyage): W2 impl (S4) — gate resolver model, adopt native effort:, doc-truth
S4 of the 2.1.181 upgrade — implementation, not a gate. TDD: failing test
written first for the resolver gate, then the fix; suite green throughout.

- Resolver MAJOR (FIX): lib/profiles/phase-signal-resolver.mjs now imports
  BASE_ALLOWED_MODELS from profile-validator and gates `model`
  (if 'model' in entry && BASE_ALLOWED_MODELS.includes(entry.model)),
  mirroring the EFFORT_LEVELS gate one line up. Out-of-allowlist models
  (gpt-4, haiku) are dropped instead of handed to an agent spawn —
  defense-in-depth behind brief-validator's validation-time check. No
  circular import (brief-validator already imports the same symbol).
  +2 tests (drops-invalid / keeps-valid).
- Native effort: (SHIP, static additive): effort: frontmatter on 8 agents —
  retrieval (task-finder, git-historian, dependency-tracer,
  architecture-mapper) = medium; adversarial-reasoning (plan-critic,
  risk-assessor, contrarian-researcher, review-coordinator) = high. The
  other 15 stay unset -> inherit Opus-4.8 default (high). This per-spawn
  REASONING effort is a different axis from brief phase_signals.effort
  (ORCHESTRATION shape) per the S3 decision.
- Doc-truth + axis distinction: new canonical docs/profiles.md
  §Model & effort axes (opus->Opus 4.8 default-high; orchestration vs
  reasoning effort table; native-effort precedence; per-agent levels).
  Short notes in CLAUDE.md (after Agents table) and README.md (Cost
  profile), both pointing to profiles.md.
- Open (non-blocking, unchanged): only STATIC effort shipped — the
  verified-safe minimum. Profile-driven DYNAMIC effort still needs
  verification of the per-spawn effort param or env-var injection.

Matrix: new "S4 resolutions" section. Tests 582 total / 580 pass / 0 fail /
2 skip (was 578 pass; +2). claude plugin validate passes (only pre-existing
root-CLAUDE.md warning).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LqBYc8Ltrk7LipyJmGxXiB
2026-06-18 12:27:07 +02:00
098ba15a5b feat(voyage): define high-effort behavior + amend brief Non-Goal/SC1 + coordinator normalization (Decision B)
Wave 6 / Step 10 — autonomy-gated. Operator confirmed: gemini-bridge
substitution for plan-critic doubling AND SC1 amendment to
resolver-invariant encoding (decisions.local.json recorded).

- commands/trekplan.md: gemini-bridge plan-review Pass 2 on
  post-revision plan in high-effort mode (replaces fragile
  plan-critic doubling per risk-assessor).
- commands/trekresearch.md: full swarm + contrarian-researcher +
  gemini-bridge always-on.
- commands/trekreview.md: skip Pass 3 reasonableness + invoke
  coordinator normalization rule.
- commands/trekexecute.md: gates_mode = closed (strict manifest-audit,
  main-merge pauses); flag override still wins.
- agents/review-coordinator.md: Pass 3 high-effort normalization —
  substitute unknown rule_key with PLAN_EXECUTE_DRIFT, preserve
  original in original_rule_key.
- .claude/projects/2026-05-13-trekflow-solo-lane/brief.md (gitignored,
  not committed): Non-Goal amendment locks low/high tiers; SC1
  amendment authorizes resolver-invariant interpretation.
- tests/lib/doc-consistency.test.mjs: +4 pins for the
  "### High-effort behavior (v5.1.1)" heading per command.

Tests: 578 pass, 0 fail, 2 skipped (+4 from 574).

Closes #7 (operator-gated decisions captured + coordinator
normalization landed).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-15 16:07:37 +02:00
40d8742712 chore(voyage): pin all sub-agents to Opus permanently (operator request)
Flip model: sonnet → model: opus across 20 agent files, 4 prose references
in commands (trekplan, trekresearch), trekendsession command frontmatter,
and CLAUDE.md tables. Aligns CLAUDE.md premium-profile row to actual
premium.yaml content (all-opus, which has been the case since v4.1.0 but
the doc was drift). Companion to VOYAGE_PROFILE=premium env-var (set in
~/.zshenv same day) — env-var governs orchestrator phase model; this
commit governs sub-agent models which are frontmatter-pinned and not
reachable by the profile resolver.

npm test: 516 pass, 0 fail, 2 skipped (unchanged from baseline).

Operator rationale: complete Opus coverage across all Voyage activity,
including the 20 sub-agents that the profile system does not control
(architecture-mapper, task-finder, plan-critic, scope-guardian,
brief-reviewer, code-correctness-reviewer, brief-conformance-reviewer,
review-coordinator, session-decomposer, plus the 6 researcher agents,
plus the 5 codebase-analysis agents).

Cost implication: sub-agent runs ~5x more expensive vs sonnet. Accepted.
2026-05-13 20:20:08 +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
Kjell Tore Guttormsen
6c9caf1982 feat(ultraplan-local): add agents/review-coordinator.md 2026-05-01 16:54:54 +02:00