916d30f63e
chore(voyage): release v5.0.0 — remove bespoke playground + /trekrevise + Handover 8; render produced artifacts to HTML + link, annotate via /playground
...
The v4.2/v4.3 bespoke playground SPA (~388 KB), the /trekrevise command,
Handover 8 (annotation → revision), the supporting lib/ modules
(anchor-parser, annotation-digest, markdown-write, revision-guard), the
Playwright e2e suite, and the @playwright/test / @axe-core/playwright
devDeps are removed. A browser walkthrough found the playground borderline
unusable, and it duplicated the official /playground plugin's
document-critique / diff-review templates.
In their place: scripts/render-artifact.mjs — a small, zero-dependency
renderer that turns a brief/plan/review .md into a self-contained,
design-system-styled, zero-network .html (frontmatter folded into a
<details> block). /trekbrief, /trekplan, and /trekreview call it on their
last step and print the file:// link; to annotate, run /playground
(document-critique) on the .md and paste the generated prompt back.
Resolves the v4.3.1-deferred findings as moot (their target files are
deleted). npm test green: 509 tests, 507 pass, 0 fail, 2 skipped.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-12 14:05:07 +02:00
412b4561f5
fix(voyage): inline screenshot gallery loads docs/screenshots/ PNGs (31d28f65)
2026-05-10 21:25:01 +02:00
5820478f71
test(voyage): add Group B (structure) + Group C (annotation export schema) tests [skip-docs]
...
Step 29 of v4.3 plan — Wave 7:
- Group B (9 tests): DS-token classes (badge--scope-voyage, guide-panel,
fleet-grid), theme-toggle wiring (data-action, wireThemeToggle,
localStorage), sidebar-tab keyboard pattern (role=tablist,
aria-selected, J/K/Esc), anchor-ID format mirror.
- Group C (7 tests, +1 vs original): export-bundle JSON parse, required
keys, per-annotation field validation, empty-export edge case,
annotation_digest order-independence, SHA-256 16-hex-char validity
(SC6 / SC-GAP-3), fixture plan anchor format.
- Fixtures: tests/fixtures/playground/v43-export-bundle.json +
v43-plan-pre-annotate.md (ANN-0001 + ANN-0002, revision: 0).
Test count: 689 → 705 pass / 0 fail / 2 skipped.
2026-05-10 18:18:24 +02:00
c412f72605
test(voyage): add annotation roundtrip + rollback + source_annotations integration — v4.2 Step 7
...
Implements SC2/SC3/SC5b/SC7 + additive-field invariant for the v4.2
annotation pipeline:
Fixtures (tests/fixtures/annotation/):
- annotation-brief.md — brief-validator-clean fixture
- annotation-plan.md — plan-validator-clean (2 steps)
- annotation-review.md — review-validator-clean
- annotation-plan-large.md — 51 steps (SC3 scale fixture)
Integration tests:
- tests/integration/annotation-roundtrip.test.mjs — 7 cases:
SC2 byte-identical empty round-trip across brief/plan/review,
SC3 scale (51 steps + 100 anchors) round-trip,
SC7 parseAnchors(stripAnchors(addAnchors(...))) === [] per target.
- tests/integration/schema-rollback.test.mjs — 4 cases:
SC5b validator-FAIL -> revisionGuard rolls back byte-identical
(sha256 invariant) for brief/plan/review + cross-target sweep.
.local.bak deleted on rollback path (validator-PASS path tested
in lib/util/revision-guard tests).
- tests/lib/source-annotations.test.mjs — 6 cases mirroring
tests/lib/source-findings.test.mjs additive-field pattern: each
validator (brief/plan/review) accepts source_annotations as
additive-optional, parser extracts as array of dicts, entries
conform to documented shape, baseline forward-compat (artifacts
without source_annotations still validate).
Verify: node --test tests/integration/annotation-roundtrip.test.mjs
tests/integration/schema-rollback.test.mjs
tests/lib/source-annotations.test.mjs -> 17 pass / 0 fail.
Full npm test: 577 pass / 0 fail / 2 skipped (Docker).
Refs plan.md Step 7 + plan-critic M4 + plan-critic B4.
2026-05-09 15:13:27 +02:00
fb733ae149
feat(voyage): add anchor-parser.mjs with placement validation — v4.2 Step 3
...
lib/parsers/anchor-parser.mjs (~190 LoC):
- parseAnchors(md) -> Anchor[] (id, target, line, snippet?, intent?)
- addAnchors(md, anchors) -> md_with_anchors
- stripAnchors(md_with_anchors) -> md (byte-identical)
- validateAnchorPlacement(md, anchors) -> errors for list-item / fenced-block / indent
Format: <!-- voyage:anchor id="ANN-NNNN" target="<slug>" line="<N>" -->
Block-level only, on its own line (col 0), blank-line separation.
Test fixture annotation-example.md with single ANN-0001 anchor — referenced by SC12 quickstart.
14 tests pass (parseAnchors, addAnchors, stripAnchors, validateAnchorPlacement).
2026-05-09 12:52:46 +02:00
e98eba88c9
feat(voyage): emit MANIFEST_PROFILE_DRIFT warning in plan-validator strict mode — brief assumption 7
...
Step 20 of v4.1 — implements drift detection in plan-validator.mjs per
brief Assumptions block 7: "Mismatch (e.g. korrupt manuell endring)
emitterer MANIFEST_PROFILE_DRIFT-warning fra plan-validator i --strict-modus."
Logic (after validateAllManifests in validatePlanContent):
1. Strict-mode only — soft mode never emits drift warnings.
2. Plan frontmatter must declare 'profile: <name>' to establish baseline.
3. For each step manifest, if profile_used is set AND differs from plan
profile, emit warning (NOT error) with code MANIFEST_PROFILE_DRIFT
and location 'step N: profile_used = X, plan profile = Y'.
Forward-compat preserved: drift is a warning, plan remains valid:true.
Operators see the drift in --strict mode without parsing breaking.
New files:
tests/validators/plan-validator-profile-drift.test.mjs — 4 tests
tests/fixtures/plan-profile-drift.md — drift fixture
Tests verify:
1. drift detected in strict mode → MANIFEST_PROFILE_DRIFT in warnings
2. drift NOT detected in soft mode → strict gate honored
3. matching profile → no drift warning
4. no plan-level profile → drift detection silent (no baseline)
Tests: 479 pass + 2 skipped (Docker not installed).
2026-05-09 10:02:53 +02:00
ef379bedf7
feat(voyage): add 5 additive profile fields to JSONL stats — SC #11
...
Step 8 av v4.1-execute (Wave 3, Session 4).
5 nye additive felter er nå dokumentert i hver kommandos prose-stats-blokk
(via Profile-seksjonen fra Step 7 — felles overflate per kommando):
- profile — string ('economy' | 'balanced' | 'premium' | <custom>)
- phase_models — object form {brief: 'sonnet', ..., continue: 'opus'}
- parallel_agents — number (snapshot av maksverdi som faktisk ble brukt)
- external_research_enabled — boolean
- profile_source — 'flag' | 'env' | 'default' | 'inheritance'
Patcher trekresearch.md med eksplisitt profile_source-mention + alle 5 felter
(de andre 5 commands hadde dette allerede via Step 7 Profile-seksjon).
SC #11 contract-test design (per brief):
(a) Fixture-records valideres som JSONL-contracts → tests/fixtures/stats-with-profile.jsonl
(5 simulerte stats-rader, én per kommando-overflate)
(b) Command-prose contains field-names → kompenserer for plan-critic Major 4
false-confidence (faktisk runtime-emission er LLM-prose-driven, ikke
testbart i node:test alene).
Tester (12 nye, baseline 445 → 457):
- Fixture parses som JSONL (5 records)
- Hver record har profile + profile_source
- profile_source-verdier i {flag, env, default, inheritance}
- Fikstur dekker alle 4 profile_source-verdier
- 6 commands × prose contains profile + profile_source
- trekplan.md prose contains phase_models + parallel_agents
- trekresearch.md prose contains external_research_enabled
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-09 09:40:21 +02:00
2349d1d431
feat(voyage): add lib/exporters/textfile-format.mjs — Prometheus text-format pure transform SC #12
...
Step 9 av v4.1-execute (Wave 2, Session 3).
Pure function transformToPrometheus(records) → Prometheus text-format 0.0.4.
Hard rules:
- NO client-side timestamps (research/01 node_exporter#1284 mitigation)
- Allowlist-redacted records ONLY (caller responsibility — Step 11 enforces)
- UTF-8 metric names normalized: lowercase, [.\\-\\s] → _, voyage_ prefix
- Empty input → empty string output
- Sorted output for determinism (snapshot-test-friendly)
Heuristic metric typing:
- counter: *_total, *_count, *_passed, *_failed, *_skipped
- histogram: *_ms, *_duration, *_p\\d+, *_seconds
- gauge: everything else (Prometheus convention)
Snapshot: tests/fixtures/expected.prom byte-for-byte match.
Regenerate: node scripts/gen-expected-prom.mjs > tests/fixtures/expected.prom
Tester (6 nye, baseline 400 → 406):
- Snapshot byte-for-byte match (SC #12 )
- Empty input handling (null, undefined, [])
- Allowlist-redaction sanity (post-bash-stats uten command_excerpt)
- NO client-side timestamps (token-count-assertion per linje)
- normalizeMetricName edge-cases
- Determinism (identisk input → identisk output)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-09 09:30:58 +02:00
f419121682
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>
2026-05-09 09:29:01 +02:00
be9ad6ec07
feat(voyage): add lib/validators/profile-validator.mjs — SC #1 , #2 , #3
...
Step 5 av v4.1-execute (Wave 2, Session 2).
Profile-validator etter brief-validator-mønsteret eksakt: validateProfileContent
(pure), validateProfile (file-reader), CLI shim med --json flag. Eksporter
PROFILE_REQUIRED_FIELDS (frozen), PROFILE_REQUIRED_PHASES (frozen).
Validerer:
- Required frontmatter fields (name, phase_models, parallel_agents_min/max,
external_research_enabled, brief_reviewer_iter_cap)
- phase_models = list-of-dicts med phase + model
- 6 required phases (brief, research, plan, execute, review, continue)
- parallel_agents_max ≥ parallel_agents_min
- Allowed model values: ['sonnet', 'opus']; haiku tillatt KUN ved
VOYAGE_ALLOW_HAIKU=1 (per global CLAUDE.md modellvalg-prinsipp)
Issue codes: PROFILE_MISSING_FIELD, PROFILE_INVALID_MODEL, PROFILE_INVALID_ENUM,
PROFILE_READ_ERROR, PROFILE_NOT_FOUND.
Field-path-reporting i error-location: phase_models[N].model for SC #2 .
Tester (10 nye, baseline 377 → 387):
- SC #1 x3 (innebygde profiler grønne)
- SC #2 (PROFILE_INVALID_MODEL med location phase_models[2].model)
- SC #3 (PROFILE_INVALID_ENUM for external_research_enabled: "yes" string)
- VOYAGE_ALLOW_HAIKU env-var deny/allow
- PROFILE_MISSING_FIELD når name fraværende
- PROFILE_NOT_FOUND for ikke-eksisterende fil
- 2 export drift-pins
Fixturer: profile-invalid-model.yaml (gpt-4 i phase_models[2]),
profile-invalid-enum.yaml (external_research_enabled som string "yes").
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-09 09:26:23 +02:00
0bdfc02e75
docs(voyage): jsonl schema audit — field-allowlist input for v4.1 otel exporter
...
Step 1 av v4.1-execute (Wave 1, Session 1).
Audit alle 6 trek*-stats.jsonl-skjemaer + lib/stats/event-emit.mjs autonomy
events + hooks/scripts/post-bash-stats.mjs PostToolUse Bash records. Produser
markdown-tabell {schema_id, fields[], writer_path, line_ref, v4.1 additive,
PII} som load-bearing input til Step 11 (field-allowlist) og Step 8 (stats
plumbing).
Spesielle merker:
- command_excerpt fra post-bash-stats.mjs flagget CWE-212 (improper cross-
boundary removal of sensitive data) — eksporten MÅ hard-ekskludere uten
eksplisitt VOYAGE_EXPORT_INCLUDE_COMMAND_EXCERPT=1 (deferred til v4.2)
- v4.1 additive fields enumerert per skjema: profile, phase_models,
parallel_agents, external_research_enabled, profile_source
- EXPORT_ALLOWLIST + EXPORT_DENYLIST utdrag i bunnen som forhåndsdefinisjon
av Step 11 inline static consts
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-09 09:20:54 +02:00
7a90d348ad
feat(voyage)!: marketplace handoff — rename plugins/ultraplan-local to plugins/voyage [skip-docs]
...
Session 5 of voyage-rebrand (V6). Operator-authorized cross-plugin scope.
- git mv plugins/ultraplan-local plugins/voyage (rename detected, history preserved)
- .claude-plugin/marketplace.json: voyage entry replaces ultraplan-local
- CLAUDE.md: voyage row in plugin list, voyage in design-system consumer list
- README.md: bulk rename ultra*-local commands -> trek* commands; ultraplan-local refs -> voyage; type discriminators (type: trekbrief/trekreview); session-title pattern (voyage:<command>:<slug>); v4.0.0 release-note paragraph
- plugins/voyage/.claude-plugin/plugin.json: homepage/repository URLs point to monorepo voyage path
- plugins/voyage/verify.sh: drop URL whitelist exception (no longer needed)
Closes voyage-rebrand. bash plugins/voyage/verify.sh PASS 7/7. npm test 361/361.
2026-05-05 15:37:52 +02:00