voyage/tests/hooks
Kjell Tore Guttormsen d2b6a696bd fix(cap): fail closed when the ledger cannot be READ, in both modules
An unreadable ledger returned 0 from countTurns in BOTH the primitive and
the hook, so a run whose ledger existed but could not be read (EISDIR,
EACCES, EIO) was handed the full budget again on every call - unbounded.
research-loop-cap.mjs argues against exactly that three lines above the
code that did it, and its missing-DIRECTORY case already failed closed.
The unreadable-FILE case now agrees with it.

Only ENOENT still counts as zero turns spent: that is the legitimate
first-turn state, and the reason this cannot just throw on any read
failure.

The hook no longer carries its own countTurns. It imports the primitive's
exported readLedger(), the same way it already resolves the data root
through resolveDataRoot() - a reader and a writer with private copies of
the counting rule is how a hook ends up enforcing a different bound than
the gate it backs. In scope + cannot count now exits 2 with a message
that says counting failed, not that the budget is spent.

Fail-closed stays scoped to the loop: a test pins that an unreadable
ledger in an OUT-of-scope session still exits 0, because a PreToolUse
hook that over-blocks bricks every session on the box.

Also dropped the existsSync pre-check before the read - readFileSync's
own ENOENT carries the same information without a second syscall that
can disagree with the read that follows it.

Review finding 5e1c6230f48ead38fa77cd8f4b06bfdc2b5b7bbf (MINOR).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LuGhWAbWyRFBFeemfhxoVv
2026-08-12 22:49:08 +02:00
..
agent-cap.test.mjs fix(cap): fail closed when the ledger cannot be READ, in both modules 2026-08-12 22:49:08 +02:00
bash-guard.test.mjs fix(hooks): close the bypasses anchoring opened in the destructive-command rule 2026-08-12 22:03:22 +02:00
hooks-json-stop-wired.test.mjs chore(voyage): W3 hardening (S2) — exec-form hooks, enforced disallowed-tools, F2 decision 2026-06-18 12:03:11 +02:00
otel-export-otlp.test.mjs feat(voyage): add lib/exporters/otlp-format.mjs — OTLP/JSON enum-integer SC #13 2026-05-09 09:32:29 +02:00
otel-export-textfile.test.mjs feat(trekresearch): emit and export the five STORM measurement fields 2026-08-09 15:01:15 +02:00
otel-export-token-capture.test.mjs feat(observability): opt-in token capture in Stop hook (VOYAGE_TOKEN_METER) 2026-06-26 14:42:58 +02:00
otel-export-validators.test.mjs fix(exporters): allowlist trekresearch engine field and pin schema fixture agreement 2026-08-09 13:04:47 +02:00
otel-export.test.mjs feat(voyage): add hooks/scripts/otel-export.mjs — Stop-hook orchestration SC #14, opt-in via VOYAGE_EXPORT_MODE 2026-05-09 09:44:13 +02:00
path-guard.test.mjs test(ultraplan-local): add path-guard + bash-guard baseline hook tests (SC8 baseline) 2026-05-04 08:55:49 +02:00
post-compact-flush.test.mjs feat(ultraplan-local): add PostCompact rehydrate hook to re-inject session-state after compaction 2026-05-04 07:57:42 +02:00
worktree-guard.test.mjs feat(ultraplan-local): harden Phase 2.6 wave executor (11 sub-changes for plugin-in-monorepo + gitignored-state topology) 2026-05-04 07:49:45 +02:00