Commit graph

73 commits

Author SHA1 Message Date
113296d7de docs(voyage): amend HANDOVER-CONTRACTS + add 5 doc-consistency pins (v5.1) 2026-05-13 21:18:42 +02:00
4504c9a8cf test(voyage): add 5 minimal command test files for v5.1 (sequencing-gate + low-effort) 2026-05-13 21:15:26 +02:00
0655b57930 feat(voyage): bump trekbrief-template to brief_version 2.1 + add phase_signals fixtures 2026-05-13 21:09:57 +02:00
bf68fe6f5f feat(voyage): add phase_signals validation + sequencing gate to brief-validator (v5.1) 2026-05-13 21:08:37 +02:00
8cbb33e1fd docs(voyage): pin operator-UX contract — always emit file:// link + open command
Operator runs Ghostty (also iTerm2, modern Terminal.app) — all support
cmd+click on file:// URLs. Producing commands (/trekbrief, /trekplan,
/trekreview) already emit both forms but the contract was implicit.
This commit makes it explicit:

1. CLAUDE.md gains an "Operator-UX guarantee" paragraph stating both
   forms must always appear in the final report: (a) plain file://
   URL with absolute path (for cmd+click), (b) copy-pasteable
   `open file://` command (for terminals without cmd+click).

2. tests/lib/doc-consistency.test.mjs gains a pin asserting both
   patterns appear in all three producing commands' final report
   blocks. Drift catches at test time.

Non-functional change to the commands themselves — they already
emit both forms (verified at trekbrief.md L510/L519, trekplan.md
L798/L802, trekreview.md L299/L317).

Operator request 2026-05-13: "Noter ned i Voyage at jeg ALLTID får
en slik direkte file:// lenke."
2026-05-13 20:31:58 +02:00
9ba8b682ef chore(voyage): release v5.0.3 — annotation UX matches the claude-code-100x reference
The operator pointed at ~/repos/claude-code-100x/claude-code-100x/build-site.js
as the annotation reference from the start. v4.2/v4.3 built a bespoke
playground instead. v5.0.0 deleted it. v5.0.1 pointed at /playground
document-critique (Claude-leads, wrong direction). v5.0.2 was operator-led
but too thin (line-click + freeform note, no intent). v5.0.3 finally
matches the reference.

scripts/annotate.mjs rewritten:
  - Markdown rendered as proper article HTML (h1/p/li/ul/table/blockquote/pre)
    instead of line-numbered raw lines.
  - Pencil-toggle annotation mode in the topbar, default ON.
  - Select text OR click any element → form popover at cursor.
  - Three intent buttons: Fiks (red) / Endre (orange) / Spørsmål (blue).
  - Comment textarea. Save (Cmd+Enter), Cancel (Esc).
  - Section context auto-detected from nearest h1/h2.
  - Sidebar panel: annotations grouped by section, intent badges,
    snippet quotes, delete buttons, click-to-scroll with flash highlight.
  - Copy Prompt: structured markdown export with intent labels.
  - localStorage persistence keyed on absolute artifact path
    (voyage-annotate:v2: prefix to avoid colliding with v5.0.2 state).

Tests: 12 (up from 10), all passing. npm test: 518 / 516 pass / 0 fail / 2 skipped.

Reference: ~/repos/claude-code-100x/claude-code-100x/build-site.js
lines 1431–2255 (annotation UI section).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-13 15:08:20 +02:00
8ea692bc60 chore(voyage): release v5.0.2 — operator-driven annotation HTML (scripts/annotate.mjs)
v5.0.0 added a read-only HTML render. v5.0.1 deleted that and pointed at
/playground document-critique, which pre-generates Claude's suggestions
and asks the operator to approve/reject them. The operator asked for the
opposite — a surface where THEY drive every annotation. v5.0.2 lands it.

scripts/annotate.mjs (~430 lines, zero deps) takes any artifact .md and
writes a self-contained HTML next to it. The HTML renders the document
with line numbers, lets the operator click any line to add their own
note (inline textarea, save with Cmd+Enter or button), keeps a sidebar
of all notes (editable + deletable + persisted in localStorage per
artifact path), and exposes Copy Prompt to gather every note into one
structured prompt. Operator copies, pastes back, Claude revises the .md.

