voyage/tests/lib
Kjell Tore Guttormsen 7dc0add768 feat(research-loop-cap): give the discovery ceiling a reader, not just a sentence
The bounded-cost NFR asks for explicit ceilings on BOTH axes - max
conversation turns and max discovered dimensions. The turn axis got
MAX_CONV_TURNS, a ledger-backed reader and a PreToolUse enforcer. The
discovery axis got one sentence in Phase 4.5 prose ("append candidates only
while the whole list stays at or below maxDimensions: 8") with no constant
of its own, no reader, and no test that a run exceeding it is caught. That
is the brief_reviewer_iter_cap shape the operator decision warned about: a
cap nothing reads.

checkDimensionCeiling() is the reader, exposed on the CLI as
--check-dimensions N (exit 0 within, exit 1 rejected), and Phase 4.5 step 3
now calls it once the final list is settled instead of merely describing the
bound.

Three deliberate choices:

- The ceiling IS MAX_TOTAL_DIMENSIONS, the constant that sizes the turn
  budget. Both axes read one settings.json:16 value, so they cannot end up
  enforcing different numbers - a second constant is how that drift starts.
- An unreadable count is REJECTED ('abc', null, undefined, {}, -1, NaN,
  non-integers). A cost ceiling that waves through what it cannot measure is
  not a ceiling.
- --check-dimensions requires no run id, effort or VOYAGE_STORM_ENABLED.
  Phase 4.5 never calls the budget gate - that is why its skip-guard reads
  the flag directly - so the ceiling check must not inherit the gate's
  preconditions.

The mitigation the review already verified still holds and is unchanged:
MAX_TOTAL_DIMENSIONS bounds actual retrieval cost regardless of how many
dimensions discovery appends. What was missing was anything that FAILS on a
list over the bound, and now a run over it is rejected by exit code.

Review finding 96a3ee51152dfe72aca703f771843f2f3639e7b6 (MINOR).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LuGhWAbWyRFBFeemfhxoVv
2026-08-12 23:09:11 +02:00
..
agent-frontmatter.test.mjs test(agents): pin examples-in-body invariant (RED before M4 relocation) 2026-06-29 10:11:53 +02:00
arg-parser.test.mjs feat(voyage): S11 — NW2 part B, integrate opt-in --workflow flag 2026-06-18 17:22:09 +02:00
atomic-write.test.mjs refactor(ultraplan-local): extract atomicWriteJson to lib/util 2026-05-01 20:21:15 +02:00
autonomy-gate.test.mjs feat(ultraplan-local): add autonomy-gate state machine + manifest schema extensions for skip_commit_check + memory_write 2026-05-04 06:28:47 +02:00
bash-normalize.test.mjs feat(ultraplan-local): Spor 1 wave 1 — lib/parsers + 66 tests grønn 2026-05-01 05:35:28 +02:00
cache-analyzer.test.mjs test(observability): close 2 MAJOR test gaps from SKAL-2 review 2026-06-26 17:14:03 +02:00
cleanup.test.mjs feat(voyage)!: rename produced_by field values + validator comments [skip-docs] 2026-05-05 14:42:21 +02:00
coordinator-contract.test.mjs feat(eval): add review-coordinator contract reference impl + deterministic test 2026-06-26 11:54:48 +02:00
doc-consistency.test.mjs fix(research-loop-cap): resolve the data root in code so the loop can run 2026-08-12 22:26:46 +02:00
fidelity-diff.test.mjs feat(voyage): S10 — NW2 part A (Workflow port + fidelity harness + smoke) 2026-06-18 14:40:19 +02:00
finding-id.test.mjs feat(ultraplan-local): add lib/parsers/finding-id.mjs (stable SHA1) 2026-05-01 13:28:05 +02:00
findings-schema.test.mjs feat(voyage): S9 — NW1 reviewer-output schema contract (TDD, ungated) 2026-06-18 14:06:07 +02:00
frontmatter.test.mjs feat(voyage)!: bulk content rewrite ultra -> voyage/trek prose [skip-docs] 2026-05-05 15:08:20 +02:00
gates-flag-coverage.test.mjs feat(voyage)!: rename commands, templates, fixtures for v4.0.0 [skip-docs] 2026-05-05 14:13:44 +02:00
gold-corpus.test.mjs feat(eval): add gold.json golden corpus + loader/validator test 2026-06-26 11:51:46 +02:00
gold-eval.test.mjs feat(eval): SKAL-1·4b offline gold-scored output eval 2026-06-30 09:00:33 +02:00
gold-scorer.test.mjs feat(eval): SKAL-1·4b offline gold-scored output eval 2026-06-30 09:00:33 +02:00
jaccard.test.mjs feat(ultraplan-local): add lib/parsers/jaccard.mjs 2026-05-01 13:28:44 +02:00
main-merge-gate.test.mjs feat(voyage)!: rename commands, templates, fixtures for v4.0.0 [skip-docs] 2026-05-05 14:13:44 +02:00
manifest-schema-extensions.test.mjs feat(voyage): add OPTIONAL_STRING_KEYS path to manifest-yaml — profile_used additive 2026-05-09 09:23:32 +02:00
manifest-yaml.test.mjs feat(graceful-handoff): 2.0 — migrate to skills/ with disable-model-invocation [skip-docs] 2026-05-01 05:45:26 +02:00
phase-signal-resolver.test.mjs test(validators): cover fable accept + unknown-model reject in both gate layers 2026-07-02 16:56:26 +02:00
plan-review-dedup.test.mjs fix(voyage): S23 — make /trekplan Phase 9 dedup executable (defect #1) 2026-06-19 21:12:36 +02:00
plan-schema.test.mjs fix(voyage): S26 — parse plan_version prose form (S22 defect #2/#3) 2026-06-19 21:59:31 +02:00
profile-application.test.mjs test(profiles): pin fable profile resolution end-to-end 2026-07-02 17:00:43 +02:00
profile-flag-coverage.test.mjs feat(voyage): document --profile flag in all 6 commands — SC #4 + arv-policy 2026-05-09 09:38:36 +02:00
profile-resolver.test.mjs test(profiles): pin fable profile resolution end-to-end 2026-07-02 17:00:43 +02:00
profile-stats-fields.test.mjs feat(trekresearch): emit and export the five STORM measurement fields 2026-08-09 15:01:15 +02:00
project-discovery.test.mjs feat(voyage)!: bulk content rewrite ultra -> voyage/trek prose [skip-docs] 2026-05-05 15:08:20 +02:00
research-loop-cap.test.mjs feat(research-loop-cap): give the discovery ceiling a reader, not just a sentence 2026-08-12 23:09:11 +02:00
review-determinism.test.mjs feat(voyage)!: rename commands, templates, fixtures for v4.0.0 [skip-docs] 2026-05-05 14:13:44 +02:00
rule-catalogue.test.mjs feat(ultraplan-local): add lib/review/rule-catalogue.mjs (12 rule keys) 2026-05-01 13:27:29 +02:00
source-findings.test.mjs feat(voyage)!: bulk content rewrite ultra -> voyage/trek prose [skip-docs] 2026-05-05 15:08:20 +02:00
stats-event-emit.test.mjs feat(voyage)!: rename stats filenames, settings keys, hook prefixes [skip-docs] 2026-05-05 14:49:03 +02:00
synthesis-digest-schema.test.mjs feat(voyage): S12 — NW3 synthesis-agent built + measured → declined per measurement [skip-docs] 2026-06-18 17:58:39 +02:00
test-census.test.mjs feat(eval): SKAL-1·4b offline gold-scored output eval 2026-06-30 09:00:33 +02:00
token-usage.test.mjs feat(stats): add claude-fable-5 to PRICE_TABLE 2026-07-02 17:15:36 +02:00