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>
This commit is contained in:
Kjell Tore Guttormsen 2026-05-12 14:05:07 +02:00
commit 916d30f63e
96 changed files with 620 additions and 14716 deletions

View file

@ -1,6 +1,6 @@
# Handover Contracts (voyage-suite local pipeline)
This document is the single source of truth for the file formats that pass between the four commands of the `trekplan` pipeline. When you fork the plugin or extend a stage, the contracts below tell you what every producer must write and what every consumer is allowed to assume.
This document is the single source of truth for the file formats that pass between the commands of the `trekplan` pipeline. There are seven handovers. When you fork the plugin or extend a stage, the contracts below tell you what every producer must write and what every consumer is allowed to assume.
For each handover, the same headings appear in the same order: **Producer**, **Consumer**, **Path conventions**, **Frontmatter schema**, **Body invariants**, **Validation strategy**, **Versioning**, **Failure modes**.
@ -16,7 +16,6 @@ Each artifact carries an explicit version field. Schema bumps are coordinated:
| `progress.json` | `schema_version` (top-level) | `"1"` |
| `review.md` | `review_version` (frontmatter) | `1.0` |
| `.session-state.local.json` | `schema_version` (top-level) | `1` (number) |
| `brief.md` / `plan.md` / `review.md` (annotated) | `revision` (frontmatter) | `0` (implicit), `1+` after `/trekrevise` |
## Breaking-change protocol
@ -37,7 +36,6 @@ Each artifact carries an explicit version field. Schema bumps are coordinated:
| 5. progress.json (resume) | `lib/validators/progress-validator.mjs` |
| 6. review → plan | `lib/validators/review-validator.mjs` |
| 7. session-state (multi-session resume) | `lib/validators/session-state-validator.mjs` |
| 8. annotation → revision | `lib/parsers/anchor-parser.mjs` + `lib/parsers/annotation-digest.mjs` (parsing) and the existing brief / plan / review validators (forward-compat — additive fields tolerated) |
Every validator exposes a CLI: `node lib/validators/<name>.mjs --json <path>` returns `{valid, errors[], warnings[], parsed}`. Errors and warnings have stable `code` fields for downstream tooling.
@ -440,96 +438,6 @@ The `next-session-prompt-validator` (`lib/validators/next-session-prompt-validat
---
## Handover 8 — annotation → revision
**Handover 8 closes the operator-feedback loop.** Where Handovers 14 flow forward (brief → research → plan → execute), Handover 5 makes execute resumable, Handover 6 routes review findings back into planning, and Handover 7 makes multi-session work survivable across fresh chats, **Handover 8 lets a human operator annotate an artifact (brief, plan, or review) inside the playground and feed those annotations back into a revision cycle without losing the original artifact's byte-for-byte content**. The pipeline becomes round-trippable: a single artifact can be annotated, revised, and re-rendered repeatedly, each revision recorded with a deterministic digest in frontmatter.
**Producer:**
- The operator (manual step) — open the artifact in `playground/voyage-playground.html`, drag-select or hover-to-anchor, fill comment + intent in the modal, click "Eksporter batch" to copy the `/trekrevise` invocation to clipboard.
- `/trekrevise --project <dir>` (Phase 3 — apply) — consumes the pasted batch, writes anchor comments back into the target artifact, increments `revision:`, appends entries to `source_annotations:`, and computes a fresh `annotation_digest`.
**Consumer:**
- Subsequent `/trekplan --project <dir>` if the revised brief or plan needs further re-planning.
- Subsequent `/trekexecute --project <dir>` if the revised plan is ready for execution.
- All existing validators (brief, plan, review) — they tolerate the additive frontmatter fields without version bumps.
**Path conventions:**
- The annotated artifact is the same canonical file in `{project_dir}/` (`brief.md`, `plan.md`, `review.md`). Revisions are *in-place* — no `plan-revN.md` shadow files. Audit trail lives in git commits + the `revision:` counter + `source_annotations:` list inside frontmatter.
- The playground itself lives at `playground/voyage-playground.html` (single self-contained file with vendored `markdown-it` + `highlight.js` under `playground/lib/`).
**Frontmatter schema (additive — applies to brief.md, plan.md, review.md):**
| Field | Type | Required | Allowed values | Notes |
|---|---|---|---|---|
| `revision` | number | optional | `0`, `1`, `2`, … | Absent = `0` (forward-compat). Incremented by `/trekrevise` on each apply |
| `source_annotations` | list | optional | block-style YAML list of dicts | Absent = empty. Audit trail of every annotation that has been folded into this artifact |
| `annotation_digest` | string | optional | first 16 hex chars of canonical SHA-256 over the sorted `source_annotations` array | Absent = no annotations applied yet. Deterministic — re-applying the same set yields the same digest |
| `revision_reason` | string | optional | free-form text | **Required only** when the revision is non-additive (e.g. removed scope, replaced an SC). Operators are encouraged to fill it for any revision |
Each entry in `source_annotations` is a YAML dict:
```yaml
source_annotations:
- id: ANN-0001
target_artifact: plan.md
target_anchor: step-3
intent: change # change | add | remove | clarify | risk
comment: "consider rolling back if cache_creation jumps >10%"
line: 412
```
**Anchor format (block-level HTML comments inside the body):**
```html
<!-- voyage:anchor id="ANN-0001" target="plan.md#step-3" line="412" -->
```
Anchors are placed **only at block boundaries** — not in list items, not mid-paragraph, not at line-start collision points where a markdown parser might fold them into surrounding content. The playground enforces this discipline via `validateAnchorPlacement()` in `lib/parsers/anchor-parser.mjs`. Anchors survive markdown rendering as comments (no visible artifact) and round-trip through `parseAnchors → addAnchors → stripAnchors` byte-identically (SC2 contract).
**Body invariants:**
- The artifact's existing required sections remain untouched. `/trekrevise` writes anchor comments only at block boundaries declared by the operator's annotations.
- After each apply: byte-identical content outside anchor blocks. Stripping all anchors with `stripAnchors()` MUST yield the artifact's original body modulo the operator's deliberate prose edits.
**Validation strategy:**
| Layer | When | What |
|---|---|---|
| `revision` shape | every read | Number (or absent → treat as `0`). Negative or non-integer rejected by validator |
| `source_annotations` shape | every read | Array of dicts; each dict must include `id`, `target_artifact`, `target_anchor`, `intent`. Other fields tolerated (drift-WARN) |
| `annotation_digest` shape | every read | 16-char lowercase hex; presence requires `source_annotations` non-empty |
| Anchor placement | `/trekrevise` Phase 2 (validate) | `validateAnchorPlacement()` rejects in-list / mid-paragraph / line-start anchors before write |
| Round-trip integrity | `/trekrevise` Phase 4 (post-write) | `stripAnchors()` of the just-written file MUST equal the pre-write body |
The parsers (`lib/parsers/anchor-parser.mjs`, `lib/parsers/annotation-digest.mjs`) are pure functions — no I/O, fully unit-tested. The existing brief / plan / review validators in `lib/validators/` already tolerate the new optional fields (forward-compat — see Step 12 manifest pins).
**Forward-compat — additive principle:** Artifacts without any of the four new fields validate as `revision: 0` with empty annotations. This means every brief / plan / review written before v4.2 remains valid without migration. Producers (operators using `/trekrevise`) opt into the schema by writing the fields; consumers tolerate their presence or absence equally.
**Idempotence:** Re-applying the same annotation batch on the same artifact (same anchors, same intents, same comments) yields the same `annotation_digest` and no body diff outside anchor refresh. This is enforced by `computeAnnotationDigest()` canonicalizing the sorted `source_annotations` array before hashing — operators can replay a batch safely, and the test suite pins this with `tests/parsers/annotation-digest.test.mjs`.
**Versioning:** No `*_version` bump for v4.2 — the four new fields are additive. A future schema break (e.g. removing `target_anchor` in favor of structured pointer) would bump `brief_version` / `plan_version` / `review_version` per the breaking-change protocol.
**Failure modes:**
- `ANNOTATION_PLACEMENT_INVALID``/trekrevise` Phase 2 halts; operator re-anchors via playground
- `ANNOTATION_DIGEST_DRIFT` → digest computed at apply-time differs from operator's expected digest in the pasted batch; halt with mismatch report (suggests the batch was hand-edited after export)
- `ANNOTATION_ROUNDTRIP_FAIL` → post-write `stripAnchors()` does not yield the original body; rollback restores the byte-identical pre-write file from `*.local.bak`
- Parser failures from `parseAnchors()` produce `{valid: false, errors: [...]}` and `/trekrevise` halts before any write. The atomic-write pattern (tmp-file + rename) guarantees the canonical file is never partially updated.
### § Lifecycle
The annotation cycle has three stages — no persistent state file (unlike Handover 7), only the artifact frontmatter and git history record what happened:
| Stage | Owner | Action |
|---|---|---|
| Annotate | Operator + playground UI | Open artifact in `playground/voyage-playground.html`, anchor + comment, click "Eksporter batch" → clipboard contains a `/trekrevise --project <dir> --apply '{...JSON...}'` command |
| Apply | `/trekrevise` | Phase 1 parse the pasted batch, Phase 2 validate placement, Phase 3 atomically write anchors + frontmatter (`revision: N+1`, append to `source_annotations`, recompute `annotation_digest`), Phase 4 round-trip integrity check, Phase 5 commit |
| Re-render | Playground (next open) | The freshly-revised artifact loads with anchors visible as inline comment markers; operator can iterate or call `/trekplan` / `/trekexecute` on the revised file |
**Single-iteration MVP (v4.2 scope):** Each operator annotation batch produces one `revision:` increment. Multi-iteration loops (e.g. revise → re-review → revise again without operator intervention) are deferred indefinitely — the brief's SC4 wording is single-revision, and operator validation that multi-iteration is desired has not been collected (see plan Alternatives table). The single-iteration MVP keeps the audit trail unambiguous: one `revision:` bump per `/trekrevise` invocation.
**Stale-anchor principle:** Unlike `.session-state.local.json`, anchors are durable and intentionally retained — they document *why* a revision happened. There is no `--cleanup` for anchors. Removing them is a manual operator decision, executed via the playground "Strip all anchors" affordance or hand-editing the source. `/trekrevise` does not remove anchors automatically.
---
## Stability summary
| Handover | Validation strength | Owner | Risk |
@ -541,6 +449,5 @@ The annotation cycle has three stages — no persistent state file (unlike Hando
| 5. progress.json | shape + resume readiness | this plugin | medium — drift during compaction handled by pre-compact-flush hook (CC v2.1.105+) |
| 6. review → plan | strict at write, soft at read | this plugin | low — additive feedback loop; consumer falls back gracefully when source_findings is absent |
| 7. session-state (multi-session resume) | required-fields + status enum + drift-WARN extras | this plugin | low — readers tolerate unknown keys; writers are owned by trekexecute Phase 8 + helper command |
| 8. annotation → revision | parser-strict at write (placement + round-trip), validator-soft at read (additive fields) | this plugin | low — additive frontmatter; existing artifacts validate as `revision: 0` without migration |
When extending the plugin or adding a new pipeline stage, follow the same pattern: produce an artifact with a versioned frontmatter (or `schema_version` for JSON), write a validator under `lib/validators/`, add fixtures under `tests/fixtures/`, and add an entry to this document.

View file

@ -1,55 +0,0 @@
# Annotation playground — quickstart
The `/trekrevise` command and the `playground/voyage-playground.html` page
let you annotate any voyage artifact (`brief.md`, `plan.md`, or `review.md`)
and fold the annotations back in-place with a deterministic audit trail.
This is **Handover 8** in `docs/HANDOVER-CONTRACTS.md`. For schema details,
read that document first.
## Hands-on with the example fixture
The plugin ships a canonical fixture at
`tests/fixtures/annotation/annotation-example.md` that is the same shape an
operator would annotate. Use it to verify your playground works before
touching a real project.
## Seven steps from artifact to revised file
1. **Open the playground.** Open `plugins/voyage/playground/voyage-playground.html`
in any modern browser. No build, no server, no network calls — the page
ships vendored `markdown-it` and `highlight.js` under `playground/lib/`.
2. **Paste the artifact content.** Copy the full body of your `brief.md`,
`plan.md`, or `review.md` (including frontmatter) into the textarea on
the left. The right pane renders the markdown live. For larger artifacts
you can also generate the rendered HTML offline with
`node plugins/voyage/scripts/render-artifact.mjs <path> --out /tmp/x.html`.
3. **Anchor a comment.** Drag-select text inside a paragraph, or hover a
block-level element and click the anchor button that appears. The
playground refuses anchors inside list items, mid-paragraph, or at
line-start collision points — only block boundaries are valid (per the
placement discipline in `lib/parsers/anchor-parser.mjs`).
4. **Fill the modal.** Choose an intent (`change`, `add`, `remove`,
`clarify`, or `risk`), write your comment, save. Repeat for every
anchor you want in this batch. The sidebar shows your batch growing as
a critique-card-list.
5. **Export the batch.** Click "Eksporter batch". The playground copies a
complete `/trekrevise --project <dir> --apply '{...JSON...}'` invocation
to your clipboard. The JSON encodes every anchor, intent, and comment.
6. **Apply via `/trekrevise`.** Paste the command in your Claude Code chat.
The command parses + validates the batch, atomically writes anchor
comment blocks back into the source artifact, increments `revision:`,
appends entries to `source_annotations:`, and recomputes
`annotation_digest`. Body content outside anchor blocks remains
byte-identical.
7. **Verify and iterate.** Re-open the revised file in the playground to
see anchors as inline comment markers. If you want another revision
pass, repeat from step 3 — each batch produces one `revision:` bump.
Single-iteration MVP per research-05; multi-iteration loops are
deferred.

View file

@ -1,129 +0,0 @@
# SC1 10-element Checklist Verification — voyage v4.3 playground
**Verifisert:** 2026-05-10 (Sesjon 6, Wave 7)
**Referanse:** `plugins/llm-security/playground/llm-security-playground.html` (visuell paritet)
**Metode:** Static-grep (Group A tester, Step 28) + manuell side-by-side + Playwright pixel-diff (Step 30)
**Status:** 8 av 10 PASS ved bokstavelig criteria, 2 redefinert per scope-guardian Assumptions (operatør-sign-off)
## Sammendrag
| # | Element | Status | Evidens |
|---|---------|--------|---------|
| 1 | Header + breadcrumb | PASS | Group A test SC1.1; `app-header__breadcrumb` + `aria-label="Brødsmuler"` |
| 2 | Breadcrumb retur-bane | PASS | Group A test SC1.2; `breadcrumb-click` handler i `voyage-playground.html:1830` |
| 3 | Theme bootstrap IIFE | PASS | Group A test SC1.3; `data-theme` + `prefers-color-scheme` IIFE i HTML head |
| 4 | Onboarding-grid (redefinert) | PASS | Group A test SC1.4 + Group B fleet-grid CSS parity-test (99707f51) verifiserer at vendored DS `components-tier3-supplement.css` `.fleet-grid` block fortsatt har `grid-template-columns: repeat(4, 1fr)` + `gap: var(--space-3)`. Voyage bruker `fleet-grid` + `fleet-tile` istedenfor onboarding-grid (scope-guardian SC-GAP-1, Assumption #21). |
| 5 | A11Y-panel | PASS | Group A test SC1.5; `guide-panel--info` + `key-stats` + `findings__items` (Wave 5 Step 22) |
| 6 | Screenshots-spor | PASS | Group D test SC1.6; `loadProjectDirectory` leser `docs/screenshots/**/*.png` via `readAsDataURL` (2 MB cap) og `renderScreenshotGallery` mounter en `<figure>`-grid i dashboardet. Erstatter scope-guardian SC-GAP-2 PASS-redef med faktisk inline gallery (v4.3 Step 8, finding 31d28f65). |
| 7 | Body typografi | PASS | Group A test SC1.7; `var(--font-size-*)` + `var(--font-family-mono)` brukt gjennomgående |
| 8 | Spacing rhythm | PASS | Group A test SC1.8; ≥5 distinkte `var(--space-N)` referanser |
| 9 | Color-token fidelity | PASS | Group A test SC1.9; `badge--scope-voyage` + `--color-scope-voyage` brukt |
| 10 | Dark-mode parity | PASS | Group A test SC1.10; `data-theme="dark"` default + persistens via `voyage-theme` localStorage |
**Nettoresultat:** 10/10 verifisert (8 bokstavelig + 2 redefinert med operatør-sign-off i Assumptions #21 og #22). Ingen revisjons-loop til Wave 1-5 utløst.
## Detaljer per element
### Element 1 — Header med breadcrumb
- **Bokstavelig krav:** sticky topbar med breadcrumb-navigasjon
- **Implementering:** `class="app-header__breadcrumb"` + `aria-label="Brødsmuler"` i `renderTopbar` (Step 8)
- **Test:** `tests/playground/voyage-playground.test.mjs``SC1.1 header — app-shell topbar with breadcrumb`
- **Status:** PASS
### Element 2 — Breadcrumb klikkbar retur-bane
- **Bokstavelig krav:** klikk på "Dashboard" i breadcrumb returnerer til hovedoversikt
- **Implementering:** `breadcrumb-click` handler renderer dashboard-fleet-grid via `renderDashboard()`
- **Test:** `SC1.2 breadcrumb — clickable returns to dashboard`
- **Status:** PASS
### Element 3 — Theme bootstrap IIFE
- **Bokstavelig krav:** sync IIFE i `<head>` setter `data-theme` før første paint, leser bruker-pref + OS-pref
- **Implementering:** Linje 22-26 i HTML; sjekker `localStorage('voyage-theme')``prefers-color-scheme: dark` matchMedia → fallback `dark`
- **Test:** `SC1.3 theme bootstrap — IIFE sets data-theme + colorScheme`
- **Status:** PASS
### Element 4 — Onboarding-grid (REDEFINERT)
- **Bokstavelig krav:** llm-security har 4 onboarding-tiles (Quickstart / Latest scan / Threats / Roadmap)
- **Voyage-tolkning:** voyage-domain har én plugin uten onboarding-flow. Redefinerer onboarding-grid som "fleet-grid + recommendation-card-pattern matcher samme grid-system" (Alternatives Considered, plan.md linje 1081)
- **Implementering:** `fleet-grid` med `fleet-tile`-children, én tile per artifact (brief/plan/research/review)
- **Test:** `SC1.4 onboarding-grid equivalent — fleet-grid pattern` + Group B `SC1.4 fleet-grid CSS parity vs vendored DS (99707f51)` som verifiserer at `components-tier3-supplement.css` `.fleet-grid` block fortsatt har `grid-template-columns: repeat(4, 1fr)` + `gap: var(--space-3)`
- **Status:** PASS (fleet-grid CSS parity verifisert via Group B strukturell test mot vendored DS; scope-guardian SC-GAP-1 lukket via /trekreview Sesjon 13, Assumption #21 operatør-sign-off står)
### Element 5 — A11Y-panel
- **Bokstavelig krav:** tilgjengelighets-panel viser severity-counters + findings-list
- **Implementering:** `guide-panel guide-panel--info` container med `key-stats` severity-grid + `findings__items` ordered list. `wireA11yToggle` kobler topbar-button til toggle. Built fra DS-primitives (Wave 5 Step 22)
- **Hooks:** `window.__voyage.scheduleRender({ a11yViolations })` populerer panelet fra Playwright-spec
- **Test:** `SC1.5 A11Y panel — guide-panel--info + key-stats + findings`
- **Status:** PASS
### Element 6 — Screenshots-spor
- **Bokstavelig krav:** llm-security har inline gallery med thumbnail-grid for case-studies
- **Voyage-implementering (v4.3 Step 8, finding 31d28f65):** `loadProjectDirectory` detekterer `docs/screenshots/**/*.png` via path-prefix-match, leser dem via `FileReader.readAsDataURL()` med 2 MB per-bilde cap (overskridelse annonseres via aria-live). `renderScreenshotGallery(screenshots)` bygger en `<figure>`-grid med `<img src="<dataUrl>" alt="<filename>">` som mountes under `fleet-grid` i `renderDashboard`. Erstatter den tidligere PASS-redef-tolkningen — voyage har nå et faktisk inline gallery.
- **Beholder:** `window.__voyage` hooks fra Wave 5 Step 23 (`navigate`, `scheduleRender`, `getProjectArtifacts`) + `docs/screenshots/README.md` mappe-konvensjon — fortsatt brukt av Playwright-spec og av operatør for manuell screenshot-prosedyre.
- **Test:** Group D Playwright-test `SC1.6 inline gallery — data:image PNGs rendered (31d28f65)` injiserer en fixture-artifact via `scheduleRender` og asserter `#voyage-dashboard img[src^="data:image/png"]` count > 0.
- **Status:** PASS (inline gallery implementert; scope-guardian SC-GAP-2 lukket).
### Element 7 — Body typografi
- **Bokstavelig krav:** typografi-skala bruker DS-tokens, ikke literal pixel-verdier
- **Implementering:** `var(--font-size-{xs,sm,md,lg,xl})` + `var(--font-family-mono)` brukt gjennomgående. Wave 1 Step 5 fjernet literal pixel font-sizes.
- **Test:** `SC1.7 body typography — DS font-size + family tokens`
- **Note:** En enkelt `font-size: 0.7rem` literal eksisterer i HTML (linje ~361) — dokumentert i Wave 1; ikke blocker.
- **Status:** PASS
### Element 8 — Spacing rhythm
- **Bokstavelig krav:** ≥5 distinkte `--space-N` token-referanser for konsistent spacing
- **Implementering:** Verifisert via grep-count i Group A test
- **Test:** `SC1.8 spacing rhythm — DS --space-N tokens used`
- **Status:** PASS
### Element 9 — Color-token fidelity
- **Bokstavelig krav:** voyage-scope tokens definert + brukt
- **Implementering:** Wave 0 Step 1 la til `--color-scope-voyage` + `badge--scope-voyage` i DS `base.css`. Brukt i playground via `class="badge badge--scope-voyage"` i topbar.
- **Test:** `SC1.9 color-token fidelity — voyage-scope tokens + DS colors`
- **Status:** PASS
### Element 10 — Dark-mode parity
- **Bokstavelig krav:** dark mode er førsteklasses borger (ikke lag-på), default-teme følger system-pref
- **Implementering:** `<html data-theme="dark">` som default; bootstrap-IIFE respekterer `voyage-theme` localStorage, deretter `prefers-color-scheme: dark`, deretter fallback `dark`
- **Test:** `SC1.10 dark-mode parity — explicit dark default + bootstrap`
- **Pixel-diff baseline:** `tests/e2e/snapshots/voyage-playground-dark.png` (Step 30 spec)
- **Status:** PASS
## Baseline-screenshots (Playwright)
Etablert via `npx playwright test --update-snapshots` 2026-05-10. Lagret under
`tests/e2e/snapshots/`:
- `voyage-playground-light.png` (1280×900, light theme)
- `voyage-playground-dark.png` (1280×900, dark theme)
Pixel-diff-spec (`tests/e2e/voyage-playground-a11y.spec.mjs`) sammenlikner mot
disse baselines med `maxDiffPixelRatio: 0.02`. Avvik > 2% utløser test-fail.
## Kjente WCAG-violations (baseline'd, defer til v4.4)
Wave 7 = VERIFICATION ONLY. HTML er FROZEN i Sesjon 6 — ingen fix. Existing
critical/serious violations er recorded i `tests/e2e/snapshots/a11y-baseline.json`
som delta-baseline:
```json
{
"light": { "aria-hidden-focus": 1, "color-contrast": 4 },
"dark": { "aria-hidden-focus": 1 }
}
```
- **`aria-hidden-focus`** (1 forekomst, light + dark): aria-hidden element inneholder fokuserbart innhold
- **`color-contrast`** (4 forekomster, kun light theme): `.key-stat--critical > .key-stat__label` har kontrast 3.85:1 (WCAG AA krever 4.5:1)
**Defer-rationale:** v4.3 brief Wave 7 mandat er `test_strategy.coverage`-utvidelse, ikke
WCAG-fixing. v4.4 skal unfreeze HTML og adressere disse via DS-token-justering.
## Verifikasjon-sjekkliste (Steg-for-steg)
- [x] Group A static-grep tester (Step 28) passerer alle 17 SC1-elementer
- [x] Manuell side-by-side mot llm-security-playground (operatør, dette dokument)
- [x] Playwright PNG-baselines etablert for light + dark theme
- [x] axe-core delta-baseline JSON committed
- [x] Pixel-diff spec passerer 2% terskel
- [x] WCAG-violations dokumentert som baseline (defer til v4.4)

View file

@ -1,89 +0,0 @@
# Voyage playground screenshot convention (v4.3)
This directory holds reference screenshots for the Voyage playground
(`playground/voyage-playground.html`). They serve as a visual baseline for
manual review of design changes and as fixtures for the Wave 7 axe-core
Playwright spec (`tests/e2e/voyage-playground-a11y.spec.mjs`).
Screenshots are NOT auto-committed. The `tests/e2e/snapshots/` directory
(produced by Playwright in Wave 7) is the test-baseline; this directory
holds curated illustrative captures for documentation.
## Mappestruktur
```
docs/screenshots/
README.md ← this file
dashboard/ ← project-dashboard fleet-grid views
artifact-detail/ ← drill-down render of brief/plan/review
annotation/ ← gutter-badge + sidebar + active highlight states
dark-mode/ ← screenshots taken with html[data-theme="dark"]
light-mode/ ← screenshots taken with html[data-theme="light"]
```
Each subfolder contains PNGs named by feature + variant, e.g.
`dashboard/fleet-grid-with-progress-tile.png`,
`annotation/active-anchor-yellow-tint.png`.
## Hooks (window.__voyage)
The playground exposes three automation hooks for headless screenshot
scripts. They are namespaced under `window.__voyage` to avoid global
pollution:
| Method | Purpose |
|--------|---------|
| `window.__voyage.navigate(surface)` | Switch surface. `surface ∈ ['dashboard', 'detail', 'render', 'a11y']` |
| `window.__voyage.scheduleRender(state)` | Pre-populate state. `state.markdown` triggers `mountRender`; `state.artifacts` triggers `renderDashboard` |
| `window.__voyage.getProjectArtifacts()` | Returns the last-loaded `ProjectArtifacts` object (or `null`) |
The hook surface mirrors the pattern used in
`plugins/llm-security/playground/llm-security-playground.html`
(`window.__navigate`, `window.__scheduleRender`).
## Producing screenshots — Playwright headless (Wave 7+)
Wave 7 ships a Playwright spec
(`tests/e2e/voyage-playground-a11y.spec.mjs`). Reuse its setup for
screenshots:
```js
// tests/e2e/snapshot-helper.mjs (NOT committed in v4.3 — illustrative)
import { test } from '@playwright/test';
test('dashboard with all artifacts', async ({ page }) => {
await page.goto('file://' + process.cwd() + '/playground/voyage-playground.html');
await page.evaluate(() => {
window.__voyage.scheduleRender({
artifacts: {
basePath: 'demo',
brief: { path: 'brief.md', content: '...' },
plan: { path: 'plan.md', content: '...' },
review: null,
research: [],
architecture: { overview: null, gaps: null, looseFiles: [] },
progress: null,
looseFiles: [],
}
});
});
await page.screenshot({ path: 'docs/screenshots/dashboard/fleet-grid-demo.png' });
});
```
## Producing screenshots — manuelt
1. Åpne `playground/voyage-playground.html` i Chrome/Firefox.
2. Hvis du trenger et bestemt UI-state, paste relevant artifact-innhold i
textarea og trykk Render.
3. macOS: `Cmd+Shift+4`, deretter `Space` for vindu-skjermbilde.
4. Lagre med beskrivende filnavn under riktig undermappe ovenfor.
## Konvensjon for nye screenshots
- Bruk PNG, ikke JPG (lossless for tekst-tunge UIer).
- Kjør i konsistent viewport (1280×800 anbefales for desktop-baseline).
- Ta dark- og light-mode-varianter parvis når komponenten har visuelle
forskjeller mellom temaene.
- Ikke commit screenshots med personidentifiserende info eller hemmelig
artifact-innhold.