The three producing commands now run annotate.mjs at their last step and
print the file:// link with explicit "Click any line to add YOUR OWN note"
instructions. The v5.0.1 /playground document-critique line is gone.

npm test green: 516 tests, 514 pass, 0 fail, 2 skipped.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-13 14:04:28 +02:00
2e0892cdaf chore(voyage): release v5.0.1 — drop standalone HTML render; print literal /playground document-critique invocation
The v5.0.0 stop-gap had /trekbrief, /trekplan, and /trekreview each render
a read-only {artifact}.html (via scripts/render-artifact.mjs) AND print a
vague "run the /playground plugin" instruction. In practice the read-only
HTML was redundant with what /playground produces and the instruction
wasn't copy-paste-ready — the operator had to guess the right invocation.

v5.0.1 deletes scripts/render-artifact.mjs + its test + npm run render,
and makes each producing command end with a single boxed, literal,
copy-paste-ready line:

    /playground build a document-critique playground for {artifact_path}

One paste from the operator launches the official playground skill's
document-critique template, which builds an interactive HTML — artifact
on the left, per-line Approve/Reject/Comment cards on the right, Copy
Prompt button at the bottom. Mark suggestions, click Copy Prompt, paste
back, Claude revises the .md. Doc-consistency test pins the literal
invocation so the prose cannot soften back into vagueness.

npm test green: 503 tests, 501 pass, 0 fail, 2 skipped.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-13 13:24:32 +02:00
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
0810f0c4fa test(voyage): regenerate pixel-diff baseline + clean a11y baseline (post-remediation) 2026-05-10 21:49:26 +02:00
ffabd7820e test(voyage): Group C.8 SC6 round-trip via readAndUpdate (1bc37231) 2026-05-10 21:48:21 +02:00
35d28a52f3 test(voyage): Group B fleet-grid CSS parity assertion (99707f51) 2026-05-10 21:46:18 +02:00
4bee1f2f7e test(voyage): convert SC2 a11y spec to absolute 0-violation assertion (09132940) 2026-05-10 21:44:31 +02:00
b202d6542c test(voyage): add Group D XSS injection runtime guard (1d3591d4) 2026-05-10 21:42:52 +02:00
412b4561f5 fix(voyage): inline screenshot gallery loads docs/screenshots/ PNGs (31d28f65) 2026-05-10 21:25:01 +02:00
9909ce1066 fix(voyage): dashboard reads fm.plan_critic + orchestrator doc-consistency (906f155d, bee33a69) 2026-05-10 21:21:02 +02:00
4910999198 fix(voyage): resolve 4 color-contrast WCAG violations in light theme (09132940) 2026-05-10 21:18:53 +02:00
48ab3c9de3 fix(voyage): move sidebar toggle outside aria-hidden region (09132940) 2026-05-10 21:16:52 +02:00
c08bde0649 fix(voyage): sanitize bodyHtml via DOMPurify in renderArtifact (1d3591d4) 2026-05-10 21:14:50 +02:00
5f26de2f0d fix(voyage): inject plan_critic via Phase 9 readAndUpdate (906f155d) 2026-05-10 21:03:54 +02:00
59cab69bf4 verify(voyage): SC1 10-element checklist + Playwright baseline screenshots
Step 31 of v4.3 plan — Wave 7 SC1 authoritative verification:

- docs/sc1-checklist-verification.md: per-element pass/fail med evidens
  (Group A test referanser + manuell side-by-side mot llm-security-
  playground). 8 av 10 PASS bokstavelig, 2 PASS-redef (Element 4
  onboarding-grid -> fleet-grid, Element 6 screenshots-spor -> hooks +
  docs-konvensjon) per scope-guardian Assumptions 21+22 (operator-
  signed-off ved sesjon-start).
- tests/e2e/snapshots/voyage-playground-light.png (1280x900 light theme)
- tests/e2e/snapshots/voyage-playground-dark.png (1280x900 dark theme)
- tests/e2e/snapshots/a11y-baseline.json: WCAG-violations baseline
  (aria-hidden-focus + color-contrast) defer-til-v4.4

