fix(trekresearch): gate Phase 4.5 on VOYAGE_STORM_ENABLED, not effort alone

CLAUDE.md claimed both STORM phases go inert when the flag is unset. Only
Phase 5 did: the flag check lives in research-loop-cap.mjs, and Phase 4.5
never invokes the cap — it was gated on effort: high alone. At high effort
with the flag unset, discovery still mined Phase-4 output and mutated the
dimension list, so `dimensions` diverged from `dimensions_baseline` and the
decline branch was unreachable for half the mechanism.

The code was the deviator, so the guard is fixed rather than the claim: the
Phase 4.5 skip-guard now names both conditions, with the reason inline.

Three surfaces scoped the flag to "the loop" and are corrected with it
(README, docs/command-modes, docs/architecture), plus the orchestrator phase
map. CLAUDE.md's claim is now true, but its stated MECHANISM was not — Phase
4.5's inertness comes from its own guard, not from the cap module — so that
sentence is corrected too.

New doc-consistency pin: the flag must be documented as gating both phases on
all four reference surfaces. Review finding 00a3af1a.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011vPSXe88qp5aqWUqbDNWoF
This commit is contained in:
Kjell Tore Guttormsen 2026-08-12 22:17:56 +02:00
commit 2e352a7dbb
8 changed files with 55 additions and 9 deletions

View file

@ -12,7 +12,7 @@ Imported from `CLAUDE.md` via pointer.
- `lib/stats/event-emit.mjs` — single-source stats event emitter for autonomy-gate transitions and main-merge-gate (v3.4.0)
- `lib/validators/{brief,research,plan,progress,session-state}-validator.mjs` — schema validators with CLI shims (`node lib/validators/X.mjs --json <path>`)
- `lib/validators/architecture-discovery.mjs` — drift-WARN external-contract discovery for `architecture/overview.md`
- `lib/util/research-loop-cap.mjs` — stateful, **default-off** turn budget for the `/trekresearch` bounded conversation loop. `allowTurn()` derives the used-turn count from its own append-only JSONL ledger; it never asks the caller how many turns it has spent, because a cap that does is not a cap. Budget = `TREKRESEARCH_MAX_CONV_TURNS` (default `3`, invalid values fall back to `3`) × `maxDimensions` (8, `settings.json:16`). Grants 0 unless `VOYAGE_STORM_ENABLED=1`; missing `CLAUDE_PLUGIN_DATA` denies (fail-closed — the opposite of `event-emit.mjs`, which is telemetry and must never block). CLI shim: `node lib/util/research-loop-cap.mjs --run-id ID --dimension D --effort E`
- `lib/util/research-loop-cap.mjs` — stateful, **default-off** turn budget for the `/trekresearch` bounded conversation loop. `allowTurn()` derives the used-turn count from its own append-only JSONL ledger; it never asks the caller how many turns it has spent, because a cap that does is not a cap. Budget = `TREKRESEARCH_MAX_CONV_TURNS` (default `3`, invalid values fall back to `3`) × `maxDimensions` (8, `settings.json:16`). Grants 0 unless `VOYAGE_STORM_ENABLED=1`; missing `CLAUDE_PLUGIN_DATA` denies (fail-closed — the opposite of `event-emit.mjs`, which is telemetry and must never block). The same flag is the second condition on Phase 4.5's skip-guard, which does not call this module: unset, **both** STORM phases are inert. CLI shim: `node lib/util/research-loop-cap.mjs --run-id ID --dimension D --effort E`
- `lib/validators/query-privacy-gate.mjs` — gates **every** outbound research query before it leaves the machine; the hard-block tier (secret-shaped strings) is not operator-overridable, so a query that trips it must be reformulated rather than forced through. CLI shim: `node lib/validators/query-privacy-gate.mjs "<query>"`
Wiring points (replaces previous prose-grep instructions):