Commit graph

618 commits

Author SHA1 Message Date
c08bde0649 fix(voyage): sanitize bodyHtml via DOMPurify in renderArtifact (1d3591d4) 2026-05-10 21:14:50 +02:00
6eaa230953 fix(voyage): sync package-lock.json version to 4.3.0 (d94dfaf1) 2026-05-10 21:05:33 +02:00
36d0e97da7 revert(voyage): remove three-tier-context scope creep (1e8d2bf2) 2026-05-10 21:05:05 +02:00
5f26de2f0d fix(voyage): inject plan_critic via Phase 9 readAndUpdate (906f155d) 2026-05-10 21:03:54 +02:00
13a83e5a95 docs(marketplace): update root README with voyage v4.3
Step 34 of v4.3 plan — Wave 8 docs (3-doc-niv mandate, marketplace landing):

- Voyage card bumped from v4.2.0 to v4.3.0
- New v4.3.0 paragraph above v4.2.0 paragraph: dashboard-sentrisk
  rebuild, file://-loader (webkitdirectory + drag-drop + URL-parameter),
  anchor-rendering modent (block-boundary + parseAnchor sync + gutter-
  badge + sidebar-rail + J/K keyboard), A11Y panel fra DS-primitives,
  screenshots-spor convention, path-traversal filter, DOMPurify-vendoring,
  test pyramid Groups A-D (672 -> 705 pass), WCAG-baseline'd defer-til-v4.4.

Cross-plugin-boundary commit: koordinert med plugins/voyage/CLAUDE.md +
README.md + CHANGELOG.md + package.json (Step 33).
2026-05-10 18:30:54 +02:00
ea4005960c docs(voyage): bump v4.3.0 + update CLAUDE.md + README.md + CHANGELOG.md
Step 33 of v4.3 plan — Wave 8 docs (3-doc-niv mandate):

- package.json: version 4.2.0 -> 4.3.0
- CHANGELOG.md: v4.3.0 entry dated 2026-05-10 with Added/Changed/Fixed/
  Deferred-to-v4.4/Notes covering Wave 0-8 leveranser (dashboard-sentrisk
  layout, file://-loader 3 entry points, anchor-rendering modent, A11Y
  panel fra DS-primitives, screenshots-spor convention, DOMPurify ven-
  doring, voyage-scope DS-tokens, test pyramid Groups A-D, Playwright
  devDeps).
- CLAUDE.md: nytt 'Playground (v4.3)' avsnitt under Architecture som
  beskriver dashboard-modell, file-loader, anchor-rendering, A11Y panel,
  screenshots-spor, security hardening, og test pyramid.
- README.md: version badge 3.4.1 -> 4.3.0; '/trekrevise — Annotation
  playground' avsnitt utvidet med v4.3 rebuild-detaljer og pekere til
  playground/README.md + sc1-checklist-verification.md.