Pixel-diff-spec (Step 30) sammenlikner mot baseline med maxDiffPixelRatio
0.02. Wave 7 = VERIFICATION ONLY; HTML FROZEN; faktisk a11y-fix deferred
til v4.4.
2026-05-10 18:24:29 +02:00
067d9ab245 test(voyage): add e2e Playwright + axe-core specs (a11y + network) [skip-docs]
Step 30 of v4.3 plan — Wave 7 Group D:
- voyage-playground-a11y.spec.mjs (3 tests): light + dark theme axe-core
  scans (compared against baseline JSON, fails only on NEW or GROWN
  violations) + pixel-diff smoke for SC1 (light + dark, 1280x900,
  maxDiffPixelRatio=0.02).
- voyage-playground-network.spec.mjs (1 test): SC7 authoritative gate via
  page.on('request') instrument — verifies zero external (http/https/ws)
  requests during page load.
- playwright.config.mjs: snapshotPathTemplate routes to tests/e2e/snapshots/
  (matches Step 31 expected_paths).

Baseline policy: HTML is FROZEN in Sesjon 6 (Wave 7 = verification, not
fix). Existing critical/serious WCAG violations (aria-hidden-focus +
color-contrast) recorded in tests/e2e/snapshots/a11y-baseline.json as
delta-baseline. Actual a11y fix deferred to v4.4.

Verify: npm run test:e2e -> 4 passed (3 a11y + 1 network).
2026-05-10 18:22:52 +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
deca35a28f test(voyage): extend playground tests with Group A static-HTML assertions [skip-docs]
Step 28 of v4.3 plan — Wave 7 Group A: 17 new static-HTML grep assertions
covering SC1 10-element checklist (one test per element), SC3 webkitdirectory
+ drag-drop attributes, SC6 export-bundle markers (buildAnnotatedMarkdown,
filename pattern, Blob + clipboard flows), and SC7 tag-level no-CDN checks
(every <script src> + <link href> must be local).

Test count: 672 → 689 pass / 0 fail / 2 skipped.
2026-05-10 18:15:53 +02:00
cd6bca978f feat(voyage): implement path-traversal + symlink/dotfile filter on loaded files 2026-05-10 18:05:35 +02:00
6293775f30 feat(voyage): implement HTML-comment indirect prompt injection mitigation (Sec T4) 2026-05-10 18:03:37 +02:00
fc8c9eecdd feat(voyage): vendor DOMPurify >=3.1.1 + sanitize annotation-content 2026-05-10 18:01:30 +02:00
e839ba2a7a feat(voyage): implement screenshots-spor convention (window.__hooks + docs/screenshots/) 2026-05-10 17:58:56 +02:00
b70b480d0d feat(voyage): build A11Y-panel from DS-primitives (greenfield) 2026-05-10 17:56:49 +02:00
df0e7837af feat(voyage): implement two-opacity pattern (active/inactive/resolved) 2026-05-10 17:53:43 +02:00
224517f205 feat(voyage): implement J/K keyboard navigation + Esc + aria-live announces
Step 20 of v4.3 playground plan. Document-level keydown handler:
  - J = next annotation (next sorted-by-line draft, wraps)
  - K = prev annotation (wraps)
  - ] = toggle sidebar visibility
  - Escape = clear active anchor + sidebar list selection

Active annotation gets yellow-tint (Step 18 setActiveAnchor) and the
matching gutter-badge receives focus + scrollIntoView. Aria-live region
announces position + target: "Annotering 3 av 7: <target> — <snippet>".

Skips input/textarea/select/contenteditable so playground never steals
keystrokes from form fields. Modifier keys (Ctrl/Alt/Meta) pass through
to browser shortcuts. Wired in init() after dashboard nav.

Trace: SC2 (WCAG AA keyboard), SC6, research/04 Dim 2 + Insight 5 +
Recommendation keyboard-navigation.
2026-05-10 17:10:59 +02:00
6db7c72511 feat(voyage): implement hidden-by-default sidebar-rail with ordered list + filter + jumplist count
Step 19 of v4.3 playground plan. Sidebar now default aria-hidden=true
(translateX collapses panel, leaves 40px FAB rail). FAB toggle has
data-action=toggle-sidebar for keyboard binding (] in Step 20).

New annotation-list section in sidebar panel:
  - filter radiogroup: Alle (default), Åpne (unresolved), Resolved
  - voyage-jumplist ordered list with numbered badges matching the
    gutter-badge ordering (sorted by line ASC)
  - aria-live jumplist count: "X av N" (filtered/total)
  - click list-item -> setActiveAnchor + scrollIntoView + data-active

