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>
This commit is contained in:
Kjell Tore Guttormsen 2026-05-09 09:29:01 +02:00
commit f419121682
4 changed files with 474 additions and 0 deletions

View file

@ -0,0 +1,26 @@
---
plan_version: "1.7"
profile: balanced
phase_models:
- phase: brief
model: sonnet
- phase: research
model: sonnet
- phase: plan
model: opus
- phase: execute
model: sonnet
- phase: review
model: opus
- phase: continue
model: sonnet
---
# Test plan (with profile)
This fixture has explicit profile + phase_models in frontmatter.
## Implementation Plan
### Step 1: Stub
- Files: src/stub.mjs

View file

@ -0,0 +1,15 @@
---
plan_version: "1.6"
---
# Test plan (v4.0-style, no profile field)
This fixture is a v4.0-style plan WITHOUT the v4.1 profile/phase_models fields.
Used by tests/lib/profile-application.test.mjs to verify backward-compat
edge-case: resolveTrekcontinueProfile returns {profile: 'premium', profile_source: 'default'}
without throwing when the plan has no profile concept.
## Implementation Plan
### Step 1: Stub
- Files: src/stub.mjs