Test count: 705 pass / 0 fail / 2 skipped (npm test).
doc-consistency.test.mjs: 42/42 pass.
2026-05-10 18:29:45 +02:00
12637f3dbd docs(voyage): write playground/README.md with v4.3 architecture + URL-parameter
Step 32 of v4.3 plan — Wave 8 docs:
- v4.3 dashboard-sentrisk arkitektur (fleet-grid + drill-down)
- Bruksveier: webkitdirectory-velger, drag-drop, URL-parameter ?project=
- Discoverability for .claude/projects/ (open/xdg-open/explorer)
- Annotation-flow + window.__voyage hooks
- Begrensninger (FF150-Win, no FSA, design/ out-of-scope, baseline'd WCAG)
- Bundle-strrelse breakdown
- Test-suite oversikt (Groups A-D)

NEW file (eksisterte ikke i v4.2 per plan-critic finding 13).
2026-05-10 18:25:37 +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
b88c120680 feat(voyage): add Playwright + @axe-core/playwright devDependencies
Step 27 of v4.3 plan — adds Playwright 1.59.1 + axe-core/playwright 4.10.2
as devDependencies, npm script test:e2e, and playwright.config.mjs with
file:// baseURL pointing at playground/. Chromium browser binary installed
to ~/Library/Caches/ms-playwright/.

Trace: SC2 (WCAG verifikasjon krever browser-driver), research/04
Recommendation security; brief Constraint linje 64 (zero new deps i
playground/) er OK fordi Playwright er devDependency på voyage-plugin-rot,
ikke i playground/.
2026-05-10 18:13:29 +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
68842cf773 docs(voyage): add three-tier ecosystem context brief
New docs/three-tier-context.md (110 lines) documents Voyage's position as
Tier 1 in a three-tier ecosystem with upstream consumers (app-creator,
app-factory; both currently in private incubation). The brief identifies
upstream-consumed contracts (brief.md format, /trekplan CLI, handover
schemas), prescribes stability principles, and explicitly preserves
Voyage's runtime agnosticism — no imports, no detection, no special cases.
Awareness without coupling.

CLAUDE.md § Architecture: 2-line callout pointing to the brief, following
the existing "opt-in upstream architect plugin" precedent.

No Voyage behavior change. Documentation-only.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-10 16:29:28 +02:00
64e27d875a test(voyage): re-localize skip-link assertions to Norwegian (Step 10) 2026-05-10 16:20:53 +02:00
2bf766673d feat(voyage): implement loadProjectDirectory pipeline (validate + classify + read) 2026-05-10 16:19:28 +02:00
974835537a feat(voyage): add drag-drop with webkitGetAsEntry + Firefox 150 Win guard 2026-05-10 16:18:23 +02:00
e04915882e feat(voyage): add webkitdirectory primary project-loader 2026-05-10 16:17:13 +02:00
820ebf0f02 feat(voyage): add A11Y skip-link + aria-label foundations 2026-05-10 16:16:18 +02:00
7c468097cf feat(voyage): implement page-shell pattern (eyebrow + title + lede + meta) 2026-05-10 16:15:25 +02:00
0eb56edb6d feat(voyage): implement renderTopbar with badge--scope-voyage and breadcrumb 2026-05-10 16:15:02 +02:00
109a17e044 feat(voyage): add theme toggle button + delegated handler 2026-05-10 10:29:27 +02:00
79e5609a0e feat(voyage): port theme-bootstrap IIFE from llm-security; set data-theme dark default 2026-05-10 10:28:03 +02:00
ee1f4055c9 refactor(voyage): fix CSS link-order + replace literal pixel font-sizes 2026-05-10 10:27:24 +02:00
01f31e73c3 refactor(voyage): replace non-canonical DS tokens with canonical names
Replaces --color-accent/--font-mono/--color-bg/--color-bg-hover/--color-bg-code/--color-text-muted/--color-text/--color-success/--color-border with their canonical DS-token equivalents. Drops all hex-fallbacks in var() per Step 4 spec.

Deviation note: plan's literal Verify regex "--color-bg\b" matches --color-bg-soft (canonical) due to \b dash-boundary semantics. Manifest must_contain (--color-scope-voyage) verifies; corrected intent-regex (--color-bg[^-a-z]|--color-bg$) returns empty. Treated as PASS on implementation grounds.
2026-05-10 10:26:12 +02:00
a24c3d1e3b fix(voyage): repair plan-determinism test reference path [skip-docs] 2026-05-10 10:21:52 +02:00
302e3aa42e chore(voyage): update v4.3 brief research_status to complete [skip-docs] 2026-05-10 10:18:46 +02:00
a7dec7fdee feat(ds): add voyage scope tokens + badge--scope-voyage (B-DS-4); resync vendor 2026-05-10 10:17:50 +02:00
d8c80756fe chore(voyage): bump version to 4.2.0 — annotation pipeline + first playground ship
v4.1.0 → v4.2.0. Two-file change per Step 14 manifest (package.json +
.claude-plugin/plugin.json). Description tagline expanded from
"brief, research, plan, execute, review, continue" to include "revise"
and "+ first marketplace playground".

Out-of-scope under Step 14 forbidden_paths (left at 4.1.0 intentionally):
- lib/exporters/otlp-format.mjs (VOYAGE_SCOPE_VERSION constant)
- hooks/scripts/otel-export.mjs (User-Agent header)
These constants are touched on the next bump where the constants directory
is in scope; keeping them stale for one release is acceptable since
otel/otlp telemetry is opt-in and the version field is informational.

Verification:
- node -e "import('./package.json',{with:{type:'json'}}).then(m=>console.log(m.default.version))" → 4.2.0
- jq -r .version .claude-plugin/plugin.json → 4.2.0
- npm test: 610 pass / 0 fail / 2 skipped (Docker)
- SC11 pipeline-self-eat gate: render-artifact.mjs renders own brief.md + plan.md to non-empty HTML
2026-05-09 15:43:28 +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