renderAnnotationList wires into mountRender so list refreshes on every
render. Filter state (voyageFilterState) persists across renders within
the session.

Trace: SC6, research/04 Dim 1 (hidden-by-default) + Insight 1 +
Recommendation sidebar/navigation.
2026-05-10 17:09:26 +02:00
84f41014f9 feat(voyage): replace pencil-icon with numbered-badge gutter + yellow-tint highlight
Step 18 of v4.3 playground plan. Replaces v4.2 Gesture 2 pencil-icon
hover-reveal with numbered circular badges in the left gutter (one per
anchored paragraph; ordering matches sidebar jumplist). 2-3px accent stripe
extends right from the badge into the gutter. Yellow-tint highlight
(rgba 255, 235, 59, 0.25 — Google Docs pattern) applies to the anchored
paragraph when an annotation is active. Body text never reflowed or
recolored. Gesture 1 (text-select adder) and Gesture 3 (page-level note)
remain for new annotation creation.

CSS uses --color-scope-voyage token for badge background and stripe.
JS adds injectAnchorBadges() + setActiveAnchor() and rewires mountRender.

Trace: SC1 + SC6, research/04 Insight 3 + Recommendation marker-design.
2026-05-10 17:06:59 +02:00
75130fe979 feat(voyage): implement block-boundary-fallback for code-fence/table/list anchors
Step 17 of v4.3 playground plan. Pure function relocateAnchorsToBlockBoundaries
(text, anchors) detects atomic markdown blocks (fenced code, tables, deeply
nested lists) and relocates anchor-comment insertion to the line BEFORE block
opening rather than inside the block. Pure markdown-text -> markdown-text
transform (no DOM, no markdown-it dependency).

Companion test tests/integration/annotation-block-boundary.test.mjs extracts
the function via balanced-brace scan and exercises it through Function() —
7 unit tests covering empty anchors, outside-block stays, fenced-code
relocation, table relocation, deeply-nested list relocation, mixed
inside/outside, and shape contract.

Trace: SC6, research/04 Dim 3 (Notion block-level fallback), plan-critic
major #6 (DOM-vs-no-DOM contradiction resolved via pure-function design).
2026-05-10 17:04:27 +02:00
3973be2a90 feat(voyage): sync browser-side anchor-parser regex with Node-side allowlist
Step 16 of v4.3 playground plan. Mirror lib/parsers/anchor-parser.mjs
ANCHOR_LINE_RE + ATTR_RE + ID_RE constants verbatim into voyage-playground.html
inline-script (file COLON COLON  scheme compat — no ES-module). parseAnchor(line)
validates id matches ANN-NNNN, target non-empty, line positive integer,
snippet ≤80c, intent in {fix,change,question,block}.

Trace: SC6, research/02 Sec T4, plan-critic blocker B4 + scope-guardian DEP-3.
Cross-file regex sync verified by static-grep test.
2026-05-10 17:01:14 +02:00
946eb7ab0f feat(voyage): implement drill-down + back-nav + URL-parameter project
Step 15 (v4.3 Sesjon 3 — Wave 3) — wires the dashboard fleet-tiles
to a drill-down view with breadcrumb update + back-to-dashboard
navigation + browser back/forward restoration via popstate.

renderArtifactDetail(artifactKey) renders the chosen artifact into
the #voyage-detail slot using renderPageShell + renderArtifact:
  - brief / plan / review → markdown render
  - progress              → JSON pretty-print in <pre>
  - research              → list of all research-briefs
  - missing entry         → "Artifact mangler" placeholder

Click delegation on .fleet-tile[data-artifact] triggers detail render
+ pushDetailURL (?artifact=<key>); data-action=back-to-dashboard
returns to the dashboard view + pushDashboardURL. Topbar breadcrumb
gets a third segment for detail views.

URL-parameter deep-linking: at page-load, ?project=<basePath>
surfaces a guide-panel hint explaining that webkitdirectory requires
a user-gesture; the hint links to the same Last prosjektmappe button
that wireProjectLoader already exposes. popstate handler restores
the view-state on browser back/forward (no-op when no project loaded).

Test additions (4): renderArtifactDetail function, URLSearchParams
presence, data-action=back-to-dashboard attribute, popstate listener.
2026-05-10 16:46:13 +02:00
a479f47b4e feat(voyage): implement dashboard via fleet-grid + fleet-tile with status vocabulary
Step 14 (v4.3 Sesjon 3 — Wave 3) — adds renderDashboard pipeline that
turns a ProjectArtifacts struct (produced by loadProjectDirectory in
Step 13) into a fleet-grid of fleet-tiles, one per artifact-type
(brief / plan / review / research / progress).

Status vocabulary: complete, in-progress, blocked, missing, stale
Severity mapping: missing → critical, blocked → high, in-progress
+ stale → medium, complete → low. Severity drives DS color tokens
via [data-severity] attribute selectors.

When loadProjectDirectory completes, dashboard takes over the main
stage (paste-flow elements hidden); topbar updates with project
breadcrumb. Step 13's pipeline already calls renderDashboard via
graceful-fallback, so wiring is automatic.

Test additions (4): fleet-grid + fleet-tile presence, renderDashboard
function declaration, status vocabulary completeness.
2026-05-10 16:43:22 +02:00
64e27d875a test(voyage): re-localize skip-link assertions to Norwegian (Step 10) 2026-05-10 16:20:53 +02:00
a24c3d1e3b fix(voyage): repair plan-determinism test reference path [skip-docs] 2026-05-10 10:21:52 +02:00
de160d7da1 docs(voyage): CLAUDE.md + README + CHANGELOG + annotation-quickstart + late doc-consistency pins — v4.2 Step 13 2026-05-09 15:41:45 +02:00
6d57314937 docs(voyage): pin Handover 8 + templates + PIPELINE_COMMANDS update — v4.2 Step 12 2026-05-09 15:36:15 +02:00
97b6f5406e feat(voyage): add export flow + A11Y baseline — v4.2 Step 11 [skip-docs]
Closes Wave 2 (Steps 6-11) of v4.2 implementation. Playground now
delivers the complete annotation pipeline: render -> create gestures
-> sidebar -> export.

Export flow:
  - 'Eksporter batch' button in sidebar export-bar
  - Export modal with role="dialog" aria-modal="true"
  - Generated /trekrevise command-string ready to copy
  - Two paths:
      navigator.clipboard.writeText (modern) with execCommand('copy')
      legacy fallback for cross-browser support
      Blob + URL.createObjectURL download as annotated-{brief|plan|review}.md
  - buildAnnotatedMarkdown injects voyage:anchor comments above target
    lines (mirrors lib/parsers/anchor-parser.mjs addAnchors() behaviour)

Resolve-til-arkiv (Google Docs pattern, per research-06):
  - Post-export marks pending drafts as exported (NOT delete)
  - Tab 2 ('Alle revisjoner') surfaces history with revision-stamp
  - aria-live='polite' toast announces export status

A11Y baseline (per research-06 + llm-security A11Y-RAPPORT.md):
  - aria-live='polite' toast region (Step 1)
  - Skip-to-main link (.visually-hidden + #main target)
  - role='dialog' + aria-modal='true' on form modal (Step 9)
                                    on export modal (Step 11)
  - role='tablist' / role='tab' / aria-selected / tabindex roving (Step 10)
  - aria-controls + aria-labelledby on tabpanels
  - aria-pressed on intent buttons (radiogroup-like)
  - aria-expanded + aria-controls on sidebar FAB
  - aria-hidden='true' on decorative SVG paths
  - aria-label on icon-only buttons
  - .visually-hidden labels for textarea + clipboard helper

Test coverage: tests/playground/voyage-playground.test.mjs +4 cases —
aria-live='polite', Skip to main, Blob, clipboard.writeText.

Verify: node --test tests/playground/voyage-playground.test.mjs ->
22 pass / 0 fail.
Full npm test: 596 pass / 0 fail / 2 skipped (Docker).

Refs plan.md Step 11 + research-06 + llm-security A11Y baseline.
2026-05-09 15:27:01 +02:00
125bfb02b2 feat(voyage): add playground sidebar with tabs + critique-card-list — v4.2 Step 10 [skip-docs]
Right-collapsible sidebar (320px) with 40px icon-rail when collapsed
(per critical decision #4 + research-06):

- 2-state FAB toggle (aria-expanded toggles aria-hidden on aside)
- Visible draft-count badge on FAB (mitigates 'forgot to export' friction)
- Two tabs:
    'Denne planen (N drafts)' — pending annotations
    'Alle revisjoner (M historiske)' — exported (Step 11 wires this)
- role="tablist" + role="tab" + aria-selected + tabindex roving
- ArrowLeft/ArrowRight keyboard nav between tabs
- .findings list of .critique-card per annotation
- Click on critique-card scrolls to anchor + .lint-annotation-glow
  1s pulse animation
- Sort-by-location (Hypothes.is pattern; line ASC)

Card visual: intent-badge (color-coded fix=green/change=blue/question=yellow/block=red),
ANN-NNNN ID, snippet preview, comment, exported-status.

Layout: main shifts margin-right: 320px above 1024px viewport so the
sidebar doesn't overlap the rendered artifact.

saveModalAsAnnotation + mountRender hooks now refresh the sidebar so
new drafts appear immediately and re-render preserves visibility.

Test coverage: tests/playground/voyage-playground.test.mjs +2 cases —
role="tablist", tabindex.

Verify: node --test tests/playground/voyage-playground.test.mjs ->
18 pass / 0 fail.
Full npm test: 592 pass / 0 fail / 2 skipped (Docker).

Refs plan.md Step 10 + critical decision #4 + research-06.
2026-05-09 15:25:01 +02:00
a7a6a53686 feat(voyage): add three annotation creation gestures + form modal — v4.2 Step 9 [skip-docs]
Three creation gestures + shared form modal for the v4.2 annotation
playground (per critical decisions #2-#4 + research-06):

Gesture 1 — text-anchored adder-popup:
  - mouseup-debounce 200ms (settles selection)
  - 300ms grace before hide (Hypothes.is friction-mitigation)
  - floating .voyage-adder-popup positioned at selection-bound corner
  - click -> opens form modal with derived heading-path + line + snippet

Gesture 2 — paragraph-anchored hover-icon:
  - 24px pencil SVG injected per <p>/<li> after each render
  - opacity 0 default, opacity 1 on hover/focus-visible
  - click -> opens form modal with no snippet

Gesture 3 — page-level note:
  - .voyage-page-note-btn injected in viewport
  - click -> opens form modal with target=page

Form modal (shared):
  - role="dialog" aria-modal="true" + aria-labelledby
  - 400px right-anchored on backdrop (per critical decision #3)
  - 4 intent buttons (Fiks / Endre / Spørsmål / Block) as aria-pressed group
  - <textarea> required for comment
  - ESC + backdrop-click + Avbryt close
  - Lagre persists via saveModalAsAnnotation

Anchor-ID generation (per critical decision #2 + risk-assessor H7):
  - sequential ANN-NNNN per project+file scope
  - persisted in localStorage under voyage_ann_<project>__<file>.drafts

Test coverage: tests/playground/voyage-playground.test.mjs +3 cases —
aria-modal, ANN-, 300ms grace.

Verify: node --test tests/playground/voyage-playground.test.mjs ->
16 pass / 0 fail.
Full npm test: 590 pass / 0 fail / 2 skipped (Docker).

Refs plan.md Step 9 + critical decisions #2/#3/#4 + research-06.
2026-05-09 15:22:52 +02:00
249142df2f feat(voyage): vendor markdown-it/highlight.js + playground render-pipeline + scripts/render-artifact.mjs CLI — v4.2 Step 8 [skip-docs]
Vendored libs (locked headless via scripts/vendor-playground-libs.mjs;
plan-critic B3 — never use highlightjs.org website builder):
  - playground/lib/markdown-it.min.js           — markdown-it@14.1.0 UMD bundle
  - playground/lib/markdown-it-front-matter.min.js — markdown-it-front-matter@0.2.4 IIFE-wrapped
  - playground/lib/highlight.min.js             — highlight.js@11.11.1 (5-lang bundle:
                                                   yaml/json/javascript/bash/markdown/diff)
  - playground/lib/VENDOR-MANIFEST.json         — pin record + audit trail

scripts/vendor-playground-libs.mjs implements the reproducible
CommonJS-to-IIFE wrapping. Re-vendoring requires only:
  node scripts/vendor-playground-libs.mjs

Render pipeline in playground/voyage-playground.html (~330 LoC total):
  - inline <script src=lib/...> for the three vendored bundles
  - markdown-it init with html: true (preserves voyage:anchor comments)
  - front-matter plugin with pre-render-then-wrap pattern (research/03)
  - paste-import-row textarea + Render/Sample/Clear buttons
  - voyage-viewport region with role + aria-live for A11Y
  - localStorage key pattern: voyage_ann_<project>__<slug> (risk-assessor H7)
  - inline sample plan (mirrors annotation-plan.md fixture)

scripts/render-artifact.mjs CLI (~200 LoC) — brief SC1 + SC11:
  - reads input.md, runs same vendored pipeline server-side
  - inlines DS CSS + (URL-stripped) highlight.js into output
  - zero http://https:// URLs in output (verified by test)
  - deterministic: two invocations -> byte-identical sha256
  - default output: <input>.html next to input

Test coverage:
  - tests/scripts/render-artifact.test.mjs — 5 cases (SC1/SC11)
  - tests/playground/voyage-playground.test.mjs — +5 cases (Step 8 extension)

Verify: node --test tests/playground/voyage-playground.test.mjs
       tests/scripts/render-artifact.test.mjs -> 18 pass / 0 fail.
Full npm test: 587 pass / 0 fail / 2 skipped (Docker).

Refs plan.md Step 8 + plan-critic B3 + scope-guardian B1.
2026-05-09 15:20:17 +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
4fbc52bbb4 feat(voyage): add commands/trekrevise.md — 7th pipeline command + settings.json scope — v4.2 Step 6 [skip-docs]
Implements Phase 1-8 of /trekrevise (Handover 8 producer):
- Phase 1: parse mode + reject MULTI_ARTIFACT_NOT_SUPPORTED
- Phase 2: read source + check stale .local.bak
- Phase 3: parseAnchors + validateAnchorPlacement (no partial revisions)
- Phase 4: computeAnnotationDigest + non-additive detection
- Phase 5: revisionGuard orchestration (backup -> mutate -> validate -> rollback-on-fail)
- Phase 6: branch on outcome (applied / rolled-back / mutator-failed)
- Phase 7: optional review-gate (advisory, no auto-rollback)
- Phase 8: trekrevise-stats.jsonl + report

Frontmatter: name=trekrevise, model=opus, allowed-tools includes Read/Write/Edit/Bash/Grep/Glob.
Reuses lib/parsers/anchor-parser, lib/parsers/annotation-digest,
lib/util/markdown-write, lib/util/revision-guard, lib/validators/{brief,plan,review}.

settings.json: register new top-level scope trekrevise with
trekrevise-stats.jsonl tracking (mirrors trekplan/trekresearch shape).

Forward-pinning to keep doc-consistency invariants green:
- tests/lib/doc-consistency.test.mjs: known-scopes allowlist += trekrevise
- CLAUDE.md commands table: add /trekrevise row

Plan Step 13 owns the full README/CLAUDE.md/CHANGELOG content sync;
this commit is the implementation milestone, not the doc milestone.

Refs plan.md Step 6 + plan-critic M3.
2026-05-09 15:09:01 +02:00
2f4330265c feat(voyage): scaffold playground/ with DS vendor sync — v4.2 Step 5
- playground/voyage-playground.html: minimal skeleton (DOCTYPE, app-header, guide-panel, aria-live region, skip-to-main link). Steps 8-11 will extend with render-pipeline + creation gestures + sidebar + export.
- playground/vendor/playground-design-system/: synced via 'node scripts/sync-design-system.mjs voyage' (27 files + MANIFEST.json with source_commit + sync_date + SHA-256 per file).
- tests/playground/voyage-playground.test.mjs: 8 tests pinning HTML existence, DOCTYPE, no-external-URLs, no-marked, A11Y skip-to-main + aria-live, MANIFEST.json structure, vendored DS files present.
- shared/PLAYGROUND-MAINTENANCE.md: consumer list updated 5 -> 6 (added voyage).
2026-05-09 12:55:02 +02:00
f316cc1efa feat(voyage): add annotation-digest.mjs with canonical SHA-256 — v4.2 Step 4
Pure module computing deterministic 16-char SHA-256 prefix for annotation set.
Canonicalization: sort by id, fixed field order (id|target_artifact|target_anchor|intent|comment|timestamp), \n-join, sha256, take first 16 hex.

Brief SC4 specifies sha256-prefix; research-05 said sha1 — brief wins per Hard Rule "Brief-driven".

6 tests pass: empty digest, order-independence, intent-sensitivity, format invariant, golden value, undefined-vs-empty equivalence.
2026-05-09 12:53:36 +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