Compare commits

..

29 commits

Author SHA1 Message Date
69610d46bd chore: roll up in-progress changes across plugins
- claude-design: scaffold new plugin (plugin.json, CHANGELOG, README)
- llm-security: playground design-system updates (tokens, components,
  tier3 supplement, new tier4 project-view CSS)
- ms-ai-architect: v2 mockup screenshots + local screenshot script
- voyage: annotate.mjs update

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-16 21:02:23 +02:00
6c94a1629f docs: add Communication patterns section to all plugin CLAUDE.md
Standardize named-markdown-link guidance across all plugins so file://
references render as independently clickable links in terminals like
Ghostty (bare file:// URLs only make the first clickable).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-16 21:01:45 +02:00
d8882f5220 feat(ms-ai-architect): v1.15.0 — playground v3 project-view integration
Erstatter v2 project-surface (screen-tabs + category-tabs + per-command paste-cards)
med v3 renderProjectView (sidebar med 17 artifacts + main-area + import-modal overlay).
renderActive() ruter project-surface til renderProjectSurfaceV3() som wrapper
renderProjectView + topbar + app-shell.

V2-surface helt fjernet:
- renderProjectSurface (152 linjer)
- renderCommandSubCard (87 linjer)
- rehydratePasteImports (15 linjer)
- ACTIONS['project-screen'], currentProjectScreen
- 5 v2-CSS-klasser: .project-tabs, .project-tab*, .sub-zone, .paste-import-row, .project-header__*, .command-cards

Zombie-handlers beholdt for test-back-compat:
currentProjectTab, ACTIONS['project-tab'], ACTIONS['parse'],
handlePasteImport, window.__handlePasteImport. Unreachable fra v3 DOM
men nødvendige for test-playground-v3.sh + test-playground-parsers.sh.

2 fingerprint-gap lukket:
- requirements.headers: utvidet med "EU AI Act — Krav" pattern
- license.headers: utvidet med "Lisens-kapabilitetsmatrise" pattern
- KNOWN_GAP_FIXTURES = {} i test-playground-fingerprints.sh

migrateDataVersion utvidet med parserFor (3. arg):
- Demo-state med kun raw_markdown auto-parses til project.artifacts[cid]
- defaultParserFor(cmdId) resolverer PARSERS[archetypeFor(cmdId)]
- 3 bootstrap-callsites oppdatert (cold-load, import, load-demo)

Ship-QA bugfixes funnet via browser-dogfood:
- components-tier4-project-view.css lagt til i <link>-kjeden (var ikke loaded
  -> modal-overlay og two-column layout virket ikke)
- renderImportModal setter data-open="true" (DS-kontrakt for display: flex)

Bundler også sesjon 2-4 deliverables som ikke ble committed tidligere:
- shared/playground-design-system v0.6.0 (Tier 4 project-view CSS + 6 tokens)
- ms-ai-architect/playground/vendor/ re-sync til DS v0.6.0
- tests/test-playground-fingerprints.sh (sesjon 4 NY - 32 PASS)
- tests/test-playground-projectview.sh (sesjon 4 NY - 30 PASS)
- tests/test-playground-actions.sh (sesjon 4 NY - 19 PASS)
- tests/test-playground-migrations.sh utvidet (7 -> 16 PASS)
- tests/run-e2e.sh wirer alle 6 playground-suiter

Stats:
- bash tests/run-e2e.sh --playground: 386 PASS, 0 FAIL, 2 WARN (pre-eks)
- bash tests/run-e2e.sh (full): All E2E suites passed
- bash tests/validate-plugin.sh: 219 PASS

Screenshots regenerert til playground/screenshots/v1.15.0/ (24 PNG-er, 12
surfaces x 2 tema). Nye v3-surfaces: project-overview, project-artifact-*,
project-import-modal (viewport-only), project-search.

Docs oppdatert (3 nivåer): README.md (badge + version history),
CHANGELOG.md, CLAUDE.md (playground-seksjon + valideringstabell),
rot-README.md + rot-CLAUDE.md (marketplace-landingen + plugin-index).

.gitignore: ny pattern *.local.html + *.local.json for sesjon-state-filer.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-16 20:58:51 +02:00
9affdca23e chore(voyage): bump version 5.1.0 → 5.1.1 2026-05-15 16:11:55 +02:00
c1b7bad389 feat(voyage): define high-effort behavior + amend brief Non-Goal/SC1 + coordinator normalization (Decision B)
Wave 6 / Step 10 — autonomy-gated. Operator confirmed: gemini-bridge
substitution for plan-critic doubling AND SC1 amendment to
resolver-invariant encoding (decisions.local.json recorded).

- commands/trekplan.md: gemini-bridge plan-review Pass 2 on
  post-revision plan in high-effort mode (replaces fragile
  plan-critic doubling per risk-assessor).
- commands/trekresearch.md: full swarm + contrarian-researcher +
  gemini-bridge always-on.
- commands/trekreview.md: skip Pass 3 reasonableness + invoke
  coordinator normalization rule.
- commands/trekexecute.md: gates_mode = closed (strict manifest-audit,
  main-merge pauses); flag override still wins.
- agents/review-coordinator.md: Pass 3 high-effort normalization —
  substitute unknown rule_key with PLAN_EXECUTE_DRIFT, preserve
  original in original_rule_key.
- .claude/projects/2026-05-13-trekflow-solo-lane/brief.md (gitignored,
  not committed): Non-Goal amendment locks low/high tiers; SC1
  amendment authorizes resolver-invariant interpretation.
- tests/lib/doc-consistency.test.mjs: +4 pins for the
  "### High-effort behavior (v5.1.1)" heading per command.

Tests: 578 pass, 0 fail, 2 skipped (+4 from 574).

Closes #7 (operator-gated decisions captured + coordinator
normalization landed).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-15 16:07:37 +02:00
07ae1e30e9 test(voyage): refactor 4 downstream command tests to runtime SC4+SC7 (closes #2 #3 #6 #10) 2026-05-14 21:46:11 +02:00
94c696fee6 test(voyage): refactor trekbrief command test to runtime SC1 (closes #1) 2026-05-14 21:44:38 +02:00
1bb6a9d63b fix(voyage): require brief-validator gate in trekresearch + trekexecute (closes #12) 2026-05-14 21:43:45 +02:00
1f056752c1 feat(voyage): wire phase-signal-resolver into 4 downstream commands (closes #9 wiring) 2026-05-14 21:43:16 +02:00
ce162e6c41 feat(voyage): add resolvePhaseModel for brief-signal orchestrator override (closes #9 part A) 2026-05-14 21:38:51 +02:00
48e092d2bc test(voyage): add profile-resolver non-interference tests (closes #4 SC5) 2026-05-14 21:36:57 +02:00
4c85a2c22b fix(voyage): coerce brief_version to string + quote template + update doc pin (closes #8 #11)
v5.1.0 shipped with an unquoted brief_version: 2.1 in trekbrief-template.md.
parseScalar coerced it to Number 2.1, and the sequencing gate guarded on
typeof === 'string', silently bypassing BRIEF_V51_MISSING_SIGNALS.

Three-part atomic fix:
- brief-validator.mjs:87+149 now accepts both string and number forms via
  String(fm.brief_version) coercion.
- trekbrief-template.md quotes the value so new briefs parse as String.
- doc-consistency.test.mjs pins the QUOTED form going forward.

Three regression tests added in brief-validator.test.mjs.
2026-05-14 21:36:10 +02:00
a67b5717c9 test(voyage): add 4 brief fixtures for v5.1.1 runtime scenarios 2026-05-14 21:34:51 +02:00
3ed2d84caa feat(voyage): add phase-signal-resolver helper for v5.1.1 wiring 2026-05-14 21:34:14 +02:00
8f4b79cfc6 docs(voyage): add CHANGELOG entry for v5.1.0 2026-05-13 21:24:49 +02:00
dfe1986f06 chore(voyage): bump version 5.0.3 → 5.1.0 2026-05-13 21:23:48 +02:00
6efcc62b68 docs(voyage): document phase_signals in CLAUDE + README + marketplace + ROADMAP (v5.1) 2026-05-13 21:22:07 +02:00
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
d3975c441c feat(voyage): wire 4 downstream commands to brief.phase_signals + composition rule (v5.1) 2026-05-13 21:13:51 +02:00
56fed8f305 feat(voyage): add Phase 3.5 per-phase effort dialog to /trekbrief (v5.1) 2026-05-13 21:11:04 +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
4b5a3a24dd chore(voyage): pin all sub-agents to Opus permanently (operator request)
Flip model: sonnet → model: opus across 20 agent files, 4 prose references
in commands (trekplan, trekresearch), trekendsession command frontmatter,
and CLAUDE.md tables. Aligns CLAUDE.md premium-profile row to actual
premium.yaml content (all-opus, which has been the case since v4.1.0 but
the doc was drift). Companion to VOYAGE_PROFILE=premium env-var (set in
~/.zshenv same day) — env-var governs orchestrator phase model; this
commit governs sub-agent models which are frontmatter-pinned and not
reachable by the profile resolver.

npm test: 516 pass, 0 fail, 2 skipped (unchanged from baseline).

Operator rationale: complete Opus coverage across all Voyage activity,
including the 20 sub-agents that the profile system does not control
(architecture-mapper, task-finder, plan-critic, scope-guardian,
brief-reviewer, code-correctness-reviewer, brief-conformance-reviewer,
review-coordinator, session-decomposer, plus the 6 researcher agents,
plus the 5 codebase-analysis agents).

Cost implication: sub-agent runs ~5x more expensive vs sonnet. Accepted.
2026-05-13 20:20:08 +02:00
c03695c97b docs(voyage): note trinity context (Tier 1 of voyage/app-creator/app-factory)
Informational blockquote after the v3.0.0 note. Documents that voyage is
Tier 1 (per-task) of a three-tier architecture under the author's private
marketplace: Tier 2 app-creator (per-app), Tier 3 app-factory (per-portfolio).
Both are pre-implementation. Asymmetry-invariant preserved: voyage stays
unaware of Tier 2/3 — Handover 1 (brief format) is the only integration
point. Brief-schema changes therefore breaking for downstream consumers,
formalized in v5.4.
2026-05-13 15:56:03 +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
133 changed files with 8259 additions and 1032 deletions

View file

@ -23,7 +23,7 @@
{ {
"name": "voyage", "name": "voyage",
"source": "./plugins/voyage", "source": "./plugins/voyage",
"description": "Voyage — brief, research, plan, execute, review, continue. Contract-driven Claude Code pipeline with specialized agent swarms, external research triangulation, adversarial review, post-hoc independent review with Handover 6 feedback loop, multi-session resumption, session decomposition, and headless execution. Renders produced artifacts to self-contained HTML + link; annotation via the official /playground plugin." "description": "Voyage — brief, research, plan, execute, review, continue. Contract-driven Claude Code pipeline with specialized agent swarms, external research triangulation, adversarial review, post-hoc independent review with Handover 6 feedback loop, multi-session resumption, session decomposition, and headless execution. /trekbrief, /trekplan, and /trekreview each end by building a self-contained operator-annotation HTML (scripts/annotate.mjs, modelled on claude-code-100x): pencil-toggle annotation mode, select text or click any element, pick intent (Fiks/Endre/Spørsmål), comment, Copy Prompt, paste back, Claude revises the .md."
}, },
{ {
"name": "linkedin-thought-leadership", "name": "linkedin-thought-leadership",

View file

@ -11,12 +11,12 @@ plugins/
graceful-handoff/ v2.1.0 — Auto-trigger handoff via Stop hook (skill + JSON pipeline + 4-step model-aware context resolution) graceful-handoff/ v2.1.0 — Auto-trigger handoff via Stop hook (skill + JSON pipeline + 4-step model-aware context resolution)
linkedin-thought-leadership/ v1.2.0 — LinkedIn content pipeline + analytics linkedin-thought-leadership/ v1.2.0 — LinkedIn content pipeline + analytics
llm-security/ v6.0.0 — Security scanning, auditing, threat modeling llm-security/ v6.0.0 — Security scanning, auditing, threat modeling
ms-ai-architect/ v1.13.1 — Microsoft AI architecture (Cosmo Skyberg persona) + manual KB-refresh slash command ms-ai-architect/ v1.15.0 — Microsoft AI architecture (Cosmo Skyberg persona) + manual KB-refresh slash command + v3 project-view (sidebar med 17 artifacts + main + import-modal overlay, v2-surface fjernet i v1.15.0)
okr/ v1.0.0 — OKR guidance for Norwegian public sector okr/ v1.0.0 — OKR guidance for Norwegian public sector
voyage/ v5.0.0 — Brief, research, plan, execute, review, continue. Contract-driven Claude Code pipeline (six-command universal pipeline + multi-session resumption + --gates autonomy chain). Renders produced artifacts to self-contained HTML + link; annotation via the official /playground plugin. v5.0.0 removed the v4.2/v4.3 bespoke playground + /trekrevise + Handover 8 (NIH; duplicated /playground's document-critique). voyage/ v5.0.3 — Brief, research, plan, execute, review, continue. Contract-driven Claude Code pipeline (six-command universal pipeline + multi-session resumption + --gates autonomy chain). /trekbrief, /trekplan, and /trekreview each end by running scripts/annotate.mjs against the just-written .md and printing the file:// link to a self-contained operator-annotation HTML modelled on claude-code-100x/build-site.js: pencil-toggle annotation mode, select text or click any element, choose intent (Fiks/Endre/Spørsmål), comment, sidebar groups by section with delete + Copy Prompt, localStorage persistence per artifact path. v5.0.0 removed the v4.2/v4.3 bespoke playground + /trekrevise + Handover 8; v5.0.1 pointed at /playground document-critique (wrong direction); v5.0.2 was operator-led but too thin; v5.0.3 matches the reference the operator pointed at from day one.
shared/ shared/
playground-design-system/ v0.1 — Aksel/Digdir-aligned CSS design system + JSON schemas + self-hosted Inter/JetBrains Mono/Source Serif 4 fonts (Tier 1+2+3 wave 1+wave 2 = 20 Tier 3 components total). Consumed by ms-ai-architect, okr, llm-security, voyage, config-audit playground-design-system/ v0.6.0 — Aksel/Digdir-aligned CSS design system + JSON schemas + self-hosted Inter/JetBrains Mono/Source Serif 4 fonts. Tier 1 base + Tier 2 + Tier 3 wave 1+2 (20 components) + Tier 4 project-view-arketype (v0.6.0 — sidebar + main + import-modal overlay). Consumed by ms-ai-architect, okr, llm-security, voyage, config-audit.
playground-examples/ — Reference scenarios (ROS-Lier, OKR-Bærum, security-Direktorat) + showcase landing + 12 isolated Tier 3 wave 2 component demos under components/ playground-examples/ — Reference scenarios (ROS-Lier, OKR-Bærum, security-Direktorat) + showcase landing + 12 isolated Tier 3 wave 2 component demos under components/
``` ```
@ -53,3 +53,20 @@ Disse trackes IKKE i git. Oppdater ved sesjonsslutt.
3. Les REMEMBER.md og TODO.md for sesjonsstatus 3. Les REMEMBER.md og TODO.md for sesjonsstatus
4. Jobb innenfor scope 4. Jobb innenfor scope
5. Oppdater REMEMBER.md ved avslutning 5. Oppdater REMEMBER.md ved avslutning
## Communication patterns
### Linking to local files
When pointing to local files in responses, always use markdown link syntax with a descriptive name:
- Use `[Human-friendly name](file:///absolute/path)` — never bare `file:///...` URLs or autolinks `<file://...>`.
- Always use absolute paths. Never `~/` or relative paths.
- For multiple files, render as a bullet list of named markdown links.
Why: bare `file://` URLs only render the first as clickable across multiple lines. Named markdown links make each entry independently clickable and look cleaner.
Example:
- [Brief](file:///Users/ktg/.../brief.html)
- [Research summary](file:///Users/ktg/.../research/summary.md)

View file

@ -77,11 +77,15 @@ Key commands: `/config-audit posture`, `/config-audit feature-gap`, `/config-aud
--- ---
### [Voyage](plugins/voyage/) `v5.0.0` ### [Voyage](plugins/voyage/) `v5.1.1`
Deep requirements gathering, research, implementation planning, self-verifying execution, independent post-hoc review, and zero-friction multi-session resumption — with specialized agent swarms, adversarial review, and failure recovery. Six-command (brief, research, plan, execute, review, continue) universal pipeline. `/trekbrief`, `/trekplan`, and `/trekreview` render their artifact to a self-contained HTML view and print the `file://` link; annotation is delegated to the official `/playground` plugin. Deep requirements gathering, research, implementation planning, self-verifying execution, independent post-hoc review, and zero-friction multi-session resumption — with specialized agent swarms, adversarial review, and failure recovery. Six-command (brief, research, plan, execute, review, continue) universal pipeline + adaptive-depth per-phase effort dialog. `/trekbrief`, `/trekplan`, and `/trekreview` render their artifact to a self-contained HTML view and print the `file://` link.
v5.0.0 (breaking) **removes the bespoke playground.** v4.2/v4.3 shipped a ~388 KB bespoke playground SPA + `/trekrevise` + Handover 8 (annotation → revision); a browser walkthrough found it borderline unusable and it duplicated the official `/playground` plugin's `document-critique` / `diff-review` templates. The SPA, the `/trekrevise` command, Handover 8, 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 all deleted. In their place: a small, zero-dependency `scripts/render-artifact.mjs` that renders any brief/plan/review `.md` to a self-contained, design-system-styled, zero-network `.html` (frontmatter folded into a `<details>` block). The producing commands call it on their last step and print the link; to annotate, run `/playground` (`document-critique`) on the `.md` and paste the generated prompt back — Claude revises the artifact freehand. Forks depending on the removed surfaces migrate to the `/playground` plugin. See `plugins/voyage/CHANGELOG.md` § v5.0.0. v5.1.1 is a 13-step remediation patch closing 11 of 12 findings from the v5.1.0 review (the SC8 dogfood gate is operator-manual, scheduled for after-execute). Load-bearing bug fixes: YAML-number bypass in `brief-validator` so the gate fires for both quoted and unquoted `brief_version` (#8 + #11). Wiring: a new `lib/profiles/phase-signal-resolver.mjs` helper is invoked from `/trekplan`/`/trekresearch`/`/trekreview`/`/trekexecute` Phase 1, the resolved JSON is captured as `phase_signal_result`, and the `brief-validator --soft` gate is required uniformly across all 4 downstream commands (#9 + #12). Test refactor: runtime SC1 walk for trekbrief + per-tier resolver-output + missing-signals falsification per downstream command + dedicated profile-resolver non-interference test (#1 #2 #3 #4 #6 #7 #10). Documentation: Decision B high-effort behavior locked per command (gemini-bridge pass for `/trekplan`, full swarm + always-on `contrarian-researcher` for `/trekresearch`, skip Pass 3 + coordinator normalization for `/trekreview`, `gates_mode: closed` for `/trekexecute`) + brief Non-Goal/SC1 amendments + REMEMBER dogfood scaffolding. v5.1.1 is additive — no breaking changes against v5.1.0. See `plugins/voyage/CHANGELOG.md` § v5.1.1.
v5.1.0 adds Phase 3.5 to `/trekbrief`: 4 tier-coupled `AskUserQuestion` calls commit an effort level (`low | standard | high`) and an optional `model` (`sonnet | opus`) per downstream phase (`research`, `plan`, `execute`, `review`). The choices land in `brief.md` as `phase_signals:` (or `phase_signals_partial: true` on force-stop). `brief_version: 2.1` activates a validator-side sequencing gate (`BRIEF_V51_MISSING_SIGNALS`) so downstream commands halt with a friendly hint when signals are missing. Composition rule per downstream command: brief signal wins per-phase, profile fills gaps. `effort == low` activates each command's existing `--quick`-equivalent code-path (`/trekexecute` low-effort = `--gates open` + sequential-only). Additive — no breaking changes; pre-2.1 briefs still validate. See `plugins/voyage/CHANGELOG.md` § v5.1.0.
v5.0.3 lands the annotation UX modelled on `~/repos/claude-code-100x/claude-code-100x/build-site.js`: pencil-toggle annotation mode, **select text or click any element to anchor**, choose intent (**Fiks** / **Endre** / **Spørsmål**), write a comment, save. The sidebar groups annotations by section with intent badges; Copy Prompt assembles them into a structured markdown the operator pastes back into Claude. State persists in `localStorage` per artifact path. v5.0.2 was operator-led but too thin (line-click + freeform note, no intent categories). v5.0.1 had pointed at `/playground document-critique` (Claude-leads — wrong direction). v5.0.0 (breaking, kept) removed the v4.2/v4.3 bespoke playground SPA, `/trekrevise`, Handover 8, the supporting `lib/` modules, the Playwright e2e suite, and the `@playwright/test` / `@axe-core/playwright` devDeps. v5.0.3's `scripts/annotate.mjs` is one self-contained zero-dependency Node script. **The operator drives every annotation** — Claude never pre-generates suggestions in this flow. See `plugins/voyage/CHANGELOG.md` § v5.0.0 → § v5.0.3.
v4.0.0 (breaking) renamed the plugin from `ultraplan-local` to **Voyage** and all commands from `/ultra*-local` to `/trek*` to remove name collision with Anthropic's `/ultraplan` and `/ultrareview` features. See `plugins/voyage/TRADEMARKS.md` and `plugins/voyage/CHANGELOG.md`. v4.0.0 (breaking) renamed the plugin from `ultraplan-local` to **Voyage** and all commands from `/ultra*-local` to `/trek*` to remove name collision with Anthropic's `/ultraplan` and `/ultrareview` features. See `plugins/voyage/TRADEMARKS.md` and `plugins/voyage/CHANGELOG.md`.
@ -94,9 +98,9 @@ Six commands, one pipeline with clear division of labor:
- **`/trekreview`** — Close the iteration loop. Independent post-hoc reviewer reads `brief.md` from scratch and evaluates the diff produced by execute. Two parallel reviewers (brief-conformance + code-correctness) plus a Judge Agent (review-coordinator) for dedup and reasonableness filtering. Severity-tagged findings (Critical/High/Medium/Low/Info) with stable 40-char hex IDs feed back into planning via Handover 6 (`/trekplan --brief review.md` → remediation plan with `source_findings:` audit trail). - **`/trekreview`** — Close the iteration loop. Independent post-hoc reviewer reads `brief.md` from scratch and evaluates the diff produced by execute. Two parallel reviewers (brief-conformance + code-correctness) plus a Judge Agent (review-coordinator) for dedup and reasonableness filtering. Severity-tagged findings (Critical/High/Medium/Low/Info) with stable 40-char hex IDs feed back into planning via Handover 6 (`/trekplan --brief review.md` → remediation plan with `source_findings:` audit trail).
- **`/trekcontinue`** — Zero-friction multi-session resumption. In a fresh chat, type `/trekcontinue` — reads `.session-state.local.json` (Handover 7), prints a 3-line summary, and immediately begins executing the next session. Any session-end mechanism may write the state file (`/trekexecute` Phase 8/2.55/4 do so automatically; `/trekendsession` helper writes it for informal flows). Forward-compat schema (unknown top-level keys ignored) so future producers can extend additively. - **`/trekcontinue`** — Zero-friction multi-session resumption. In a fresh chat, type `/trekcontinue` — reads `.session-state.local.json` (Handover 7), prints a 3-line summary, and immediately begins executing the next session. Any session-end mechanism may write the state file (`/trekexecute` Phase 8/2.55/4 do so automatically; `/trekendsession` helper writes it for informal flows). Forward-compat schema (unknown top-level keys ignored) so future producers can extend additively.
`/trekbrief`, `/trekplan`, and `/trekreview` each finish by rendering their `.md` artifact to a self-contained `.html` next to it (`scripts/render-artifact.mjs` — zero deps, zero network) and printing the `file://` link. To annotate, run the official `/playground` plugin (`document-critique`) on the `.md` and paste its generated prompt back into the conversation. `/trekbrief`, `/trekplan`, and `/trekreview` each end by running `scripts/annotate.mjs` against the just-written `.md`, printing the `file://<abs path>` link to the resulting self-contained operator-annotation HTML. The operator opens it, clicks any line to add their own note, watches a sidebar of every note (editable, deletable, persisted in browser `localStorage`), clicks "Copy Prompt" to get one structured prompt with every note, pastes back into Claude — Claude revises the `.md` from the notes. The operator drives every annotation.
All artifacts land in one project directory: `.claude/projects/{YYYY-MM-DD}-{slug}/` contains `brief.md` (+ `brief.html`), `research/NN-*.md`, `plan.md` (+ `plan.html`), `sessions/`, `progress.json`, `review.md` (+ `review.html`), and `.session-state.local.json` (gitignored). `--project <dir>` works across `/trekresearch`, `/trekplan`, `/trekexecute`, `/trekreview`, and (optionally) `/trekcontinue`. All artifacts land in one project directory: `.claude/projects/{YYYY-MM-DD}-{slug}/` contains `brief.md`, `research/NN-*.md`, `plan.md`, `sessions/`, `progress.json`, `review.md`, and `.session-state.local.json` (gitignored). `--project <dir>` works across `/trekresearch`, `/trekplan`, `/trekexecute`, `/trekreview`, and (optionally) `/trekcontinue`.
v3.4.0 (non-breaking) adds the **autonomy chain from brief approval to main-merge** plus parallel-wave hardenings. New `lib/util/autonomy-gate.mjs` state machine (`idle → approved → executing → merge-pending → main-merged`), `lib/review/plan-review-dedup.mjs` for Phase 9 inline dedup, `lib/stats/event-emit.mjs` for autonomy-gate transitions and main-merge gate, and `--gates {open|closed|adaptive}` flag on all four pipeline commands. `commands/trekplan.md` Phase 8 seals Opus-4.7 plan/list-emission schema-drift via `plan-validator --strict`. `commands/trekexecute.md` Phase 2.6 wave-executor adds 11 hardenings for plugin-in-monorepo + gitignored-state topology (GIT_OPTIONAL_LOCKS, --max-turns, --max-budget-usd, scoped --allowedTools, push-before-cleanup ordering). New `hooks/scripts/post-compact-flush.mjs` PostCompact hook re-injects session-state after compaction. SC7 synthetic determinism floor (Jaccard ≥ 0.833) for plan + review fixtures. Hook baseline regression pins. Architecture decision: Path B (sequential `--no-ff` parallel waves with manifest-driven failure recovery) ships; Path C (cache-first hybrid) deferred to v3.5.0 contingent on cache-telemetry harvest. v3.4.0 (non-breaking) adds the **autonomy chain from brief approval to main-merge** plus parallel-wave hardenings. New `lib/util/autonomy-gate.mjs` state machine (`idle → approved → executing → merge-pending → main-merged`), `lib/review/plan-review-dedup.mjs` for Phase 9 inline dedup, `lib/stats/event-emit.mjs` for autonomy-gate transitions and main-merge gate, and `--gates {open|closed|adaptive}` flag on all four pipeline commands. `commands/trekplan.md` Phase 8 seals Opus-4.7 plan/list-emission schema-drift via `plan-validator --strict`. `commands/trekexecute.md` Phase 2.6 wave-executor adds 11 hardenings for plugin-in-monorepo + gitignored-state topology (GIT_OPTIONAL_LOCKS, --max-turns, --max-budget-usd, scoped --allowedTools, push-before-cleanup ordering). New `hooks/scripts/post-compact-flush.mjs` PostCompact hook re-injects session-state after compaction. SC7 synthetic determinism floor (Jaccard ≥ 0.833) for plan + review fixtures. Hook baseline regression pins. Architecture decision: Path B (sequential `--no-ff` parallel waves with manifest-driven failure recovery) ships; Path C (cache-first hybrid) deferred to v3.5.0 contingent on cache-telemetry harvest.
@ -120,7 +124,7 @@ Defense-in-depth security: plugin hooks block destructive commands and sensitive
Modes: default, brief-driven, project-scoped, research-enriched, foreground, quick, decompose, export, resume Modes: default, brief-driven, project-scoped, research-enriched, foreground, quick, decompose, export, resume
23 specialized agents · 6 commands (+ 1 helper) · 5 plugin hooks · 500+ tests · Self-contained HTML artifact rendering · No cloud dependency 23 specialized agents · 6 commands (+ 1 helper) · 5 plugin hooks · 500+ tests · Operator-driven HTML annotation surface · No cloud dependency
→ [Full documentation](plugins/voyage/README.md) · [Migration guide](plugins/voyage/MIGRATION.md) → [Full documentation](plugins/voyage/README.md) · [Migration guide](plugins/voyage/MIGRATION.md)
@ -168,7 +172,7 @@ Key command: `/graceful-handoff [topic-slug] [--no-commit] [--no-push] [--dry-ru
--- ---
### [MS AI Architect — Azure AI and Microsoft Foundry](plugins/ms-ai-architect/) `v1.14.0` `🇳🇴 Norwegian` ### [MS AI Architect — Azure AI and Microsoft Foundry](plugins/ms-ai-architect/) `v1.15.0` `🇳🇴 Norwegian`
Microsoft AI solution architecture guidance for Norwegian public sector and enterprise. Microsoft AI solution architecture guidance for Norwegian public sector and enterprise.
@ -183,11 +187,11 @@ Key commands: `/architect`, `/architect:ros`, `/architect:security`, `/architect
12 specialized agents · 25 commands · 5 skills (387 reference docs) · 2 hooks · manual sitemap-driven KB refresh 12 specialized agents · 25 commands · 5 skills (387 reference docs) · 2 hooks · manual sitemap-driven KB refresh
**One-click demo (v1.14.0, 2026-05-08):** "Last inn demo-data"-knappen på onboarding bootstrapper en ferdig "Acme Kommune" med demo-prosjektet "Acme: Kunde-chatbot" og alle 17 rapport-typer pre-importert som `raw_markdown` (konsistente navn på tvers av alle fixtures). Visualisering rehydreres automatisk på project-surface mount. 24 retina-screenshots committed under `playground/screenshots/v1.14.0/` (12 surfaces × 2 tema), så forkere ser pluginen uten å kjøre noe. Standalone Playwright-runner under `tests/screenshot/` (egen `package.json`). **One-click demo (v1.15.0, 2026-05-16):** "Last inn demo-data"-knappen på onboarding bootstrapper en ferdig "Acme Kommune" med demo-prosjektet "Acme: Kunde-chatbot" og alle 17 rapport-typer pre-importert. v2→v3 migrasjon auto-parser `raw_markdown` til `project.artifacts[cid]` så project-view viser aggregert verdict (BLOKKERT), key stats (17/17 artifacts), top-risks-liste, og navigerbart artifact-sidebar i én navigasjon. 24 retina-screenshots committed under `playground/screenshots/v1.15.0/` (12 surfaces × 2 tema), så forkere ser pluginen uten å kjøre noe. Standalone Playwright-runner under `tests/screenshot/` (egen `package.json`).
**Playground (v3, v1.14.0 — root-cause refaktor, 2026-05-08):** Multi-surface decision-builder + report viewer. The single-file HTML app lives at `playground/ms-ai-architect-playground.html` (~3870+ lines). v1.14.0 leverer DS-konvensjon-adopsjon på 14 renderere over 6 sesjoner: B-DS-1/2/3 fikset i shared/ DS v0.4.0 (kanban-card word-break, expansion title-block, matrix-bubble cursor); 3 risk-renderere til DS-summary-grid + ros-layout; 6 compliance/govern-renderere bytter `.report-meta`-wrapper mot DS-konvensjon; renderMigrate + renderPoc til expansion-list per fase; 5b-fixes i renderCost/renderCompare/renderUtredning. Lokal `<style>`-blokk: 191 → 122 effektive linjer (~36% reduksjon siden v1.13.1). **Playground (v3, v1.15.0 — project-view integration, 2026-05-16):** Multi-surface decision-builder + report viewer. The single-file HTML app lives at `playground/ms-ai-architect-playground.html` (~3870+ lines). v1.15.0 erstatter v2 project-surface (screen-tabs + category-tabs + per-command paste-cards) med v3 `renderProjectView` (sidebar med 17 artifacts gruppert i 4 kategorier + main-area med per-artifact view eller overview + import-modal som DS-overlay). V2-surface helt fjernet (`renderProjectSurface`, `renderCommandSubCard`, `rehydratePasteImports`, 5 v2-CSS-klasser). 2 fingerprint-gap lukket (requirements + license headers). `migrateDataVersion` utvidet med `parserFor` slik at demo-state og persisted localStorage auto-parses. Ship-QA: `components-tier4-project-view.css` lagt til i `<link>`-kjeden (var ikke loaded → modal-overlay og two-column layout virket ikke). 386 E2E PASS, 0 FAIL, 2 WARN.
- **4 surfaces:** Onboarding (4 strukturerte / 14 fritekst, prefill alle command-skjemaer) → Home (project list + 3 entry tracks) → Catalog (24 commands grouped in 5 expansion categories with search) → Project (per-project tabs, command-form prefill, paste-back report import + visualization) - **4 surfaces:** Onboarding (4 strukturerte / 14 fritekst, prefill alle command-skjemaer) → Home (project list + 3 entry tracks) → Catalog (24 commands grouped in 5 expansion categories with search) → **Project v3** (sidebar med 17 artifacts + søk + main-area med per-artifact view eller aggregate overview + import-modal overlay)
- **Persistence:** IndexedDB primary + localStorage fallback, schema-versioned (`STATE_KEY = 'ms-ai-architect-state-v1'`) with eager migrations pipeline. v1.10.0 adds idempotent `dataVersion v1→v2` migration that backfills `verdict` + `keyStats` on existing reports. - **Persistence:** IndexedDB primary + localStorage fallback, schema-versioned (`STATE_KEY = 'ms-ai-architect-state-v1'`) with eager migrations pipeline. v1.10.0 adds idempotent `dataVersion v1→v2` migration that backfills `verdict` + `keyStats` on existing reports.
- **17 inline report renderers (felles grunnskjelett)** — all wrap output through `renderPageShell()` with eyebrow + h1 + optional verdict-pill + optional key-stats-grid + archetype body (pyramid, 5×5/6×5/7×5 matrix, radar, kanban, mat-ladder, scenario-cards, screen-tabs, residual-pair, top-risks, recommendation-card, suppressed-panel, critique-card, read-more, traffic-light). - **17 inline report renderers (felles grunnskjelett)** — all wrap output through `renderPageShell()` with eyebrow + h1 + optional verdict-pill + optional key-stats-grid + archetype body (pyramid, 5×5/6×5/7×5 matrix, radar, kanban, mat-ladder, scenario-cards, screen-tabs, residual-pair, top-risks, recommendation-card, suppressed-panel, critique-card, read-more, traffic-light).
- **Foundation helpers**`renderPageShell`, `renderVerdictPill`, `renderKeyStatsGrid`, `inferVerdict`, `inferKeyStats`, `KEY_STATS_CONFIG`. - **Foundation helpers**`renderPageShell`, `renderVerdictPill`, `renderKeyStatsGrid`, `inferVerdict`, `inferKeyStats`, `KEY_STATS_CONFIG`.

View file

@ -92,3 +92,20 @@ node --test tests/*.test.mjs
- Conventional Commits: `type(scope): description` - Conventional Commits: `type(scope): description`
- English for all code, comments, and documentation - English for all code, comments, and documentation
- Norwegian for project-internal communication - Norwegian for project-internal communication
## Communication patterns
### Linking to local files
When pointing to local files in responses, always use markdown link syntax with a descriptive name:
- Use `[Human-friendly name](file:///absolute/path)` — never bare `file:///...` URLs or autolinks `<file://...>`.
- Always use absolute paths. Never `~/` or relative paths.
- For multiple files, render as a bullet list of named markdown links.
Why: bare `file://` URLs only render the first as clickable across multiple lines. Named markdown links make each entry independently clickable and look cleaner.
Example:
- [Brief](file:///Users/ktg/.../brief.html)
- [Research summary](file:///Users/ktg/.../research/summary.md)

View file

@ -0,0 +1,18 @@
{
"name": "claude-design",
"version": "0.1.0-pre",
"description": "Claude Design expertise — facilitates the full prompt-to-artifact workflow on claude.ai/design. Skills, agents, and commands to be defined via Voyage pipeline.",
"author": {
"name": "Kjell Tore Guttormsen"
},
"auto_discover": true,
"license": "MIT",
"repository": "https://git.fromaitochitta.com/open/ktg-plugin-marketplace",
"keywords": [
"claude-design",
"claude-ai",
"prompt-engineering",
"artifacts",
"design"
]
}

View file

@ -0,0 +1,17 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
### Added
- Plugin bootstrap: directory created inside `ktg-plugin-marketplace`, minimal manifest and skeleton docs.
- Voyage pipeline kicked off: brief → research → plan → execute → review.
## [0.1.0-pre] — 2026-05-15
### Added
- Initial scaffold (README, CLAUDE.md, ROADMAP, TODO, plugin.json placeholder).

View file

@ -0,0 +1,60 @@
# claude-design
## Kontekst
Plugin som skal være ekspert på **Claude Design** (claude.ai/design) — Anthropics surface for å lage interaktive artifacts via prompt.
Skal dekke hele prosessen fra idé til ferdig artifact:
1. Forstå hva brukeren vil lage
2. Velge riktig artifact-type og prompt-strategi
3. Generere effektive prompts for Claude Design
4. Iterere og refine basert på preview
5. Polere og levere
Den endelige sammensetningen — skills, agenter, kommandoer — defineres gjennom Voyage-pipelinen og er IKKE fastlagt på forhånd.
## Status
`0.1.0-pre` — bootstrap. Innholdet bygges via `/trekbrief``/trekresearch``/trekplan``/trekexecute``/trekreview`.
## Marketplace-kontekst
Plugin ligger inne i `ktg-plugin-marketplace`. Ingen egen git-repo, ingen egen Forgejo-remote. Alle commits til marketplace-repoet på `git.fromaitochitta.com/open/ktg-plugin-marketplace`.
Følg marketplace-konvensjonene fra rot-CLAUDE.md:
- Norsk dialog, engelsk kode/dokumentasjon
- Conventional Commits: `type(scope): description` — scope er `claude-design`
- Hooks i Node.js (.mjs), ikke bash
- Null npm-avhengigheter i hooks
- Docs-trippel oppdatert i samme commit ved feature-endringer: plugin `README.md`, plugin `CLAUDE.md`, rot-`README.md`
## Arbeidsflyt
Pipelinen er sannhet inntil release:
1. Brief lukker scope og scope-grenser
2. Research samler eksterne kilder (Anthropic cookbook, claude.ai/design-dokumentasjon, community-mønstre)
3. Plan beskriver fil-for-fil hva som skal lages
4. Execute leverer kode/innhold
5. Review er release-gate
Voyage-policy: Opus på alle sub-agenter og orchestrator-faser.
## Communication patterns
### Linking to local files
When pointing to local files in responses, always use markdown link syntax with a descriptive name:
- Use `[Human-friendly name](file:///absolute/path)` — never bare `file:///...` URLs or autolinks `<file://...>`.
- Always use absolute paths. Never `~/` or relative paths.
- For multiple files, render as a bullet list of named markdown links.
Why: bare `file://` URLs only render the first as clickable across multiple lines. Named markdown links make each entry independently clickable and look cleaner.
Example:
- [Brief](file:///Users/ktg/.../brief.html)
- [Research summary](file:///Users/ktg/.../research/summary.md)

View file

@ -0,0 +1,22 @@
# claude-design
Claude Code plugin: be an expert on **Claude Design** (claude.ai/design).
This plugin holds the knowledge, skills, agents, and commands needed to facilitate the entire process of designing and producing artifacts on Claude Design — from initial idea, through prompt engineering, through iteration, to a finished result.
> **Status: 0.1.0-pre — bootstrap.** Skill/agent/command surface is being defined through the Voyage pipeline (`/trekbrief``/trekresearch``/trekplan``/trekexecute``/trekreview`).
## Scope (to be confirmed via brief)
- Deep knowledge of Claude Design as a product surface
- Prompt-engineering patterns specifically tuned for Claude Design
- End-to-end facilitation: discovery → prompt → preview → refine → ship
- Reference material from the Anthropic cookbook and other authoritative sources
## Status
Pre-release. No commands or agents yet. See `ROADMAP.md` and `TODO.md` for the current Voyage iteration.
## License
MIT

View file

@ -225,3 +225,20 @@ node --test 'tests/**/*.test.mjs'
- Session directories accumulate — use `/config-audit cleanup` to manage - Session directories accumulate — use `/config-audit cleanup` to manage
- Scanners run on Node.js >= 18 (uses node:test, node:fs/promises) - Scanners run on Node.js >= 18 (uses node:test, node:fs/promises)
- Plugin CLAUDE.md files in node_modules should be excluded via scope - Plugin CLAUDE.md files in node_modules should be excluded via scope
## Communication patterns
### Linking to local files
When pointing to local files in responses, always use markdown link syntax with a descriptive name:
- Use `[Human-friendly name](file:///absolute/path)` — never bare `file:///...` URLs or autolinks `<file://...>`.
- Always use absolute paths. Never `~/` or relative paths.
- For multiple files, render as a bullet list of named markdown links.
Why: bare `file://` URLs only render the first as clickable across multiple lines. Named markdown links make each entry independently clickable and look cleaner.
Example:
- [Brief](file:///Users/ktg/.../brief.html)
- [Research summary](file:///Users/ktg/.../research/summary.md)

View file

@ -63,3 +63,20 @@ node --test plugins/graceful-handoff/tests/
- v1.0.0 (2026-04-19): initial declarative command - v1.0.0 (2026-04-19): initial declarative command
- v2.0.0 (2026-05-01): skill-arkitektur + JSON-pipeline + 3 hooks + auto-trigger (BREAKING) - v2.0.0 (2026-05-01): skill-arkitektur + JSON-pipeline + 3 hooks + auto-trigger (BREAKING)
- v2.1.0 (2026-05-01): modell-bevisst kontekstvindu — 4-stegs resolution-kjede (used_percentage → payload-size → model-map → 1M default). Fikser for-tidlig auto-handoff på Opus 4.7 - v2.1.0 (2026-05-01): modell-bevisst kontekstvindu — 4-stegs resolution-kjede (used_percentage → payload-size → model-map → 1M default). Fikser for-tidlig auto-handoff på Opus 4.7
## Communication patterns
### Linking to local files
When pointing to local files in responses, always use markdown link syntax with a descriptive name:
- Use `[Human-friendly name](file:///absolute/path)` — never bare `file:///...` URLs or autolinks `<file://...>`.
- Always use absolute paths. Never `~/` or relative paths.
- For multiple files, render as a bullet list of named markdown links.
Why: bare `file://` URLs only render the first as clickable across multiple lines. Named markdown links make each entry independently clickable and look cleaner.
Example:
- [Brief](file:///Users/ktg/.../brief.html)
- [Research summary](file:///Users/ktg/.../research/summary.md)

View file

@ -45,3 +45,20 @@ If `keep-coding-instructions` is removed or set to `false`, Claude Code will str
- Per-plugin variants (code-focused, deep-technical, etc.) — would belong in a future v1.1 if there's real demand - Per-plugin variants (code-focused, deep-technical, etc.) — would belong in a future v1.1 if there's real demand
- Forcing the style on other plugins — it remains opt-in. Other plugins may reference it in their READMEs. - Forcing the style on other plugins — it remains opt-in. Other plugins may reference it in their READMEs.
- Translation of the style file itself into Norwegian — defeats the purpose of language-agnostic instruction - Translation of the style file itself into Norwegian — defeats the purpose of language-agnostic instruction
## Communication patterns
### Linking to local files
When pointing to local files in responses, always use markdown link syntax with a descriptive name:
- Use `[Human-friendly name](file:///absolute/path)` — never bare `file:///...` URLs or autolinks `<file://...>`.
- Always use absolute paths. Never `~/` or relative paths.
- For multiple files, render as a bullet list of named markdown links.
Why: bare `file://` URLs only render the first as clickable across multiple lines. Named markdown links make each entry independently clickable and look cleaner.
Example:
- [Brief](file:///Users/ktg/.../brief.html)
- [Research summary](file:///Users/ktg/.../research/summary.md)

View file

@ -98,3 +98,20 @@ All content commands (post, quick, react, pipeline, first-post, video, multiplat
5. Topic must align with user's 5 core expertise areas (360Brew signal) 5. Topic must align with user's 5 core expertise areas (360Brew signal)
6. Topic rotation: no back-to-back same pillar, no pillar >50% in 14 days (warn-only) 6. Topic rotation: no back-to-back same pillar, no pillar >50% in 14 days (warn-only)
7. Progressive onboarding: personalization score hidden until 3+ posts; voice guardian suppressed until 5+ voice samples 7. Progressive onboarding: personalization score hidden until 3+ posts; voice guardian suppressed until 5+ voice samples
## Communication patterns
### Linking to local files
When pointing to local files in responses, always use markdown link syntax with a descriptive name:
- Use `[Human-friendly name](file:///absolute/path)` — never bare `file:///...` URLs or autolinks `<file://...>`.
- Always use absolute paths. Never `~/` or relative paths.
- For multiple files, render as a bullet list of named markdown links.
Why: bare `file://` URLs only render the first as clickable across multiple lines. Named markdown links make each entry independently clickable and look cleaner.
Example:
- [Brief](file:///Users/ktg/.../brief.html)
- [Research summary](file:///Users/ktg/.../research/summary.md)

View file

@ -392,3 +392,20 @@ Prompt injection is **structurally unsolvable** with current architectures (join
- Agents operate read-only unless the specific command explicitly grants Write/Edit (`clean` and `harden` do) - Agents operate read-only unless the specific command explicitly grants Write/Edit (`clean` and `harden` do)
- Irreversible operations (baseline overwrites, file edits) require user confirmation via AskUserQuestion - Irreversible operations (baseline overwrites, file edits) require user confirmation via AskUserQuestion
- Do not access paths outside the project root without explicit user instruction - Do not access paths outside the project root without explicit user instruction
## Communication patterns
### Linking to local files
When pointing to local files in responses, always use markdown link syntax with a descriptive name:
- Use `[Human-friendly name](file:///absolute/path)` — never bare `file:///...` URLs or autolinks `<file://...>`.
- Always use absolute paths. Never `~/` or relative paths.
- For multiple files, render as a bullet list of named markdown links.
Why: bare `file://` URLs only render the first as clickable across multiple lines. Named markdown links make each entry independently clickable and look cleaner.
Example:
- [Brief](file:///Users/ktg/.../brief.html)
- [Research summary](file:///Users/ktg/.../research/summary.md)

View file

@ -1,5 +1,88 @@
# playground-design-system — CHANGELOG # playground-design-system — CHANGELOG
## 0.6.0 — 2026-05-15
### Added — Project-view archetype (Tier 4)
Generic "project as artifact-collection" archetype for plugins where a project owns 0-N read-only report artifacts grouped by category. Default view is an aggregated dashboard; clicking a sidebar item swaps the main panel to the per-artifact render. Edit-mode is paste-import only (no inline editor).
- **New file `components-tier4-project-view.css`** — 11 sections covering:
- `.project-view` + `.project-view__layout` (grid: nav 280px + main 1fr, responsive collapse at 1280 / 960px)
- `.project-view__header` (CSS Grid with eyebrow/title/lede/verdict/key-stats/actions areas)
- `.verdict-pill` (small pill variant — companion to existing `.verdict-pill-lg` in tier2)
- `.project-view__nav` + `.project-view__nav-search` (sticky sidebar with search)
- `.artifact-list` + `__group` / `__group-label` / `__group-count` / `__group-items` / `__item` / `__item-marker` / `__item-body` / `__item-name` / `__item-meta` (grouped, severity-coded sidebar)
- `.artifact-status[data-severity]` (mini-pill: positive | medium | critical)
- `.project-view__main` (main column container)
- `.project-overview` + `__intro` / `__verdict-grid` / `__verdict-tile[data-severity]` / `__section` / `__top-risks` / `__next-actions` / `__missing-reports` (aggregated dashboard)
- `.project-view__artifact` + `__artifact-header` / `__artifact-title` / `__artifact-meta` / `__artifact-actions` / `__artifact-body` (single-rapport viewer wrapper)
- `.empty-artifact-prompt` + `__icon` / `__title` / `__text` / `__actions` (empty-state)
- `.import-modal` + `__backdrop` / `__panel` / `__head` / `__title` / `__close` / `__form` / `__detect` / `__preview` / `__preview-label` / `__footer` (overlay modal for paste-import)
- **6 new tokens in `tokens.css`:**
- `--project-view-nav-width: 280px` — sidebar width at full layout
- `--project-view-collapse-bp: 960px` — doc-only token referenced by responsive breakpoints
- `--artifact-list-item-pad-y: var(--space-2)` — sidebar row vertical padding
- `--artifact-list-item-pad-x: var(--space-3)` — sidebar row horizontal padding
- `--artifact-marker-size: 14px` — sidebar status marker diameter
- `--artifact-marker-border: 1.5px` — sidebar status marker border thickness
### Påvirkning
Endringen er **additiv**: ny komponent-fil + 6 nye tokens, ingen eksisterende selectors eller verdier endres. Plugin-konsumenter (`ms-ai-architect`, `llm-security`, `okr`, `config-audit`, `voyage`) får silent drift mot ny source-commit, men kan re-sync på eget tempo. Bare `ms-ai-architect` og `llm-security` re-syncer i samme commit som denne DS-bumpen (forberedelse til koordinert v1.15.0 / v7.7.0-release etter ~8 sesjoner med JS-implementasjon).
Førsteadoptere: `ms-ai-architect` v1.15.0 (17 artefakter, 5 kategorier) + `llm-security` v7.7.0 (≥18 artefakter, 6 kategorier). State-driven visibility håndteres i plugin-JS, ikke i denne CSS-en — kun aktiv state rendres per pass.
### Plugins som må laste den nye filen
Etter `<link>` til `components-tier3-supplement.css`, legg til:
```html
<link rel="stylesheet" href="vendor/playground-design-system/components-tier4-project-view.css">
```
### For å adoptere v0.6.0
```bash
node scripts/sync-design-system.mjs <plugin-name>
# --force hvis drift detected
```
## 0.5.0 — 2026-05-10
### Added
- **voyage scope tokens (B-DS-4):** `--color-scope-voyage` (aqua-blue `#1B5FB8`), `--color-scope-voyage-soft` (`#E5EFFA`), `--color-scope-voyage-strong` (`#143E78`) appended to scope-color group in `tokens.css`. Matches the existing `--color-scope-{architect,okr,security,ultraplan,config}` family so voyage-playground can use the canonical badge convention.
- **`.badge--scope-voyage`** in `base.css`: white-on-aqua-blue badge variant matching the existing scope-badge family.
### Påvirkning
Endringen er **additiv**: legger TIL voyage-scope-tokens og en ny badge-modifier. Ingen eksisterende selectors eller token-verdier endres. Plugin-konsumenter (llm-security, ms-ai-architect, okr, config-audit) får stale vendor-state mot ny source-commit, men det er silent drift — re-sync skjer på eget tempo neste playground-touch. Bare `voyage` re-syncer i denne commit-en.
Førsteadopter: `voyage` v4.3.0 (multi-sesjons-løp 2026-05-10, sesjon 1 = Wave 0+1 Foundation).
## 0.4.0 — 2026-05-08
### Bug fixes
- **`.kanban-card__name`** (components-tier3-supplement.css): bytt `word-break: break-all` til `word-break: break-word` + `overflow-wrap: anywhere`. `break-all` knekker midt i ord ("Tekn isk dokumen tasjon"); ny verdi respekterer ordskjøt og brytter kun lange tokens (B-DS-1).
- **`.expansion__title-main`, `.expansion__title-sub`** (components-tier3-supplement.css): legg til `display: block`. Begge er `<span>`-elementer som flyter inline by default, noe som gir "dokumentertKilde: Art. 9" på samme linje. `display: block` sikrer vertikal stacking (B-DS-2).
- **`.matrix__bubble`** (components.css): legg til `cursor: pointer`, `transition`, `:hover { transform: scale(1.15) }` og `:focus-visible { outline + offset }`. Antar at consumer rendrer bobler som `<button>` for click-handlers — gir visuell + keyboard-fokus-feedback (B-DS-3).
### Påvirkning
Bugfixene er **backward-compatible** — alle eksisterende selectors og verdier som er endret, var bugfixes. Plugin-konsumenter som har lokal-overrides for disse mønstrene bør re-syncer og slette overridene:
- **ms-ai-architect:** re-sync i samme commit, sletter linje 191-193 (matrix-bubble), 208-211 (expansion-title), 213-216 (kanban-card-name) i `playground/ms-ai-architect-playground.html`.
- **llm-security, voyage, okr, config-audit:** re-sync på eget tempo (ikke breaking — gammel vendored DS fungerer fortsatt med eksisterende lokal-overrides).
### For å adoptere v0.4
```bash
node scripts/sync-design-system.mjs <plugin-name>
# --force hvis drift detected
```
Førsteadopter: `ms-ai-architect` v1.14.0 (planlagt 2026-05-08, multi-sesjons-løp som starter med DS-bump i sesjon 2).
## 0.3.0 — 2026-05-04 ## 0.3.0 — 2026-05-04
### Added — Playground/report-page foundation primitives (sections 13-25 in tier3-supplement) ### Added — Playground/report-page foundation primitives (sections 13-25 in tier3-supplement)

View file

@ -2,17 +2,18 @@
"generated_by": "scripts/sync-design-system.mjs", "generated_by": "scripts/sync-design-system.mjs",
"do_not_edit": true, "do_not_edit": true,
"source": "shared/playground-design-system/", "source": "shared/playground-design-system/",
"source_commit": "487f7ae746aeb1c0f19bb0f4b8d0ffcf0a59a677", "source_commit": "c1b7bad3899c5cfe9ff90663003609b018aa79a0",
"sync_date": "2026-05-05T16:33:38.829Z", "sync_date": "2026-05-15T14:11:15.296Z",
"file_count": 26, "file_count": 27,
"files": { "files": {
"CHANGELOG.md": "e293a911701e0ae8e95f8d30e2b583d1c578d0c2af4fd2abfbee3a7d65d5f7ba", "CHANGELOG.md": "b5018b46cd0830334109e915d23b5554c060412c2b7e132f97f2933e5dd5d79c",
"README.md": "83de0e29b207c979b7b2a3327b7a4ec0c2e1b4d3705ee2677f26c28c3a3ee643", "README.md": "83de0e29b207c979b7b2a3327b7a4ec0c2e1b4d3705ee2677f26c28c3a3ee643",
"base.css": "604fe6839e2ed304bc0ba112a4e045f208b4b3f084f449a1abdb94ce0a1e5263", "base.css": "df0db874473412eb771b7355b589f7478042987756898f0921584286bd5ba70a",
"components-tier2.css": "c2cb7e9d76d6af28d50db654030413777feb2f2f2b93213e598de8b686b14523", "components-tier2.css": "c2cb7e9d76d6af28d50db654030413777feb2f2f2b93213e598de8b686b14523",
"components-tier3-supplement.css": "b78664275948f05b9cb4e577921695bd39d15b34c671809d8c8465cac4e1739b", "components-tier3-supplement.css": "51fab10377d80029d6552613069d46fd14ce66af77fe6705b1c6bdf5c9e6481e",
"components-tier3.css": "c391ea387298ce864bc35078e7e044b2cdd4187e3130456347d91876599ff4b1", "components-tier3.css": "c391ea387298ce864bc35078e7e044b2cdd4187e3130456347d91876599ff4b1",
"components.css": "f76b22ba9fd64c2e806b4467536174347105f3e5ccca8a6349a919287d864b86", "components-tier4-project-view.css": "f8f784df70044ecc9bdc862a327b1ee58b201d056581316808a9b60632c5a993",
"components.css": "56fa7392b8b20b567a46f72a8fe9e0205d78ce475eae6b22fc3f50b39b235545",
"fonts.css": "e3c3df581c6e4d66e25c555f125c745f6512a33038401089d2519a94ea63ee3d", "fonts.css": "e3c3df581c6e4d66e25c555f125c745f6512a33038401089d2519a94ea63ee3d",
"fonts/Inter-Bold.woff2": "220976705fbec109f43c5cfdceca639e99ace7e51f3eb67292b105d3575eb39b", "fonts/Inter-Bold.woff2": "220976705fbec109f43c5cfdceca639e99ace7e51f3eb67292b105d3575eb39b",
"fonts/Inter-Medium.woff2": "8458f8afa67b5691c1fcbe51607a2dafb53a9839e48131c608a186b65415d96d", "fonts/Inter-Medium.woff2": "8458f8afa67b5691c1fcbe51607a2dafb53a9839e48131c608a186b65415d96d",
@ -31,6 +32,6 @@
"schemas/finding.schema.json": "0b24797373650582bac232d31a4dd9260593375a0d17259e18f1141a20de8d0c", "schemas/finding.schema.json": "0b24797373650582bac232d31a4dd9260593375a0d17259e18f1141a20de8d0c",
"schemas/okr-set.schema.json": "aa27347fb232a956ec9dcee1775115710e2715a665c8d729ac50b90c6884de26", "schemas/okr-set.schema.json": "aa27347fb232a956ec9dcee1775115710e2715a665c8d729ac50b90c6884de26",
"schemas/ros-threat.schema.json": "e16497c1a6b79d6e78149d6cf1c28ac9df1e93234627a0c546814fb24d6c96d9", "schemas/ros-threat.schema.json": "e16497c1a6b79d6e78149d6cf1c28ac9df1e93234627a0c546814fb24d6c96d9",
"tokens.css": "1499bc2eea0178e35935413c79a10bbee7d49fdfa91bd33eeba3bb9e9acab809" "tokens.css": "63dca13f8341937169fc8e84d3f37ae0c714901fa006c865ea377bd448f87644"
} }
} }

View file

@ -146,6 +146,7 @@ button { font-family: inherit; }
.badge--scope-security { background: var(--color-scope-security); color: #fff; border-color: transparent; } .badge--scope-security { background: var(--color-scope-security); color: #fff; border-color: transparent; }
.badge--scope-ultraplan { background: var(--color-scope-ultraplan); color: #fff; border-color: transparent; } .badge--scope-ultraplan { background: var(--color-scope-ultraplan); color: #fff; border-color: transparent; }
.badge--scope-config { background: var(--color-scope-config); color: #fff; border-color: transparent; } .badge--scope-config { background: var(--color-scope-config); color: #fff; border-color: transparent; }
.badge--scope-voyage { background: var(--color-scope-voyage); color: #fff; border-color: transparent; }
/* ---------- Cards / surfaces ---------- */ /* ---------- Cards / surfaces ---------- */
.card { .card {

View file

@ -280,7 +280,7 @@
.kanban-card[data-verdict="trusted"] { border-left: 4px solid var(--color-state-success); } .kanban-card[data-verdict="trusted"] { border-left: 4px solid var(--color-state-success); }
.kanban-card[data-verdict="unknown"] { border-left: 4px solid var(--color-state-warning); } .kanban-card[data-verdict="unknown"] { border-left: 4px solid var(--color-state-warning); }
.kanban-card__name { font-family: var(--font-family-mono); font-size: 13px; color: var(--color-text-primary); word-break: break-all; } .kanban-card__name { font-family: var(--font-family-mono); font-size: 13px; color: var(--color-text-primary); word-break: break-word; overflow-wrap: anywhere; }
.kanban-card__meta { font-size: 11px; color: var(--color-text-tertiary); } .kanban-card__meta { font-size: 11px; color: var(--color-text-tertiary); }
.kanban-card__reason { font-size: 12px; color: var(--color-text-secondary); } .kanban-card__reason { font-size: 12px; color: var(--color-text-secondary); }
@ -696,8 +696,8 @@
.expansion__head:hover { background: var(--color-bg-soft); } .expansion__head:hover { background: var(--color-bg-soft); }
.expansion__head:focus-visible { outline: none; box-shadow: var(--shadow-focus); } .expansion__head:focus-visible { outline: none; box-shadow: var(--shadow-focus); }
.expansion__title { flex: 1; } .expansion__title { flex: 1; }
.expansion__title-main { font-size: var(--font-size-md); color: var(--color-text-primary); font-weight: var(--font-weight-medium); } .expansion__title-main { display: block; font-size: var(--font-size-md); color: var(--color-text-primary); font-weight: var(--font-weight-medium); }
.expansion__title-sub { font-size: var(--font-size-sm); color: var(--color-text-secondary); margin-top: 2px; } .expansion__title-sub { display: block; font-size: var(--font-size-sm); color: var(--color-text-secondary); margin-top: 2px; }
.expansion__chev { .expansion__chev {
color: var(--color-text-tertiary); color: var(--color-text-tertiary);
transition: transform var(--duration-normal) var(--ease-default); transition: transform var(--duration-normal) var(--ease-default);

View file

@ -0,0 +1,666 @@
/* Code generated by sync-design-system.mjs; DO NOT EDIT. */
/* =============================================================================
Playground Design System components-tier4-project-view.css
v0.6.0 Tier 4 project-view archetype
============================================================================
Generic "project as artifact-collection" archetype. Default-view is an
aggregated overview dashboard; clicking a sidebar item swaps main to a
per-artifact render. Tracks 0-N read-only artifacts; edit-mode is paste-
import only (markdown from terminal parser store).
First adopters: ms-ai-architect v1.15.0 (17 artifacts, 5 categories) +
llm-security v7.7.0 (18 artifacts, 6 categories). Each plugin injects a
PROJECT_VIEW_CONFIG object that maps commands renderers, categories,
verdict-aggregators, missing-report heuristics.
The CSS in this file is plugin-agnostic. Plugin-specific shape (category
names, artifact ordering, custom severity-mappings) lives in JS config.
State-driven visibility is NOT handled here production playgrounds emit
only the active state (overview | artifact | empty | import) per render
pass. The mockup uses body[data-state="..."] for prototyping; production
renders one branch at a time.
============================================================================= */
/* === 1. Project-view top-level layout ===================================== */
.project-view {
display: flex;
flex-direction: column;
gap: var(--space-6);
}
.project-view__layout {
display: grid;
grid-template-columns: var(--project-view-nav-width) 1fr;
gap: var(--space-6);
align-items: start;
}
@media (max-width: 1279px) {
.project-view__layout { grid-template-columns: 240px 1fr; }
}
@media (max-width: 959px) {
.project-view__layout { grid-template-columns: 1fr; }
}
/* === 2. Project-view header =============================================== */
.project-view__header {
background: var(--color-surface);
border: 1px solid var(--color-border-subtle);
border-radius: var(--radius-md);
padding: var(--space-5) var(--space-6);
display: grid;
grid-template-columns: 1fr auto;
grid-template-areas:
"title verdict"
"title keystats"
"actions actions";
gap: var(--space-4) var(--space-6);
align-items: start;
}
.project-view__title-block { grid-area: title; }
.project-view__verdict { grid-area: verdict; justify-self: end; }
.project-view__key-stats { grid-area: keystats; justify-self: end; }
.project-view__actions { grid-area: actions; display: flex; gap: var(--space-2); justify-content: flex-end; }
.project-view__eyebrow {
font-size: 11px;
text-transform: uppercase;
letter-spacing: 0.08em;
color: var(--color-text-tertiary);
font-weight: var(--font-weight-semibold);
margin: 0 0 var(--space-2) 0;
}
.project-view__title {
font-size: var(--font-size-2xl);
font-weight: var(--font-weight-bold);
color: var(--color-text-primary);
margin: 0 0 var(--space-2) 0;
}
.project-view__lede {
color: var(--color-text-secondary);
margin: 0;
max-width: 60ch;
}
.project-view__key-stats {
display: flex;
gap: var(--space-5);
}
.project-view__key-stat-label {
font-size: 10px;
text-transform: uppercase;
color: var(--color-text-tertiary);
letter-spacing: 0.06em;
}
.project-view__key-stat-value {
font-size: var(--font-size-lg);
font-weight: var(--font-weight-semibold);
font-variant-numeric: tabular-nums;
}
/* === 3. Verdict-pill (small) ==============================================
Companion to .verdict-pill-lg (Tier 2). Inline-flex pill used in project
header + sidebar status badges. The larger -lg variant lives in
components-tier2.css; both share the same severity-band semantics. */
.verdict-pill {
display: inline-flex;
align-items: center;
gap: var(--space-1);
padding: 4px 12px;
border-radius: 999px;
font-weight: var(--font-weight-semibold);
font-size: var(--font-size-sm);
}
.verdict-pill--positive { background: var(--color-state-success); color: #fff; }
.verdict-pill--medium { background: var(--color-severity-medium); color: var(--color-severity-medium-on); }
.verdict-pill--critical { background: var(--color-severity-critical); color: #fff; }
.verdict-pill--in-progress {
background: var(--color-bg-soft);
color: var(--color-text-secondary);
border: 1px dashed var(--color-border-moderate);
}
/* === 4. Sidebar nav ======================================================= */
.project-view__nav {
position: sticky;
top: var(--space-6);
background: var(--color-surface);
border: 1px solid var(--color-border-subtle);
border-radius: var(--radius-md);
padding: var(--space-4);
display: flex;
flex-direction: column;
gap: var(--space-3);
}
.project-view__nav-search input {
width: 100%;
box-sizing: border-box;
padding: 6px 10px;
font-size: var(--font-size-sm);
background: var(--color-bg);
color: var(--color-text-primary);
border: 1px solid var(--color-border-moderate);
border-radius: var(--radius-sm);
}
/* === 5. Artifact-list ===================================================== */
.artifact-list {
display: flex;
flex-direction: column;
gap: var(--space-4);
margin: 0;
padding: 0;
list-style: none;
}
.artifact-list__group {
display: flex;
flex-direction: column;
gap: var(--space-1);
}
.artifact-list__group-label {
display: flex;
justify-content: space-between;
align-items: center;
font-size: 10px;
text-transform: uppercase;
letter-spacing: 0.06em;
color: var(--color-text-tertiary);
font-weight: var(--font-weight-semibold);
padding: 0 var(--space-2);
}
.artifact-list__group-count {
background: var(--color-bg-soft);
color: var(--color-text-tertiary);
font-family: var(--font-family-mono);
font-size: 10px;
padding: 1px 6px;
border-radius: 999px;
}
.artifact-list__group-items {
list-style: none;
margin: 0;
padding: 0;
display: flex;
flex-direction: column;
gap: 2px;
}
.artifact-list__item {
display: grid;
grid-template-columns: auto 1fr auto;
align-items: center;
gap: var(--space-2);
padding: var(--artifact-list-item-pad-y) var(--artifact-list-item-pad-x);
border-radius: var(--radius-sm);
cursor: pointer;
background: transparent;
border: 1px solid transparent;
transition: background 120ms ease, border-color 120ms ease;
}
.artifact-list__item:hover { background: var(--color-bg-soft); }
.artifact-list__item[data-state="active"] {
background: var(--color-bg-soft);
border-color: var(--color-primary-500);
box-shadow: inset 3px 0 0 var(--color-primary-500);
padding-left: calc(var(--artifact-list-item-pad-x) - 3px);
}
.artifact-list__item-marker {
width: var(--artifact-marker-size);
height: var(--artifact-marker-size);
border-radius: 50%;
border: var(--artifact-marker-border) solid var(--color-border-moderate);
background: transparent;
flex-shrink: 0;
}
.artifact-list__item[data-state="filled"][data-severity="positive"] .artifact-list__item-marker {
background: var(--color-state-success);
border-color: var(--color-state-success);
}
.artifact-list__item[data-state="filled"][data-severity="medium"] .artifact-list__item-marker {
background: var(--color-severity-medium);
border-color: var(--color-severity-medium);
}
.artifact-list__item[data-state="filled"][data-severity="critical"] .artifact-list__item-marker {
background: var(--color-severity-critical);
border-color: var(--color-severity-critical);
}
.artifact-list__item-body { min-width: 0; }
.artifact-list__item-name {
font-size: var(--font-size-sm);
font-weight: var(--font-weight-medium);
color: var(--color-text-primary);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.artifact-list__item[data-state="empty"] .artifact-list__item-name {
color: var(--color-text-tertiary);
font-weight: var(--font-weight-regular);
}
.artifact-list__item-meta {
font-size: 11px;
color: var(--color-text-tertiary);
}
/* === 6. Artifact-status (mini pill in sidebar) =========================== */
.artifact-status {
font-family: var(--font-family-mono);
font-size: 10px;
font-weight: var(--font-weight-semibold);
padding: 1px 5px;
border-radius: var(--radius-sm);
letter-spacing: 0.04em;
}
.artifact-status[data-severity="positive"] { background: var(--color-severity-low-soft); color: var(--color-severity-low-on); }
.artifact-status[data-severity="medium"] { background: var(--color-severity-medium-soft); color: var(--color-severity-medium-on); }
.artifact-status[data-severity="critical"] { background: var(--color-severity-critical-soft); color: var(--color-severity-critical-on); }
/* === 7. Project-view main panel ========================================== */
.project-view__main {
min-width: 0;
display: flex;
flex-direction: column;
gap: var(--space-5);
}
/* === 8. Project-overview (default dashboard) ============================= */
.project-overview {
display: flex;
flex-direction: column;
gap: var(--space-6);
}
.project-overview__intro {
background: var(--color-surface);
border: 1px solid var(--color-border-subtle);
border-radius: var(--radius-md);
padding: var(--space-5);
}
.project-overview__intro h2 {
font-size: var(--font-size-lg);
margin: 0 0 var(--space-2) 0;
}
.project-overview__intro p {
color: var(--color-text-secondary);
margin: 0;
}
.project-overview__verdict-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
gap: var(--space-3);
}
.project-overview__verdict-tile {
background: var(--color-surface);
border: 1px solid var(--color-border-subtle);
border-left: 4px solid var(--color-border-moderate);
border-radius: var(--radius-md);
padding: var(--space-4);
display: flex;
flex-direction: column;
gap: var(--space-1);
}
.project-overview__verdict-tile[data-severity="positive"] { border-left-color: var(--color-state-success); }
.project-overview__verdict-tile[data-severity="medium"] { border-left-color: var(--color-severity-medium); }
.project-overview__verdict-tile[data-severity="critical"] { border-left-color: var(--color-severity-critical); }
.project-overview__verdict-tile[data-severity="empty"] { border-left-style: dashed; }
.project-overview__verdict-tile-label {
font-size: 11px;
text-transform: uppercase;
letter-spacing: 0.06em;
color: var(--color-text-tertiary);
font-weight: var(--font-weight-semibold);
}
.project-overview__verdict-tile-value {
font-size: var(--font-size-lg);
font-weight: var(--font-weight-semibold);
}
.project-overview__verdict-tile-meta {
font-size: var(--font-size-xs);
color: var(--color-text-secondary);
}
.project-overview__section h3 {
font-size: 12px;
text-transform: uppercase;
letter-spacing: 0.08em;
color: var(--color-text-tertiary);
font-weight: var(--font-weight-semibold);
margin: 0 0 var(--space-3) 0;
}
.project-overview__top-risks,
.project-overview__next-actions {
background: var(--color-surface);
border: 1px solid var(--color-border-subtle);
border-radius: var(--radius-md);
padding: var(--space-5);
}
.project-overview__top-risks ol,
.project-overview__next-actions ol {
list-style: none;
counter-reset: rank;
margin: 0;
padding: 0;
display: flex;
flex-direction: column;
gap: var(--space-2);
}
.project-overview__top-risks li,
.project-overview__next-actions li {
counter-increment: rank;
display: grid;
grid-template-columns: auto 1fr auto;
align-items: center;
gap: var(--space-3);
padding: var(--space-2) var(--space-3);
border-radius: var(--radius-sm);
background: var(--color-bg-soft);
}
.project-overview__top-risks li::before,
.project-overview__next-actions li::before {
content: counter(rank);
font-family: var(--font-family-mono);
font-weight: var(--font-weight-bold);
color: var(--color-text-tertiary);
font-size: var(--font-size-sm);
min-width: 20px;
}
.project-overview__missing-reports {
background: var(--color-surface);
border: 1px solid var(--color-border-subtle);
border-radius: var(--radius-md);
padding: var(--space-5);
}
.project-overview__missing-reports ul {
list-style: none;
margin: 0;
padding: 0;
display: flex;
flex-direction: column;
gap: var(--space-2);
}
.project-overview__missing-reports li {
display: flex;
justify-content: space-between;
align-items: center;
gap: var(--space-3);
padding: var(--space-2) var(--space-3);
background: var(--color-bg-soft);
border-radius: var(--radius-sm);
border-left: 3px dashed var(--color-border-moderate);
}
/* === 9. Artifact-view (one report rendered) ============================== */
.project-view__artifact {
background: var(--color-surface);
border: 1px solid var(--color-border-subtle);
border-radius: var(--radius-md);
padding: var(--space-6);
display: flex;
flex-direction: column;
gap: var(--space-5);
}
.project-view__artifact-header {
display: flex;
justify-content: space-between;
align-items: start;
gap: var(--space-4);
padding-bottom: var(--space-4);
border-bottom: 1px solid var(--color-border-subtle);
}
.project-view__artifact-title {
font-size: var(--font-size-xl);
margin: 0 0 var(--space-1) 0;
}
.project-view__artifact-meta {
font-size: var(--font-size-sm);
color: var(--color-text-tertiary);
margin: 0;
}
.project-view__artifact-actions {
display: flex;
gap: var(--space-2);
flex-shrink: 0;
}
.project-view__artifact-body {
display: flex;
flex-direction: column;
gap: var(--space-5);
}
/* === 10. Empty-artifact-prompt (no report imported yet) ================== */
.empty-artifact-prompt {
background: var(--color-surface);
border: 2px dashed var(--color-border-moderate);
border-radius: var(--radius-md);
padding: var(--space-8);
display: flex;
flex-direction: column;
align-items: center;
gap: var(--space-3);
text-align: center;
}
.empty-artifact-prompt__icon {
font-size: 48px;
opacity: 0.5;
}
.empty-artifact-prompt__title {
font-size: var(--font-size-lg);
margin: 0;
}
.empty-artifact-prompt__text {
color: var(--color-text-secondary);
margin: 0;
max-width: 50ch;
}
.empty-artifact-prompt__actions {
display: flex;
gap: var(--space-2);
margin-top: var(--space-2);
}
/* === 11. Import-modal (overlay) ========================================== */
.import-modal {
position: fixed;
inset: 0;
z-index: 200;
display: none;
}
.import-modal[data-open="true"] {
display: flex;
align-items: center;
justify-content: center;
}
.import-modal__backdrop {
position: absolute;
inset: 0;
background: rgba(0, 0, 0, 0.55);
}
.import-modal__panel {
position: relative;
width: min(720px, 92vw);
max-height: 90vh;
overflow: auto;
background: var(--color-surface);
border: 1px solid var(--color-border-strong);
border-radius: var(--radius-md);
box-shadow: var(--shadow-lg);
display: flex;
flex-direction: column;
}
.import-modal__head {
display: flex;
justify-content: space-between;
align-items: center;
gap: var(--space-3);
padding: var(--space-4) var(--space-5);
border-bottom: 1px solid var(--color-border-subtle);
}
.import-modal__title {
margin: 0;
font-size: var(--font-size-lg);
font-weight: var(--font-weight-semibold);
}
.import-modal__close {
background: transparent;
border: none;
cursor: pointer;
padding: 4px 10px;
color: var(--color-text-tertiary);
font-size: 20px;
line-height: 1;
border-radius: var(--radius-sm);
}
.import-modal__close:hover {
background: var(--color-bg-soft);
color: var(--color-text-primary);
}
.import-modal__form {
padding: var(--space-5);
display: flex;
flex-direction: column;
gap: var(--space-4);
}
.import-modal__form .field {
display: flex;
flex-direction: column;
gap: var(--space-1);
}
.import-modal__form label {
font-size: var(--font-size-sm);
font-weight: var(--font-weight-semibold);
color: var(--color-text-secondary);
}
.import-modal__form select,
.import-modal__form textarea {
width: 100%;
box-sizing: border-box;
padding: var(--space-2) var(--space-3);
background: var(--color-bg);
color: var(--color-text-primary);
border: 1px solid var(--color-border-moderate);
border-radius: var(--radius-sm);
font-family: var(--font-family-mono);
font-size: var(--font-size-sm);
}
.import-modal__form textarea {
resize: vertical;
min-height: 180px;
}
.import-modal__detect {
display: flex;
align-items: center;
gap: var(--space-2);
padding: var(--space-2) var(--space-3);
border-radius: var(--radius-sm);
background: var(--color-severity-low-soft);
color: var(--color-severity-low-on);
font-size: var(--font-size-sm);
}
.import-modal__preview {
border: 1px solid var(--color-border-subtle);
border-radius: var(--radius-sm);
padding: var(--space-3);
background: var(--color-bg);
max-height: 200px;
overflow: auto;
}
.import-modal__preview-label {
font-size: 11px;
text-transform: uppercase;
letter-spacing: 0.06em;
color: var(--color-text-tertiary);
margin-bottom: var(--space-2);
}
.import-modal__footer {
display: flex;
justify-content: flex-end;
gap: var(--space-2);
padding: var(--space-3) var(--space-5);
border-top: 1px solid var(--color-border-subtle);
background: var(--color-bg-soft);
}

View file

@ -191,6 +191,15 @@
color: var(--color-bg); color: var(--color-bg);
border: none; border: none;
} }
/* B-DS-3 (v0.4.0): bobler rendres som <button> i renderMatrixHtml gi
visuell + keyboard-fokus-feedback. Antar at consumer bruker
<button class="matrix__bubble">, ellers bare-virkning ufarlig <span>. */
.matrix__bubble {
cursor: pointer;
transition: transform var(--duration-fast) var(--ease-default);
}
.matrix__bubble:hover { transform: scale(1.15); }
.matrix__bubble:focus-visible { outline: 2px solid var(--color-primary-500); outline-offset: 2px; }
[data-theme="dark"] .matrix__bubble { background: rgba(0,0,0,0.45); color: var(--color-text-primary); border-color: rgba(255,255,255,0.15); } [data-theme="dark"] .matrix__bubble { background: rgba(0,0,0,0.45); color: var(--color-text-primary); border-color: rgba(255,255,255,0.15); }
.matrix__x-label { .matrix__x-label {

View file

@ -102,6 +102,9 @@
--color-scope-security: #A40E26; /* llm-security — crimson */ --color-scope-security: #A40E26; /* llm-security — crimson */
--color-scope-ultraplan: #4338CA; /* ultraplan-local — indigo */ --color-scope-ultraplan: #4338CA; /* ultraplan-local — indigo */
--color-scope-config: #3F5963; /* config-audit — slate */ --color-scope-config: #3F5963; /* config-audit — slate */
--color-scope-voyage: #1B5FB8; /* voyage — aqua-blue */
--color-scope-voyage-soft: #E5EFFA; /* voyage — light tint */
--color-scope-voyage-strong: #143E78; /* voyage — dark strong */
/* ---------- Spacing -------------------------------------------------- */ /* ---------- Spacing -------------------------------------------------- */
--space-1: 4px; --space-1: 4px;
@ -140,6 +143,14 @@
--container-default: 1080px; --container-default: 1080px;
--container-wide: 1280px; --container-wide: 1280px;
--sidebar-width: 280px; --sidebar-width: 280px;
/* ---------- Project-view (Tier 4 — v0.6.0) --------------------------- */
--project-view-nav-width: 280px;
--project-view-collapse-bp: 960px; /* doc-only — referenced by media queries */
--artifact-list-item-pad-y: var(--space-2);
--artifact-list-item-pad-x: var(--space-3);
--artifact-marker-size: 14px;
--artifact-marker-border: 1.5px;
} }
:root { color-scheme: light; } :root { color-scheme: light; }

View file

@ -1,6 +1,6 @@
{ {
"name": "ms-ai-architect", "name": "ms-ai-architect",
"version": "1.14.0", "version": "1.15.0",
"description": "Microsoft AI Solution Architect - structured architecture guidance for the full Microsoft AI stack", "description": "Microsoft AI Solution Architect - structured architecture guidance for the full Microsoft AI stack",
"author": { "author": {
"name": "Kjell Tore Guttormsen" "name": "Kjell Tore Guttormsen"

View file

@ -1,4 +1,6 @@
*.local.md *.local.md
*.local.html
*.local.json
.mcp.json .mcp.json
.DS_Store .DS_Store
.claude/ .claude/

View file

@ -5,6 +5,67 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [1.15.0] - 2026-05-16
### Changed — playground v3 project-view integration
V2 project-surface (screen-tabs + category-tabs + per-command paste-cards) erstattet av v3 project-view (sidebar + main + import-modal). Sluttproduktet av 5 sesjoner: V2-PROJECT-VIEW-SPEC, JS-foundation, renderProjectView/renderProjectArtifact/renderArtifactNav, testing (97 nye assertions), og nå integration + ship.
#### Sesjon 5: integration + ship
- `renderActive()` ruter `project`-surface til ny `renderProjectSurfaceV3()` som wrapper renderProjectView + topbar + app-shell.
- `renderProjectSurface` (152 linjer), `renderCommandSubCard` (87 linjer), `rehydratePasteImports` (15 linjer) slettet.
- `currentProjectScreen` modul-var slettet; `currentProjectTab` beholdt som zombie for `ACTIONS['project-tab']`/`ACTIONS['parse']`/`handlePasteImport` (test-back-compat).
- `ACTIONS['project-screen']` slettet.
- 5 v2-CSS-klasser slettet: `.project-tabs`, `.project-tab`, `.project-tab__count`, `.sub-zone`, `.paste-import-row`, `.project-header__*`, `.command-cards`.
#### Fingerprint-gap lukket
- `requirements.headers`: utvidet til `/^\s*#\s*(EU\s*AI\s*Act\s*[—-]\s*Krav|AI\s*Act-?krav|Krav per|Requirements)/im` (matcher "EU AI Act — Krav for høyrisiko provider+deployer").
- `license.headers`: utvidet til `/^\s*#\s*(Lisens(kart|kapabilitets|-kapabilitets)?(legging|matrise)?|License\s*Mapping)/im` (matcher "Lisens-kapabilitetsmatrise").
- `KNOWN_GAP_FIXTURES = {}` i `tests/test-playground-fingerprints.sh` (var `{ requirements: true, license: true }`).
#### Migrasjon utvidet (v2→v3) med parserFor
- `migrateDataVersion(state, archetypeFor, parserFor)` tredje arg lagt til.
- Hvis `reports[cid].parsed` mangler men `raw_markdown` finnes, kjøres `parserFor(cid)` automatisk.
- `defaultParserFor(cmdId)` resolverer `PARSERS[archetypeFor(cmdId)]`.
- Tre callsites oppdatert: cold-load, import-state, load-demo.
#### Browser-fixes funnet via dogfood
- `components-tier4-project-view.css` lagt til i `<link>`-chain (filen var vendored, men ikke loaded → modal-overlay og two-column layout virket ikke).
- `renderImportModal` setter `data-open="true"``.import-modal`-div (DS-kontrakt: `display: flex` aktiveres kun ved `[data-open="true"]`).
#### Tester
- `bash tests/run-e2e.sh --playground`**386 PASS**, 0 FAIL, 2 WARN (pre-eksisterende: `.cmd-pipeline` reservert; multiselect form-felt).
- v3-static: 219 PASS (var 202 — la til 17 nye renderer-routing-asserts)
- parsers: 70 PASS
- migrations: 16 PASS
- fingerprints: 32 PASS (var 30, 2 WARN → 32, 0 WARN)
- project-view: 30 PASS
- actions: 19 PASS
- `bash tests/validate-plugin.sh` → 219 PASS.
#### Screenshots regenerert til v1.15.0/
24 PNG-er (12 surfaces × 2 tema, retina, fullPage der applicable). Nye surfaces: project-overview, project-artifact-{classify, security, ros, cost, summary}, project-import-modal (viewport-only), project-search.
#### Demo-flyt verifisert i nettleser
- "Last inn demo-data" → 17 artifacts loaded + migrasjonen v2→v3 fyller `parsed`/`verdict`/`keyStats`.
- Sidebar viser alle 17 grupperte commands med severity-badges.
- Aggregate verdict (BLOKKERT) + key stats (17/17, 5/5, 2026-05-04) i header.
- Importer/Re-importer-modal åpner som overlay med backdrop.
- Per-artifact navigasjon (klikk i sidebar) → mounter riktig renderer i main-area.
### Notes on 1.15.0
- Sesjon 5 fullført i én pass — token-budsjettet (~80-100k) holdt.
- v2-mockup.local.html + V2-PROJECT-VIEW-SPEC.local.md beholdt inntil sesjon 8 ship (per scope grenser).
- Pre-eksisterende kosmetiske issues (duplisert artifact-title, "Manglende rapporter"-heading-feil) ikke fikset — utenfor scope for sesjon 5 (integration), planlagt v1.16.0.
## [1.14.0] - 2026-05-08 ## [1.14.0] - 2026-05-08
### Changed — playground root-cause refaktor (6 sesjoner) ### Changed — playground root-cause refaktor (6 sesjoner)

View file

@ -187,12 +187,14 @@ claude --plugin ./plugins/ms-ai-architect
/architect:help /architect:help
``` ```
## Playground (v3 / v1.14.0) ## Playground (v3 / v1.15.0)
Interaktiv decision-builder + rapport-viewer for Microsoft AI-beslutninger. Erstatter v2 5-stegs-pipelinen med en multi-surface-app som persisterer state og visualiserer importerte rapporter inline. Spec: v3-arkitektur dokumentert under `.claude/projects/2026-05-03-playground-v3-architecture/`. v1.10.0-utvidelser dokumentert under `.claude/projects/2026-05-03-ms-ai-architect-v1-10-playground/`. v1.11.0 leverer design-system 100%-adoption (PARALLEL-CSS-migrasjon til DS-konvensjon, inline `<style>`-trim 37%, severity-coded card borders, app-header-restruktur, `.stack-lg` body spacing, AI Act-pyramide bredde-fix). v1.13.0/.1 patchet 10+ symptomatiske visuelle bugs. v1.14.0 leverer root-cause refaktor over 6 sesjoner: B-DS-1/2/3 fikset i shared/ DS v0.4.0 (kanban-card word-break, expansion title-block, matrix-bubble cursor); 3 risk-renderere (renderDpia/Security/Ros) til DS-summary-grid + ros-layout; 6 compliance/govern-renderere bytter lokal `.report-meta`-wrapper mot DS-konvensjon; renderMigrate + renderPoc til expansion-list per fase (slett `.phase-detail`-CSS); 5b-fixes: renderCost p50/p90-objekter ekstrahert via `.monthly` (var "[object Object]"), renderCompare distinctive-token-matching erstatter firstWord-heuristikk, renderUtredning droppet misvisende `role="tab"`. Lokal `<style>`-blokk: 191 → 122 effektive linjer (~36% reduksjon). Alle 17 renderere PASS visuell QA. Interaktiv decision-builder + rapport-viewer for Microsoft AI-beslutninger. Erstatter v2 5-stegs-pipelinen med en multi-surface-app som persisterer state og visualiserer importerte rapporter inline. Spec: v3-arkitektur dokumentert under `.claude/projects/2026-05-03-playground-v3-architecture/`. v1.10.0-utvidelser dokumentert under `.claude/projects/2026-05-03-ms-ai-architect-v1-10-playground/`. v1.11.0 leverer design-system 100%-adoption. v1.13.0/.1 patchet 10+ symptomatiske visuelle bugs. v1.14.0 leverer root-cause refaktor over 6 sesjoner (DS-konvensjon-adopsjon på 14 renderere, lokal CSS halvert).
**v1.15.0 (sesjon 5 av ~8 i v2-prosjektet):** Project-surface byttet fra v2 `renderProjectSurface` (screen-tabs + category-tabs + per-command paste-cards) til v3 `renderProjectView` (sidebar med 17 artifacts + main-area + import-modal overlay). `renderActive()` ruter `project`-surface til `renderProjectSurfaceV3()` som wrapper renderProjectView + topbar + app-shell. V2-surface helt fjernet: `renderProjectSurface` (152 linjer), `renderCommandSubCard` (87 linjer), `rehydratePasteImports` (15 linjer), `currentProjectScreen`, `ACTIONS['project-screen']`, 5 v2-CSS-klasser. Zombie-handlers beholdt for test-back-compat: `currentProjectTab`, `ACTIONS['project-tab']`, `ACTIONS['parse']`, `handlePasteImport`, `window.__handlePasteImport`. 2 fingerprint-gap lukket: requirements.headers + license.headers. `migrateDataVersion` utvidet med `parserFor` → demo-state (kun `raw_markdown`) auto-parses til `project.artifacts[cid]`. Ship-QA-bugfixes: `components-tier4-project-view.css` lagt til i `<link>`-kjeden (manglet → modal-overlay og two-column layout virket ikke); `renderImportModal` setter `data-open="true"` (DS-kontrakt).
- **Fil:** `playground/ms-ai-architect-playground.html` (~3870+ linjer, single-file v3-arkitektur) - **Fil:** `playground/ms-ai-architect-playground.html` (~3870+ linjer, single-file v3-arkitektur)
- **4 surfaces:** Onboarding (18 felles felt — 4 strukturerte / 14 fritekst etter v1.10.0) → Home (prosjekt-liste + 3 entry-tracks) → Catalog (25 commands gruppert i 5 expansion-grupper med søk) → Project (per-prosjekt tabs, command-form-prefill fra felles state, paste-back-import med rapport-visualisering) - **4 surfaces:** Onboarding (18 felles felt — 4 strukturerte / 14 fritekst etter v1.10.0) → Home (prosjekt-liste + 3 entry-tracks) → Catalog (25 commands gruppert i 5 expansion-grupper med søk) → **Project v3** (sidebar med 17 artifacts gruppert i 4 kategorier + søk + main-area med per-artifact view eller overview med top-risks/next-actions + import-modal som DS-overlay)
- **Persistens:** IndexedDB-primær med localStorage-fallback. Schema-versjonert (`STATE_KEY = 'ms-ai-architect-state-v1'`) med eager `MIGRATIONS`-pipeline. v1.10.0 introduserer `dataVersion v1→v2`-migrasjon (idempotent) som backfill-er `verdict`+`keyStats`. - **Persistens:** IndexedDB-primær med localStorage-fallback. Schema-versjonert (`STATE_KEY = 'ms-ai-architect-state-v1'`) med eager `MIGRATIONS`-pipeline. v1.10.0 introduserer `dataVersion v1→v2`-migrasjon (idempotent) som backfill-er `verdict`+`keyStats`.
- **17 rapport-renderers (felles grunnskjelett):** Alle wrapper output via `renderPageShell()` med eyebrow + h1 + valgfri verdict-pill + valgfri key-stats-grid + arketype-spesifikk body. Parser → struktur → HTML rutet via kanonisk archetype-routing-tabell. - **17 rapport-renderers (felles grunnskjelett):** Alle wrapper output via `renderPageShell()` med eyebrow + h1 + valgfri verdict-pill + valgfri key-stats-grid + arketype-spesifikk body. Parser → struktur → HTML rutet via kanonisk archetype-routing-tabell.
- **Foundation-helpers:** `renderPageShell`, `renderVerdictPill`, `renderKeyStatsGrid`, `inferVerdict`, `inferKeyStats`, `KEY_STATS_CONFIG`. - **Foundation-helpers:** `renderPageShell`, `renderVerdictPill`, `renderKeyStatsGrid`, `inferVerdict`, `inferKeyStats`, `KEY_STATS_CONFIG`.
@ -200,23 +202,26 @@ Interaktiv decision-builder + rapport-viewer for Microsoft AI-beslutninger. Erst
- **Theme:** Mørk default + lys theme-toggle med Aksel-tokens i begge moduser (lagt til i v1.10.0). Persistert i `localStorage('ms-ai-architect-theme')`. Theme-bootstrap-script i `<head>` unngår FOUC. - **Theme:** Mørk default + lys theme-toggle med Aksel-tokens i begge moduser (lagt til i v1.10.0). Persistert i `localStorage('ms-ai-architect-theme')`. Theme-bootstrap-script i `<head>` unngår FOUC.
- **Eksport/import:** JSON Decision Record-envelope (Blob + FileReader), schema-versjon-bevisst på import. - **Eksport/import:** JSON Decision Record-envelope (Blob + FileReader), schema-versjon-bevisst på import.
### Validering (v1.14.0-tall) ### Validering (v1.15.0-tall)
| Test | Kommando | Dekning | | Test | Kommando | Dekning |
|------|----------|---------| |------|----------|---------|
| Statisk struktur | `bash tests/test-playground-v3.sh` | 202 PASS — vendored CSS, surfaces, 25 commands, 14 parsere, 17 renderers (felles grunnskjelett), design-system-klasser, action-handlers, Tier 3-bruk, onboarding field-distribution | | Statisk struktur | `bash tests/test-playground-v3.sh` | 219 PASS, 2 WARN (pre-eks.) — vendored CSS, surfaces, 25 commands, 14 parsere, 17 renderers via PROJECT_VIEW_CONFIG.renderers-routing, action-handlers |
| Parser-fixtures | `bash tests/test-playground-parsers.sh` | 70 PASS — 17 fixtures × parser-routing | | Parser-fixtures | `bash tests/test-playground-parsers.sh` | 70 PASS — 17 fixtures × parser-routing |
| Migrasjon | `bash tests/test-playground-migrations.sh` | 7 PASS — v1→v2 idempotent migrasjon | | Migrasjon | `bash tests/test-playground-migrations.sh` | 16 PASS — v1→v2 + v2→v3 idempotent migrasjon |
| Kombinert (E2E) | `bash tests/run-e2e.sh --playground` | 272 PASS — statisk + parser-suiter | | Fingerprints | `bash tests/test-playground-fingerprints.sh` | 32 PASS — 17-fixture true-positive + 4 anti-match + API-sanity |
| Project-view | `bash tests/test-playground-projectview.sh` | 30 PASS — 4 view-states + nav-søk + null-guard |
| ACTIONS | `bash tests/test-playground-actions.sh` | 19 PASS — 6 pure-state-handlers + projectViewUiState |
| Kombinert (E2E) | `bash tests/run-e2e.sh --playground` | 386 PASS, 0 FAIL, 2 WARN |
| Plugin-validering | `bash tests/validate-plugin.sh` | 219 PASS | | Plugin-validering | `bash tests/validate-plugin.sh` | 219 PASS |
| Manuell A11Y QA | Se `playground/MANUAL-CHECKLIST.md` | 10 seksjoner inkl. axe-core-kjøring per surface | | Manuell A11Y QA | Se `playground/MANUAL-CHECKLIST.md` | 10 seksjoner inkl. axe-core-kjøring per surface |
| A11Y-rapport | `playground/A11Y-RAPPORT.md` | Statisk vurdering klar — browser-axe-kjøring pending | | A11Y-rapport | `playground/A11Y-RAPPORT.md` | Statisk vurdering klar — browser-axe-kjøring pending |
### Demo system (v1.11.0) ### Demo system (v1.11.0 → v1.15.0)
`scripts/build-demo-state.mjs` leser alle 17 fixture-filer fra `playground/test-fixtures/` og injiserer dem som en `<script type="application/json" id="demo-state-v1">`-blokk i playground HTML (idempotent — erstatter eksisterende blokk). "Last inn demo-data"-knappen på onboarding-overflaten kaller `ACTIONS['load-demo']` som leser blokken, erstatter alle state-grener via Proxy-mutasjon, og navigerer til project-surface med 17 pre-importerte rapporter. `rehydratePasteImports()` kjøres via `queueMicrotask` etter project-surface render — fyller textareas fra `project.reports[id].raw_markdown` og kaller `handlePasteImport` for hver. `handlePasteImport` har equal-value-guard for å unngå render-loop. `scripts/build-demo-state.mjs` leser alle 17 fixture-filer fra `playground/test-fixtures/` og injiserer dem som en `<script type="application/json" id="demo-state-v1">`-blokk i playground HTML (idempotent — erstatter eksisterende blokk). "Last inn demo-data"-knappen på onboarding-overflaten kaller `ACTIONS['load-demo']` som leser blokken, erstatter alle state-grener via Proxy-mutasjon, kjører `migrateDataVersion` (v2→v3 auto-parser raw_markdown til artifacts), og navigerer til project-surface. Demo viser 17 artifacts gruppert i sidebar med severity-badges, aggregate verdict (BLOKKERT), top-risks-liste, og fungerende re-importer/slett-knapper per artifact.
`tests/screenshot/` inneholder en frittstående Playwright-runner med egen `package.json` (gitignored `node_modules`). `node run.mjs` produserer 24 PNG-er (12 surfaces × 2 tema, retina, fullPage) under `playground/screenshots/v1.14.0/` (v1.10.0 + v1.11.0 beholdt som historisk referanse). Disse committes så forkere ser pluginen uten å installere noe. Demo-org er "Acme Kommune" og demo-prosjekt er "Acme: Kunde-chatbot" — konsistente navn på tvers av alle 17 fixtures (etter v1.11.0 rename fra "Acme AS" / "Demosystem"). `tests/screenshot/` inneholder en frittstående Playwright-runner med egen `package.json` (gitignored `node_modules`). `node run.mjs` produserer 24 PNG-er (12 surfaces × 2 tema) under `playground/screenshots/v1.15.0/`. v1.15.0-surfaces: onboarding-empty, project-overview, project-artifact-{classify,security,ros,cost,summary}, project-import-modal (viewport-only — modal er position:fixed overlay), project-search, home, catalog, onboarding-prefilled. v1.10.0/v1.11.0/v1.14.0 beholdt som historisk referanse. Disse committes så forkere ser pluginen uten å installere noe. Demo-org er "Acme Kommune" og demo-prosjekt er "Acme: Kunde-chatbot".
### Design-system 100%-adoption (v1.11.0 → v1.14.0) ### Design-system 100%-adoption (v1.11.0 → v1.14.0)
@ -283,3 +288,20 @@ kopi av marketplace-rotens `shared/playground-design-system/`. Dette holder plug
**Tilsynsmyndigheter:** Datatilsynet (personvern), nasjonal AI-tilsynsmyndighet (under etablering), sektortilsyn. **Tilsynsmyndigheter:** Datatilsynet (personvern), nasjonal AI-tilsynsmyndighet (under etablering), sektortilsyn.
## Communication patterns
### Linking to local files
When pointing to local files in responses, always use markdown link syntax with a descriptive name:
- Use `[Human-friendly name](file:///absolute/path)` — never bare `file:///...` URLs or autolinks `<file://...>`.
- Always use absolute paths. Never `~/` or relative paths.
- For multiple files, render as a bullet list of named markdown links.
Why: bare `file://` URLs only render the first as clickable across multiple lines. Named markdown links make each entry independently clickable and look cleaner.
Example:
- [Brief](file:///Users/ktg/.../brief.html)
- [Research summary](file:///Users/ktg/.../research/summary.md)

View file

@ -6,7 +6,7 @@
*AI-generated: all code produced by Claude Code through dialog-driven development. [Full disclosure →](../../README.md#ai-generated-code-disclosure)* *AI-generated: all code produced by Claude Code through dialog-driven development. [Full disclosure →](../../README.md#ai-generated-code-disclosure)*
![Version](https://img.shields.io/badge/version-1.14.0-blue) ![Version](https://img.shields.io/badge/version-1.15.0-blue)
![Platform](https://img.shields.io/badge/platform-Claude_Code_Plugin-purple) ![Platform](https://img.shields.io/badge/platform-Claude_Code_Plugin-purple)
![Docs](https://img.shields.io/badge/reference_docs-387-green) ![Docs](https://img.shields.io/badge/reference_docs-387-green)
![Agents](https://img.shields.io/badge/agents-12-orange) ![Agents](https://img.shields.io/badge/agents-12-orange)
@ -638,6 +638,7 @@ Category-to-skill routing is defined in `scripts/skill-gen/category-skill-map.js
| Version | Date | Highlights | | Version | Date | Highlights |
|---------|------|-----------| |---------|------|-----------|
| **1.15.0** | 2026-05-16 | Playground v3 project-view integration — `renderProjectSurface` (v2 screen-tabs + category-tabs + per-command paste-cards) erstattet av `renderProjectView` (sidebar med 17 artifacts + main-area + import-modal overlay). `renderActive()` delegerer nå til `renderProjectSurfaceV3()`. Dead code fjernet: `renderCommandSubCard`, `rehydratePasteImports`, `currentProjectScreen`, `ACTIONS['project-screen']`, 5 v2-CSS-klasser (`.project-tabs`, `.project-tab`, `.project-tab__count`, `.sub-zone`, `.paste-import-row`, `.project-header__*`, `.command-cards`). 2 fingerprint-gap lukket: `requirements.headers` matcher nå "EU AI Act — Krav for høyrisiko..."; `license.headers` matcher "Lisens-kapabilitetsmatrise...". v2→v3 migrasjon utvidet med `parserFor` slik at demo-state med kun `raw_markdown` auto-parses inn i `project.artifacts[cid]`. `components-tier4-project-view.css` wired inn (var ikke loaded — modal-overlay og two-column layout virket ikke). `renderImportModal` setter `data-open="true"` (DS-kontrakt). 219 plugin-validering, 386 E2E playground (32 fingerprints, 219 v3-static, 70 parsers, 16 migrations, 30 project-view, 19 actions), 0 FAIL, 2 WARN (pre-eksisterende). 24 screenshots regenerert til `playground/screenshots/v1.15.0/`. Demo viser nå 17 artifacts navigerbare i sidebar, aggregate verdict (BLOKKERT), top-risks-liste, og fungerende re-importer/slett-knapper per artifact. |
| **1.14.0** | 2026-05-08 | Playground root-cause refaktor — DS-konvensjon-adopsjon på tvers av 14 renderere over 6 sesjoner. Sesjon 2: B-DS-1/2/3 fikset i shared/ DS v0.4.0 (kanban-card word-break, expansion title-block, matrix-bubble cursor). Sesjon 3: renderDpia/Security/Ros til DS-summary-grid + ros-layout. Sesjon 4: 6 compliance/govern-renderere bytter `.report-meta`-wrapper mot DS-konvensjon (renderAiActPyramid, renderRequirements, renderConformity, renderTransparency, renderFria, renderReview). Sesjon 5: renderMigrate + renderPoc → expansion-list per fase (slett `.phase-detail`-CSS). Sesjon 5b: renderCost key-stats viste "[object Object]" (parser-output har p50/p90 = {monthly,yearly}-objekter — nå ekstrahert via `.monthly`); renderCompare distinctive-token-matching erstatter firstWord-heuristikk; renderUtredning droppet misvisende `role="tab"`-attributter. Lokal `<style>`-blokk: 191 → 122 effektive linjer (~36% reduksjon). 17 renderere PASS visuell QA mot demo-data. 219 plugin-validering, 272 E2E playground, 7 migrations PASS. 24 screenshots regenerert. | | **1.14.0** | 2026-05-08 | Playground root-cause refaktor — DS-konvensjon-adopsjon på tvers av 14 renderere over 6 sesjoner. Sesjon 2: B-DS-1/2/3 fikset i shared/ DS v0.4.0 (kanban-card word-break, expansion title-block, matrix-bubble cursor). Sesjon 3: renderDpia/Security/Ros til DS-summary-grid + ros-layout. Sesjon 4: 6 compliance/govern-renderere bytter `.report-meta`-wrapper mot DS-konvensjon (renderAiActPyramid, renderRequirements, renderConformity, renderTransparency, renderFria, renderReview). Sesjon 5: renderMigrate + renderPoc → expansion-list per fase (slett `.phase-detail`-CSS). Sesjon 5b: renderCost key-stats viste "[object Object]" (parser-output har p50/p90 = {monthly,yearly}-objekter — nå ekstrahert via `.monthly`); renderCompare distinctive-token-matching erstatter firstWord-heuristikk; renderUtredning droppet misvisende `role="tab"`-attributter. Lokal `<style>`-blokk: 191 → 122 effektive linjer (~36% reduksjon). 17 renderere PASS visuell QA mot demo-data. 219 plugin-validering, 272 E2E playground, 7 migrations PASS. 24 screenshots regenerert. |
| **1.13.1** | 2026-05-06 | Playground visual bugs patch — 10 bugs identifisert post-v1.13.0 av maintainer i nettleser. Fixet: (B7) classify Forpliktelser indent via `.report-meta` CSS-reset; (B8a) `requirement-expand` ACTIONS-handler manglet — R-01..R-09-rader i AI Act-krav var ikke klikkbare; (B8b) expansion title-main + title-sub display:block så de stables; (B10) kanban-card `word-break:break-word` override DS' break-all; (B11) DPIA matrix-bobler match by description (Pass 1 first-cell exact + Pass 2 any-cell substring); (B12, B13, B15) defensive `display:block; clear:both; width:100%` på top-risks/suppressed-panel/phase-detail/aiact-timeline; (B14) Migrate/POC phases-summary-tabell over phase-detail-seksjoner. 23/23 smoke + 271 E2E + 219 plugin-validering. | | **1.13.1** | 2026-05-06 | Playground visual bugs patch — 10 bugs identifisert post-v1.13.0 av maintainer i nettleser. Fixet: (B7) classify Forpliktelser indent via `.report-meta` CSS-reset; (B8a) `requirement-expand` ACTIONS-handler manglet — R-01..R-09-rader i AI Act-krav var ikke klikkbare; (B8b) expansion title-main + title-sub display:block så de stables; (B10) kanban-card `word-break:break-word` override DS' break-all; (B11) DPIA matrix-bobler match by description (Pass 1 first-cell exact + Pass 2 any-cell substring); (B12, B13, B15) defensive `display:block; clear:both; width:100%` på top-risks/suppressed-panel/phase-detail/aiact-timeline; (B14) Migrate/POC phases-summary-tabell over phase-detail-seksjoner. 23/23 smoke + 271 E2E + 219 plugin-validering. |
| **1.13.0** | 2026-05-06 | Playground visual DS-fixes — 5 bugs identifisert og fikset i fix-pakke som speiler llm-security v7.6.1: (B1) `renderFindingsBlock` + `renderRequirements` outer-wrapper byttet fra `<div class="findings">` (DS grid 360px+1fr klemte indre struktur) til `<section class="report-meta">`; (B2) lokal `.report-table` CSS for 6+ rapporter (Trusler, Kostnadsoversikt, TCO, Risiko, Key Metrics) som manglet styling; (B3) ROS-matrise-bobler byttet `<span>``<button>` med `data-threat-id` + click-handler som scroller til Trusler-tabell-rad og highlighter; (B4) `renderRadarSvg` bumpet 300×300→380×380, R=125, dynamisk `text-anchor` for å unngå label-overlap ved 6+ akser; (B5) `recommendation-card__body` overflow-wrap. 22/22 smoke-test PASS. 219 plugin-validering. 272 E2E. | | **1.13.0** | 2026-05-06 | Playground visual DS-fixes — 5 bugs identifisert og fikset i fix-pakke som speiler llm-security v7.6.1: (B1) `renderFindingsBlock` + `renderRequirements` outer-wrapper byttet fra `<div class="findings">` (DS grid 360px+1fr klemte indre struktur) til `<section class="report-meta">`; (B2) lokal `.report-table` CSS for 6+ rapporter (Trusler, Kostnadsoversikt, TCO, Risiko, Key Metrics) som manglet styling; (B3) ROS-matrise-bobler byttet `<span>``<button>` med `data-threat-id` + click-handler som scroller til Trusler-tabell-rad og highlighter; (B4) `renderRadarSvg` bumpet 300×300→380×380, R=125, dynamisk `text-anchor` for å unngå label-overlap ved 6+ akser; (B5) `recommendation-card__body` overflow-wrap. 22/22 smoke-test PASS. 219 plugin-validering. 272 E2E. |

Binary file not shown.

After

Width:  |  Height:  |  Size: 718 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 245 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 620 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 616 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 879 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 872 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 960 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 954 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 863 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 857 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 744 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 739 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 816 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 809 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 265 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 262 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 594 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 588 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 216 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 214 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 415 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 412 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 247 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 245 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 740 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 735 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 475 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 473 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 668 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 665 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 742 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 740 KiB

View file

@ -1,5 +1,65 @@
# playground-design-system — CHANGELOG # playground-design-system — CHANGELOG
## 0.6.0 — 2026-05-15
### Added — Project-view archetype (Tier 4)
Generic "project as artifact-collection" archetype for plugins where a project owns 0-N read-only report artifacts grouped by category. Default view is an aggregated dashboard; clicking a sidebar item swaps the main panel to the per-artifact render. Edit-mode is paste-import only (no inline editor).
- **New file `components-tier4-project-view.css`** — 11 sections covering:
- `.project-view` + `.project-view__layout` (grid: nav 280px + main 1fr, responsive collapse at 1280 / 960px)
- `.project-view__header` (CSS Grid with eyebrow/title/lede/verdict/key-stats/actions areas)
- `.verdict-pill` (small pill variant — companion to existing `.verdict-pill-lg` in tier2)
- `.project-view__nav` + `.project-view__nav-search` (sticky sidebar with search)
- `.artifact-list` + `__group` / `__group-label` / `__group-count` / `__group-items` / `__item` / `__item-marker` / `__item-body` / `__item-name` / `__item-meta` (grouped, severity-coded sidebar)
- `.artifact-status[data-severity]` (mini-pill: positive | medium | critical)
- `.project-view__main` (main column container)
- `.project-overview` + `__intro` / `__verdict-grid` / `__verdict-tile[data-severity]` / `__section` / `__top-risks` / `__next-actions` / `__missing-reports` (aggregated dashboard)
- `.project-view__artifact` + `__artifact-header` / `__artifact-title` / `__artifact-meta` / `__artifact-actions` / `__artifact-body` (single-rapport viewer wrapper)
- `.empty-artifact-prompt` + `__icon` / `__title` / `__text` / `__actions` (empty-state)
- `.import-modal` + `__backdrop` / `__panel` / `__head` / `__title` / `__close` / `__form` / `__detect` / `__preview` / `__preview-label` / `__footer` (overlay modal for paste-import)
- **6 new tokens in `tokens.css`:**
- `--project-view-nav-width: 280px` — sidebar width at full layout
- `--project-view-collapse-bp: 960px` — doc-only token referenced by responsive breakpoints
- `--artifact-list-item-pad-y: var(--space-2)` — sidebar row vertical padding
- `--artifact-list-item-pad-x: var(--space-3)` — sidebar row horizontal padding
- `--artifact-marker-size: 14px` — sidebar status marker diameter
- `--artifact-marker-border: 1.5px` — sidebar status marker border thickness
### Påvirkning
Endringen er **additiv**: ny komponent-fil + 6 nye tokens, ingen eksisterende selectors eller verdier endres. Plugin-konsumenter (`ms-ai-architect`, `llm-security`, `okr`, `config-audit`, `voyage`) får silent drift mot ny source-commit, men kan re-sync på eget tempo. Bare `ms-ai-architect` og `llm-security` re-syncer i samme commit som denne DS-bumpen (forberedelse til koordinert v1.15.0 / v7.7.0-release etter ~8 sesjoner med JS-implementasjon).
Førsteadoptere: `ms-ai-architect` v1.15.0 (17 artefakter, 5 kategorier) + `llm-security` v7.7.0 (≥18 artefakter, 6 kategorier). State-driven visibility håndteres i plugin-JS, ikke i denne CSS-en — kun aktiv state rendres per pass.
### Plugins som må laste den nye filen
Etter `<link>` til `components-tier3-supplement.css`, legg til:
```html
<link rel="stylesheet" href="vendor/playground-design-system/components-tier4-project-view.css">
```
### For å adoptere v0.6.0
```bash
node scripts/sync-design-system.mjs <plugin-name>
# --force hvis drift detected
```
## 0.5.0 — 2026-05-10
### Added
- **voyage scope tokens (B-DS-4):** `--color-scope-voyage` (aqua-blue `#1B5FB8`), `--color-scope-voyage-soft` (`#E5EFFA`), `--color-scope-voyage-strong` (`#143E78`) appended to scope-color group in `tokens.css`. Matches the existing `--color-scope-{architect,okr,security,ultraplan,config}` family so voyage-playground can use the canonical badge convention.
- **`.badge--scope-voyage`** in `base.css`: white-on-aqua-blue badge variant matching the existing scope-badge family.
### Påvirkning
Endringen er **additiv**: legger TIL voyage-scope-tokens og en ny badge-modifier. Ingen eksisterende selectors eller token-verdier endres. Plugin-konsumenter (llm-security, ms-ai-architect, okr, config-audit) får stale vendor-state mot ny source-commit, men det er silent drift — re-sync skjer på eget tempo neste playground-touch. Bare `voyage` re-syncer i denne commit-en.
Førsteadopter: `voyage` v4.3.0 (multi-sesjons-løp 2026-05-10, sesjon 1 = Wave 0+1 Foundation).
## 0.4.0 — 2026-05-08 ## 0.4.0 — 2026-05-08
### Bug fixes ### Bug fixes

View file

@ -2,16 +2,17 @@
"generated_by": "scripts/sync-design-system.mjs", "generated_by": "scripts/sync-design-system.mjs",
"do_not_edit": true, "do_not_edit": true,
"source": "shared/playground-design-system/", "source": "shared/playground-design-system/",
"source_commit": "9f806469f37742be65f778059bf364308c9d2811", "source_commit": "c1b7bad3899c5cfe9ff90663003609b018aa79a0",
"sync_date": "2026-05-08T17:57:53.412Z", "sync_date": "2026-05-15T14:11:07.444Z",
"file_count": 26, "file_count": 27,
"files": { "files": {
"CHANGELOG.md": "dfbd75552c94848acba3e2503bfad56c1c4bc8cfdcbd638d9409149010913d28", "CHANGELOG.md": "b5018b46cd0830334109e915d23b5554c060412c2b7e132f97f2933e5dd5d79c",
"README.md": "83de0e29b207c979b7b2a3327b7a4ec0c2e1b4d3705ee2677f26c28c3a3ee643", "README.md": "83de0e29b207c979b7b2a3327b7a4ec0c2e1b4d3705ee2677f26c28c3a3ee643",
"base.css": "604fe6839e2ed304bc0ba112a4e045f208b4b3f084f449a1abdb94ce0a1e5263", "base.css": "df0db874473412eb771b7355b589f7478042987756898f0921584286bd5ba70a",
"components-tier2.css": "c2cb7e9d76d6af28d50db654030413777feb2f2f2b93213e598de8b686b14523", "components-tier2.css": "c2cb7e9d76d6af28d50db654030413777feb2f2f2b93213e598de8b686b14523",
"components-tier3-supplement.css": "51fab10377d80029d6552613069d46fd14ce66af77fe6705b1c6bdf5c9e6481e", "components-tier3-supplement.css": "51fab10377d80029d6552613069d46fd14ce66af77fe6705b1c6bdf5c9e6481e",
"components-tier3.css": "c391ea387298ce864bc35078e7e044b2cdd4187e3130456347d91876599ff4b1", "components-tier3.css": "c391ea387298ce864bc35078e7e044b2cdd4187e3130456347d91876599ff4b1",
"components-tier4-project-view.css": "f8f784df70044ecc9bdc862a327b1ee58b201d056581316808a9b60632c5a993",
"components.css": "56fa7392b8b20b567a46f72a8fe9e0205d78ce475eae6b22fc3f50b39b235545", "components.css": "56fa7392b8b20b567a46f72a8fe9e0205d78ce475eae6b22fc3f50b39b235545",
"fonts.css": "e3c3df581c6e4d66e25c555f125c745f6512a33038401089d2519a94ea63ee3d", "fonts.css": "e3c3df581c6e4d66e25c555f125c745f6512a33038401089d2519a94ea63ee3d",
"fonts/Inter-Bold.woff2": "220976705fbec109f43c5cfdceca639e99ace7e51f3eb67292b105d3575eb39b", "fonts/Inter-Bold.woff2": "220976705fbec109f43c5cfdceca639e99ace7e51f3eb67292b105d3575eb39b",
@ -31,6 +32,6 @@
"schemas/finding.schema.json": "0b24797373650582bac232d31a4dd9260593375a0d17259e18f1141a20de8d0c", "schemas/finding.schema.json": "0b24797373650582bac232d31a4dd9260593375a0d17259e18f1141a20de8d0c",
"schemas/okr-set.schema.json": "aa27347fb232a956ec9dcee1775115710e2715a665c8d729ac50b90c6884de26", "schemas/okr-set.schema.json": "aa27347fb232a956ec9dcee1775115710e2715a665c8d729ac50b90c6884de26",
"schemas/ros-threat.schema.json": "e16497c1a6b79d6e78149d6cf1c28ac9df1e93234627a0c546814fb24d6c96d9", "schemas/ros-threat.schema.json": "e16497c1a6b79d6e78149d6cf1c28ac9df1e93234627a0c546814fb24d6c96d9",
"tokens.css": "1499bc2eea0178e35935413c79a10bbee7d49fdfa91bd33eeba3bb9e9acab809" "tokens.css": "63dca13f8341937169fc8e84d3f37ae0c714901fa006c865ea377bd448f87644"
} }
} }

View file

@ -146,6 +146,7 @@ button { font-family: inherit; }
.badge--scope-security { background: var(--color-scope-security); color: #fff; border-color: transparent; } .badge--scope-security { background: var(--color-scope-security); color: #fff; border-color: transparent; }
.badge--scope-ultraplan { background: var(--color-scope-ultraplan); color: #fff; border-color: transparent; } .badge--scope-ultraplan { background: var(--color-scope-ultraplan); color: #fff; border-color: transparent; }
.badge--scope-config { background: var(--color-scope-config); color: #fff; border-color: transparent; } .badge--scope-config { background: var(--color-scope-config); color: #fff; border-color: transparent; }
.badge--scope-voyage { background: var(--color-scope-voyage); color: #fff; border-color: transparent; }
/* ---------- Cards / surfaces ---------- */ /* ---------- Cards / surfaces ---------- */
.card { .card {

View file

@ -0,0 +1,666 @@
/* Code generated by sync-design-system.mjs; DO NOT EDIT. */
/* =============================================================================
Playground Design System components-tier4-project-view.css
v0.6.0 Tier 4 project-view archetype
============================================================================
Generic "project as artifact-collection" archetype. Default-view is an
aggregated overview dashboard; clicking a sidebar item swaps main to a
per-artifact render. Tracks 0-N read-only artifacts; edit-mode is paste-
import only (markdown from terminal parser store).
First adopters: ms-ai-architect v1.15.0 (17 artifacts, 5 categories) +
llm-security v7.7.0 (18 artifacts, 6 categories). Each plugin injects a
PROJECT_VIEW_CONFIG object that maps commands renderers, categories,
verdict-aggregators, missing-report heuristics.
The CSS in this file is plugin-agnostic. Plugin-specific shape (category
names, artifact ordering, custom severity-mappings) lives in JS config.
State-driven visibility is NOT handled here production playgrounds emit
only the active state (overview | artifact | empty | import) per render
pass. The mockup uses body[data-state="..."] for prototyping; production
renders one branch at a time.
============================================================================= */
/* === 1. Project-view top-level layout ===================================== */
.project-view {
display: flex;
flex-direction: column;
gap: var(--space-6);
}
.project-view__layout {
display: grid;
grid-template-columns: var(--project-view-nav-width) 1fr;
gap: var(--space-6);
align-items: start;
}
@media (max-width: 1279px) {
.project-view__layout { grid-template-columns: 240px 1fr; }
}
@media (max-width: 959px) {
.project-view__layout { grid-template-columns: 1fr; }
}
/* === 2. Project-view header =============================================== */
.project-view__header {
background: var(--color-surface);
border: 1px solid var(--color-border-subtle);
border-radius: var(--radius-md);
padding: var(--space-5) var(--space-6);
display: grid;
grid-template-columns: 1fr auto;
grid-template-areas:
"title verdict"
"title keystats"
"actions actions";
gap: var(--space-4) var(--space-6);
align-items: start;
}
.project-view__title-block { grid-area: title; }
.project-view__verdict { grid-area: verdict; justify-self: end; }
.project-view__key-stats { grid-area: keystats; justify-self: end; }
.project-view__actions { grid-area: actions; display: flex; gap: var(--space-2); justify-content: flex-end; }
.project-view__eyebrow {
font-size: 11px;
text-transform: uppercase;
letter-spacing: 0.08em;
color: var(--color-text-tertiary);
font-weight: var(--font-weight-semibold);
margin: 0 0 var(--space-2) 0;
}
.project-view__title {
font-size: var(--font-size-2xl);
font-weight: var(--font-weight-bold);
color: var(--color-text-primary);
margin: 0 0 var(--space-2) 0;
}
.project-view__lede {
color: var(--color-text-secondary);
margin: 0;
max-width: 60ch;
}
.project-view__key-stats {
display: flex;
gap: var(--space-5);
}
.project-view__key-stat-label {
font-size: 10px;
text-transform: uppercase;
color: var(--color-text-tertiary);
letter-spacing: 0.06em;
}
.project-view__key-stat-value {
font-size: var(--font-size-lg);
font-weight: var(--font-weight-semibold);
font-variant-numeric: tabular-nums;
}
/* === 3. Verdict-pill (small) ==============================================
Companion to .verdict-pill-lg (Tier 2). Inline-flex pill used in project
header + sidebar status badges. The larger -lg variant lives in
components-tier2.css; both share the same severity-band semantics. */
.verdict-pill {
display: inline-flex;
align-items: center;
gap: var(--space-1);
padding: 4px 12px;
border-radius: 999px;
font-weight: var(--font-weight-semibold);
font-size: var(--font-size-sm);
}
.verdict-pill--positive { background: var(--color-state-success); color: #fff; }
.verdict-pill--medium { background: var(--color-severity-medium); color: var(--color-severity-medium-on); }
.verdict-pill--critical { background: var(--color-severity-critical); color: #fff; }
.verdict-pill--in-progress {
background: var(--color-bg-soft);
color: var(--color-text-secondary);
border: 1px dashed var(--color-border-moderate);
}
/* === 4. Sidebar nav ======================================================= */
.project-view__nav {
position: sticky;
top: var(--space-6);
background: var(--color-surface);
border: 1px solid var(--color-border-subtle);
border-radius: var(--radius-md);
padding: var(--space-4);
display: flex;
flex-direction: column;
gap: var(--space-3);
}
.project-view__nav-search input {
width: 100%;
box-sizing: border-box;
padding: 6px 10px;
font-size: var(--font-size-sm);
background: var(--color-bg);
color: var(--color-text-primary);
border: 1px solid var(--color-border-moderate);
border-radius: var(--radius-sm);
}
/* === 5. Artifact-list ===================================================== */
.artifact-list {
display: flex;
flex-direction: column;
gap: var(--space-4);
margin: 0;
padding: 0;
list-style: none;
}
.artifact-list__group {
display: flex;
flex-direction: column;
gap: var(--space-1);
}
.artifact-list__group-label {
display: flex;
justify-content: space-between;
align-items: center;
font-size: 10px;
text-transform: uppercase;
letter-spacing: 0.06em;
color: var(--color-text-tertiary);
font-weight: var(--font-weight-semibold);
padding: 0 var(--space-2);
}
.artifact-list__group-count {
background: var(--color-bg-soft);
color: var(--color-text-tertiary);
font-family: var(--font-family-mono);
font-size: 10px;
padding: 1px 6px;
border-radius: 999px;
}
.artifact-list__group-items {
list-style: none;
margin: 0;
padding: 0;
display: flex;
flex-direction: column;
gap: 2px;
}
.artifact-list__item {
display: grid;
grid-template-columns: auto 1fr auto;
align-items: center;
gap: var(--space-2);
padding: var(--artifact-list-item-pad-y) var(--artifact-list-item-pad-x);
border-radius: var(--radius-sm);
cursor: pointer;
background: transparent;
border: 1px solid transparent;
transition: background 120ms ease, border-color 120ms ease;
}
.artifact-list__item:hover { background: var(--color-bg-soft); }
.artifact-list__item[data-state="active"] {
background: var(--color-bg-soft);
border-color: var(--color-primary-500);
box-shadow: inset 3px 0 0 var(--color-primary-500);
padding-left: calc(var(--artifact-list-item-pad-x) - 3px);
}
.artifact-list__item-marker {
width: var(--artifact-marker-size);
height: var(--artifact-marker-size);
border-radius: 50%;
border: var(--artifact-marker-border) solid var(--color-border-moderate);
background: transparent;
flex-shrink: 0;
}
.artifact-list__item[data-state="filled"][data-severity="positive"] .artifact-list__item-marker {
background: var(--color-state-success);
border-color: var(--color-state-success);
}
.artifact-list__item[data-state="filled"][data-severity="medium"] .artifact-list__item-marker {
background: var(--color-severity-medium);
border-color: var(--color-severity-medium);
}
.artifact-list__item[data-state="filled"][data-severity="critical"] .artifact-list__item-marker {
background: var(--color-severity-critical);
border-color: var(--color-severity-critical);
}
.artifact-list__item-body { min-width: 0; }
.artifact-list__item-name {
font-size: var(--font-size-sm);
font-weight: var(--font-weight-medium);
color: var(--color-text-primary);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.artifact-list__item[data-state="empty"] .artifact-list__item-name {
color: var(--color-text-tertiary);
font-weight: var(--font-weight-regular);
}
.artifact-list__item-meta {
font-size: 11px;
color: var(--color-text-tertiary);
}
/* === 6. Artifact-status (mini pill in sidebar) =========================== */
.artifact-status {
font-family: var(--font-family-mono);
font-size: 10px;
font-weight: var(--font-weight-semibold);
padding: 1px 5px;
border-radius: var(--radius-sm);
letter-spacing: 0.04em;
}
.artifact-status[data-severity="positive"] { background: var(--color-severity-low-soft); color: var(--color-severity-low-on); }
.artifact-status[data-severity="medium"] { background: var(--color-severity-medium-soft); color: var(--color-severity-medium-on); }
.artifact-status[data-severity="critical"] { background: var(--color-severity-critical-soft); color: var(--color-severity-critical-on); }
/* === 7. Project-view main panel ========================================== */
.project-view__main {
min-width: 0;
display: flex;
flex-direction: column;
gap: var(--space-5);
}
/* === 8. Project-overview (default dashboard) ============================= */
.project-overview {
display: flex;
flex-direction: column;
gap: var(--space-6);
}
.project-overview__intro {
background: var(--color-surface);
border: 1px solid var(--color-border-subtle);
border-radius: var(--radius-md);
padding: var(--space-5);
}
.project-overview__intro h2 {
font-size: var(--font-size-lg);
margin: 0 0 var(--space-2) 0;
}
.project-overview__intro p {
color: var(--color-text-secondary);
margin: 0;
}
.project-overview__verdict-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
gap: var(--space-3);
}
.project-overview__verdict-tile {
background: var(--color-surface);
border: 1px solid var(--color-border-subtle);
border-left: 4px solid var(--color-border-moderate);
border-radius: var(--radius-md);
padding: var(--space-4);
display: flex;
flex-direction: column;
gap: var(--space-1);
}
.project-overview__verdict-tile[data-severity="positive"] { border-left-color: var(--color-state-success); }
.project-overview__verdict-tile[data-severity="medium"] { border-left-color: var(--color-severity-medium); }
.project-overview__verdict-tile[data-severity="critical"] { border-left-color: var(--color-severity-critical); }
.project-overview__verdict-tile[data-severity="empty"] { border-left-style: dashed; }
.project-overview__verdict-tile-label {
font-size: 11px;
text-transform: uppercase;
letter-spacing: 0.06em;
color: var(--color-text-tertiary);
font-weight: var(--font-weight-semibold);
}
.project-overview__verdict-tile-value {
font-size: var(--font-size-lg);
font-weight: var(--font-weight-semibold);
}
.project-overview__verdict-tile-meta {
font-size: var(--font-size-xs);
color: var(--color-text-secondary);
}
.project-overview__section h3 {
font-size: 12px;
text-transform: uppercase;
letter-spacing: 0.08em;
color: var(--color-text-tertiary);
font-weight: var(--font-weight-semibold);
margin: 0 0 var(--space-3) 0;
}
.project-overview__top-risks,
.project-overview__next-actions {
background: var(--color-surface);
border: 1px solid var(--color-border-subtle);
border-radius: var(--radius-md);
padding: var(--space-5);
}
.project-overview__top-risks ol,
.project-overview__next-actions ol {
list-style: none;
counter-reset: rank;
margin: 0;
padding: 0;
display: flex;
flex-direction: column;
gap: var(--space-2);
}
.project-overview__top-risks li,
.project-overview__next-actions li {
counter-increment: rank;
display: grid;
grid-template-columns: auto 1fr auto;
align-items: center;
gap: var(--space-3);
padding: var(--space-2) var(--space-3);
border-radius: var(--radius-sm);
background: var(--color-bg-soft);
}
.project-overview__top-risks li::before,
.project-overview__next-actions li::before {
content: counter(rank);
font-family: var(--font-family-mono);
font-weight: var(--font-weight-bold);
color: var(--color-text-tertiary);
font-size: var(--font-size-sm);
min-width: 20px;
}
.project-overview__missing-reports {
background: var(--color-surface);
border: 1px solid var(--color-border-subtle);
border-radius: var(--radius-md);
padding: var(--space-5);
}
.project-overview__missing-reports ul {
list-style: none;
margin: 0;
padding: 0;
display: flex;
flex-direction: column;
gap: var(--space-2);
}
.project-overview__missing-reports li {
display: flex;
justify-content: space-between;
align-items: center;
gap: var(--space-3);
padding: var(--space-2) var(--space-3);
background: var(--color-bg-soft);
border-radius: var(--radius-sm);
border-left: 3px dashed var(--color-border-moderate);
}
/* === 9. Artifact-view (one report rendered) ============================== */
.project-view__artifact {
background: var(--color-surface);
border: 1px solid var(--color-border-subtle);
border-radius: var(--radius-md);
padding: var(--space-6);
display: flex;
flex-direction: column;
gap: var(--space-5);
}
.project-view__artifact-header {
display: flex;
justify-content: space-between;
align-items: start;
gap: var(--space-4);
padding-bottom: var(--space-4);
border-bottom: 1px solid var(--color-border-subtle);
}
.project-view__artifact-title {
font-size: var(--font-size-xl);
margin: 0 0 var(--space-1) 0;
}
.project-view__artifact-meta {
font-size: var(--font-size-sm);
color: var(--color-text-tertiary);
margin: 0;
}
.project-view__artifact-actions {
display: flex;
gap: var(--space-2);
flex-shrink: 0;
}
.project-view__artifact-body {
display: flex;
flex-direction: column;
gap: var(--space-5);
}
/* === 10. Empty-artifact-prompt (no report imported yet) ================== */
.empty-artifact-prompt {
background: var(--color-surface);
border: 2px dashed var(--color-border-moderate);
border-radius: var(--radius-md);
padding: var(--space-8);
display: flex;
flex-direction: column;
align-items: center;
gap: var(--space-3);
text-align: center;
}
.empty-artifact-prompt__icon {
font-size: 48px;
opacity: 0.5;
}
.empty-artifact-prompt__title {
font-size: var(--font-size-lg);
margin: 0;
}
.empty-artifact-prompt__text {
color: var(--color-text-secondary);
margin: 0;
max-width: 50ch;
}
.empty-artifact-prompt__actions {
display: flex;
gap: var(--space-2);
margin-top: var(--space-2);
}
/* === 11. Import-modal (overlay) ========================================== */
.import-modal {
position: fixed;
inset: 0;
z-index: 200;
display: none;
}
.import-modal[data-open="true"] {
display: flex;
align-items: center;
justify-content: center;
}
.import-modal__backdrop {
position: absolute;
inset: 0;
background: rgba(0, 0, 0, 0.55);
}
.import-modal__panel {
position: relative;
width: min(720px, 92vw);
max-height: 90vh;
overflow: auto;
background: var(--color-surface);
border: 1px solid var(--color-border-strong);
border-radius: var(--radius-md);
box-shadow: var(--shadow-lg);
display: flex;
flex-direction: column;
}
.import-modal__head {
display: flex;
justify-content: space-between;
align-items: center;
gap: var(--space-3);
padding: var(--space-4) var(--space-5);
border-bottom: 1px solid var(--color-border-subtle);
}
.import-modal__title {
margin: 0;
font-size: var(--font-size-lg);
font-weight: var(--font-weight-semibold);
}
.import-modal__close {
background: transparent;
border: none;
cursor: pointer;
padding: 4px 10px;
color: var(--color-text-tertiary);
font-size: 20px;
line-height: 1;
border-radius: var(--radius-sm);
}
.import-modal__close:hover {
background: var(--color-bg-soft);
color: var(--color-text-primary);
}
.import-modal__form {
padding: var(--space-5);
display: flex;
flex-direction: column;
gap: var(--space-4);
}
.import-modal__form .field {
display: flex;
flex-direction: column;
gap: var(--space-1);
}
.import-modal__form label {
font-size: var(--font-size-sm);
font-weight: var(--font-weight-semibold);
color: var(--color-text-secondary);
}
.import-modal__form select,
.import-modal__form textarea {
width: 100%;
box-sizing: border-box;
padding: var(--space-2) var(--space-3);
background: var(--color-bg);
color: var(--color-text-primary);
border: 1px solid var(--color-border-moderate);
border-radius: var(--radius-sm);
font-family: var(--font-family-mono);
font-size: var(--font-size-sm);
}
.import-modal__form textarea {
resize: vertical;
min-height: 180px;
}
.import-modal__detect {
display: flex;
align-items: center;
gap: var(--space-2);
padding: var(--space-2) var(--space-3);
border-radius: var(--radius-sm);
background: var(--color-severity-low-soft);
color: var(--color-severity-low-on);
font-size: var(--font-size-sm);
}
.import-modal__preview {
border: 1px solid var(--color-border-subtle);
border-radius: var(--radius-sm);
padding: var(--space-3);
background: var(--color-bg);
max-height: 200px;
overflow: auto;
}
.import-modal__preview-label {
font-size: 11px;
text-transform: uppercase;
letter-spacing: 0.06em;
color: var(--color-text-tertiary);
margin-bottom: var(--space-2);
}
.import-modal__footer {
display: flex;
justify-content: flex-end;
gap: var(--space-2);
padding: var(--space-3) var(--space-5);
border-top: 1px solid var(--color-border-subtle);
background: var(--color-bg-soft);
}

View file

@ -102,6 +102,9 @@
--color-scope-security: #A40E26; /* llm-security — crimson */ --color-scope-security: #A40E26; /* llm-security — crimson */
--color-scope-ultraplan: #4338CA; /* ultraplan-local — indigo */ --color-scope-ultraplan: #4338CA; /* ultraplan-local — indigo */
--color-scope-config: #3F5963; /* config-audit — slate */ --color-scope-config: #3F5963; /* config-audit — slate */
--color-scope-voyage: #1B5FB8; /* voyage — aqua-blue */
--color-scope-voyage-soft: #E5EFFA; /* voyage — light tint */
--color-scope-voyage-strong: #143E78; /* voyage — dark strong */
/* ---------- Spacing -------------------------------------------------- */ /* ---------- Spacing -------------------------------------------------- */
--space-1: 4px; --space-1: 4px;
@ -140,6 +143,14 @@
--container-default: 1080px; --container-default: 1080px;
--container-wide: 1280px; --container-wide: 1280px;
--sidebar-width: 280px; --sidebar-width: 280px;
/* ---------- Project-view (Tier 4 — v0.6.0) --------------------------- */
--project-view-nav-width: 280px;
--project-view-collapse-bp: 960px; /* doc-only — referenced by media queries */
--artifact-list-item-pad-y: var(--space-2);
--artifact-list-item-pad-x: var(--space-3);
--artifact-marker-size: 14px;
--artifact-marker-border: 1.5px;
} }
:root { color-scheme: light; } :root { color-scheme: light; }

View file

@ -76,6 +76,10 @@ fi
if $RUN_PLAYGROUND; then if $RUN_PLAYGROUND; then
bash "$SCRIPT_DIR/test-playground-v3.sh" || FAILURES=$((FAILURES + 1)) bash "$SCRIPT_DIR/test-playground-v3.sh" || FAILURES=$((FAILURES + 1))
bash "$SCRIPT_DIR/test-playground-parsers.sh" || FAILURES=$((FAILURES + 1)) bash "$SCRIPT_DIR/test-playground-parsers.sh" || FAILURES=$((FAILURES + 1))
bash "$SCRIPT_DIR/test-playground-migrations.sh" || FAILURES=$((FAILURES + 1))
bash "$SCRIPT_DIR/test-playground-fingerprints.sh" || FAILURES=$((FAILURES + 1))
bash "$SCRIPT_DIR/test-playground-projectview.sh" || FAILURES=$((FAILURES + 1))
bash "$SCRIPT_DIR/test-playground-actions.sh" || FAILURES=$((FAILURES + 1))
fi fi
if $RUN_KB_UPDATE; then if $RUN_KB_UPDATE; then

View file

@ -1,14 +1,12 @@
#!/usr/bin/env node #!/usr/bin/env node
// Capture playground screenshots for v1.14.0 documentation. // Capture playground screenshots for v1.15.0 documentation.
// //
// Opens the single-file playground HTML via file://, drives it through: // v1.15.0: v2 project-surface (renderProjectSurface med screen-tabs +
// - Initial onboarding (empty state) // category-tabs) erstattet av renderProjectView (sidebar med 17 artifacts +
// - "Last inn demo-data" → project surface with all 17 reports rehydrated // main-area med per-artifact view + import-modal). Skjermbilder oppdatert
// - All 4 project screen-tabs (oversikt / rapporter / kontekst / eksport) // til å fange v3-surfaces.
// - Each rapport-tab category (regulatory / security / economy / docs / tool)
// - Both themes (dark + light)
// //
// Output: playground/screenshots/v1.14.0/<surface>-<theme>.png // Output: playground/screenshots/v1.15.0/<surface>-<theme>.png
// //
// Usage: // Usage:
// cd tests/screenshot // cd tests/screenshot
@ -25,7 +23,7 @@ const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename); const __dirname = dirname(__filename);
const PLUGIN_ROOT = resolve(__dirname, '..', '..'); const PLUGIN_ROOT = resolve(__dirname, '..', '..');
const HTML_PATH = join(PLUGIN_ROOT, 'playground', 'ms-ai-architect-playground.html'); const HTML_PATH = join(PLUGIN_ROOT, 'playground', 'ms-ai-architect-playground.html');
const OUT_DIR = join(PLUGIN_ROOT, 'playground', 'screenshots', 'v1.14.0'); const OUT_DIR = join(PLUGIN_ROOT, 'playground', 'screenshots', 'v1.15.0');
const HTML_URL = 'file://' + HTML_PATH; const HTML_URL = 'file://' + HTML_PATH;
const VIEWPORT = { width: 1440, height: 900 }; const VIEWPORT = { width: 1440, height: 900 };
@ -49,7 +47,6 @@ async function clearState(page) {
await page.evaluate(() => { await page.evaluate(() => {
try { localStorage.clear(); } catch (e) {} try { localStorage.clear(); } catch (e) {}
try { try {
// Best-effort: clear IndexedDB databases.
const dbs = ['ms-ai-architect-state-v1', 'ms-ai-architect-playground']; const dbs = ['ms-ai-architect-state-v1', 'ms-ai-architect-playground'];
dbs.forEach((n) => indexedDB.deleteDatabase(n)); dbs.forEach((n) => indexedDB.deleteDatabase(n));
} catch (e) {} } catch (e) {}
@ -61,9 +58,9 @@ async function loadDemo(page) {
const action = document.querySelector('[data-action="load-demo"]'); const action = document.querySelector('[data-action="load-demo"]');
if (action) action.click(); if (action) action.click();
}); });
// Wait for project surface to render + rehydrate paste-imports.
await page.waitForSelector('[data-surface="project"]:not([hidden])', { timeout: 5000 }); await page.waitForSelector('[data-surface="project"]:not([hidden])', { timeout: 5000 });
await page.waitForTimeout(800); // settle rehydrate microtasks // Settle migrasjon (v2→v3 auto-parse) + render.
await page.waitForTimeout(1200);
} }
async function clickAction(page, action) { async function clickAction(page, action) {
@ -71,28 +68,46 @@ async function clickAction(page, action) {
const el = document.querySelector('[data-action="' + a + '"]'); const el = document.querySelector('[data-action="' + a + '"]');
if (el) el.click(); if (el) el.click();
}, action); }, action);
await page.waitForTimeout(300);
}
async function clickProjectTab(page, tabId) {
await page.evaluate((t) => {
const el = document.querySelector('[data-action="project-tab"][data-tab="' + t + '"]');
if (el) el.click();
}, tabId);
await page.waitForTimeout(400); await page.waitForTimeout(400);
} }
async function clickProjectScreen(page, screenId) { async function selectArtifact(page, artifactId) {
await page.evaluate((s) => { await page.evaluate((id) => {
const el = document.querySelector('[data-action="project-screen"][data-screen="' + s + '"]'); const el = document.querySelector('[data-action="project-select-artifact"][data-artifact-id="' + id + '"]');
if (el) el.click(); if (el) el.click();
}, screenId); }, artifactId);
await page.waitForTimeout(500);
}
async function openImportModal(page, prefillCmd) {
await page.evaluate((cid) => {
// Foretrukket: artifact-reimport-knappen (har eksisterende markdown).
if (cid) {
const el = document.querySelector('[data-action="artifact-reimport"][data-command="' + cid + '"]');
if (el) { el.click(); return; }
}
// Fallback: generisk "Importer rapport"-knapp.
const open = document.querySelector('[data-action="import-open"]');
if (open) open.click();
}, prefillCmd);
await page.waitForSelector('[data-import-modal]', { timeout: 3000 });
await page.waitForTimeout(400); await page.waitForTimeout(400);
} }
async function shoot(page, name) { async function setSearchQuery(page, query) {
await page.evaluate((q) => {
const input = document.querySelector('[data-project-search]');
if (!input) return;
input.value = q;
input.dispatchEvent(new Event('input', { bubbles: true }));
}, query);
await page.waitForTimeout(400);
}
async function shoot(page, name, opts) {
const path = join(OUT_DIR, name + '.png'); const path = join(OUT_DIR, name + '.png');
await page.screenshot({ path, fullPage: FULL_PAGE }); const useFullPage = (opts && opts.fullPage != null) ? opts.fullPage : FULL_PAGE;
await page.screenshot({ path, fullPage: useFullPage });
console.log(' → ' + name + '.png'); console.log(' → ' + name + '.png');
} }
@ -106,52 +121,58 @@ async function captureAllSurfaces(page, theme) {
await setTheme(page, theme); await setTheme(page, theme);
await shoot(page, '01-onboarding-empty-' + theme); await shoot(page, '01-onboarding-empty-' + theme);
// 2. Load demo → project surface (rapporter screen, regulatory tab default) // 2. Load demo → project-view overview (default — ingen artifact valgt)
await loadDemo(page); await loadDemo(page);
await setTheme(page, theme); await setTheme(page, theme);
await shoot(page, '02-project-rapporter-regulatory-' + theme); await shoot(page, '02-project-overview-' + theme);
// 3. Project tab cycle (5 categories) // 3-7. 5 sample artifacts som dekker arketype-bredden
const TABS = [ const SAMPLE_ARTIFACTS = [
{ id: 'security', label: 'security' }, { id: 'classify', label: 'classify' }, // AI Act-pyramide
{ id: 'economy', label: 'economy' }, { id: 'security', label: 'security' }, // 6×5 sikkerhets-matrise
{ id: 'documentation', label: 'documentation' }, { id: 'ros', label: 'ros' }, // ROS matrise + radar
{ id: 'tool', label: 'tool' } { id: 'cost', label: 'cost' }, // P10/P50/P90 distribusjon
{ id: 'summary', label: 'summary' } // Beslutningsnotat
]; ];
for (const tab of TABS) { for (let i = 0; i < SAMPLE_ARTIFACTS.length; i++) {
await clickProjectTab(page, tab.id); const a = SAMPLE_ARTIFACTS[i];
await page.waitForTimeout(500); await selectArtifact(page, a.id);
await shoot(page, '03-project-rapporter-' + tab.label + '-' + theme); const num = String(3 + i).padStart(2, '0');
await shoot(page, num + '-project-artifact-' + a.label + '-' + theme);
} }
// 4. Project screen-tabs (oversikt / kontekst / eksport) // 8. Import-modal åpen (med prefill fra eksisterende ros-artifact)
await clickProjectScreen(page, 'oversikt'); // Viewport-only (ikke fullPage) — modal er position:fixed; fullPage
await shoot(page, '04-project-oversikt-' + theme); // skroller forbi overlay-en og kaster bort kontekst.
await clickProjectScreen(page, 'kontekst'); await openImportModal(page, 'ros');
await shoot(page, '05-project-kontekst-' + theme); await page.evaluate(() => window.scrollTo(0, 0));
await clickProjectScreen(page, 'eksport'); await page.waitForTimeout(200);
await shoot(page, '06-project-eksport-' + theme); await shoot(page, '08-project-import-modal-' + theme, { fullPage: false });
await clickAction(page, 'import-close');
await page.waitForTimeout(300);
// Back to rapporter for nav screenshots // 9. Sidebar-søk aktivt (filtrer på "ros")
await clickProjectScreen(page, 'rapporter'); await setSearchQuery(page, 'ros');
await shoot(page, '09-project-search-' + theme);
await setSearchQuery(page, ''); // reset
// 5. Home surface // 10. Home surface
await clickAction(page, 'goto-home'); await clickAction(page, 'goto-home');
await page.waitForSelector('[data-surface="home"]:not([hidden])'); await page.waitForSelector('[data-surface="home"]:not([hidden])');
await page.waitForTimeout(300); await page.waitForTimeout(300);
await shoot(page, '07-home-' + theme); await shoot(page, '10-home-' + theme);
// 6. Catalog surface // 11. Catalog surface
await clickAction(page, 'goto-catalog'); await clickAction(page, 'goto-catalog');
await page.waitForSelector('[data-surface="catalog"]:not([hidden])'); await page.waitForSelector('[data-surface="catalog"]:not([hidden])');
await page.waitForTimeout(300); await page.waitForTimeout(300);
await shoot(page, '08-catalog-' + theme); await shoot(page, '11-catalog-' + theme);
// 7. Onboarding (with prefilled state from demo) // 12. Onboarding prefilled (post-demo med org-felter fylt)
await clickAction(page, 'goto-onboarding'); await clickAction(page, 'goto-onboarding');
await page.waitForSelector('[data-surface="onboarding"]:not([hidden])'); await page.waitForSelector('[data-surface="onboarding"]:not([hidden])');
await page.waitForTimeout(300); await page.waitForTimeout(300);
await shoot(page, '09-onboarding-prefilled-' + theme); await shoot(page, '12-onboarding-prefilled-' + theme);
} }
async function main() { async function main() {
@ -160,7 +181,7 @@ async function main() {
const browser = await chromium.launch(); const browser = await chromium.launch();
const context = await browser.newContext({ const context = await browser.newContext({
viewport: VIEWPORT, viewport: VIEWPORT,
deviceScaleFactor: 2 // crisper screenshots for retina deviceScaleFactor: 2
}); });
const page = await context.newPage(); const page = await context.newPage();
page.on('console', (msg) => { page.on('console', (msg) => {

View file

@ -0,0 +1,53 @@
#!/usr/bin/env node
// Mockup verification screenshots — sesjon 2 (DS-hoist).
// Captures the 4 mockup states × 2 themes to confirm visual identity
// after hoisting project-view CSS to shared DS.
//
// Output: playground/screenshots/v2-mockup/<state>-<theme>.png
import { chromium } from 'playwright';
import { fileURLToPath } from 'node:url';
import { dirname, resolve, join } from 'node:path';
import { mkdirSync, existsSync } from 'node:fs';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const PLUGIN_ROOT = resolve(__dirname, '..', '..');
const HTML_PATH = join(PLUGIN_ROOT, 'playground', 'v2-mockup.local.html');
const OUT_DIR = join(PLUGIN_ROOT, 'playground', 'screenshots', 'v2-mockup');
const HTML_URL = 'file://' + HTML_PATH;
const VIEWPORT = { width: 1440, height: 1200 };
const STATES = ['overview', 'artifact', 'empty', 'import'];
const THEMES = ['dark', 'light'];
if (!existsSync(OUT_DIR)) mkdirSync(OUT_DIR, { recursive: true });
const browser = await chromium.launch();
const ctx = await browser.newContext({ viewport: VIEWPORT, deviceScaleFactor: 2 });
const page = await ctx.newPage();
await page.goto(HTML_URL, { waitUntil: 'load' });
for (const theme of THEMES) {
await page.evaluate((t) => {
document.documentElement.setAttribute('data-theme', t);
const btns = document.querySelectorAll('[data-action="set-theme"]');
btns.forEach((b) => b.setAttribute('aria-pressed', b.getAttribute('data-target') === t ? 'true' : 'false'));
}, theme);
await page.waitForTimeout(200);
for (const state of STATES) {
await page.evaluate((s) => {
const btn = document.querySelector(`[data-action="set-state"][data-target="${s}"]`);
if (btn) btn.click();
}, state);
await page.waitForTimeout(300);
const outPath = join(OUT_DIR, `${state}-${theme}.png`);
await page.screenshot({ path: outPath, fullPage: true });
console.log(`${state}-${theme}.png`);
}
}
await browser.close();
console.log('\nDone. 8 screenshots → ' + OUT_DIR);

View file

@ -0,0 +1,248 @@
#!/bin/bash
# test-playground-actions.sh — Playground v3 ACTIONS handler-state-effekter
#
# Verifiserer at de 6 pure-state-ACTIONS-handlerne (project-select-artifact,
# project-show-overview, import-open, import-close, artifact-reimport,
# artifact-delete) muterer state korrekt.
#
# Handlerne import-detect og import-save krever document/DOM og dekkes ikke
# her — de testes implisitt via browser-walkthrough før release og via
# manuell QA per playground/MANUAL-CHECKLIST.md.
#
# Bash 3.2-kompatibel. Bruker node til JS-eval. Ingen npm-deps.
set -euo pipefail
PLUGIN_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
HTML_FILE="$PLUGIN_ROOT/playground/ms-ai-architect-playground.html"
# shellcheck disable=SC1091
source "$PLUGIN_ROOT/tests/lib/e2e-helpers.sh"
init_suite "Playground v3 — ACTIONS handler state-effekter"
if [ ! -f "$HTML_FILE" ]; then
fail "HTML-fila finnes ikke: $HTML_FILE"
print_summary; exit 1
fi
pass "HTML-fil finnes: $(basename "$HTML_FILE")"
NODE_OUT=$(node -e '
const fs = require("fs");
const htmlPath = process.argv[1];
const htmlSrc = fs.readFileSync(htmlPath, "utf8");
// Ekstraher ACTIONS-blokken fra "// PROJECT-VIEW V2 ACTIONS" (sesjon 3-kommentaren)
// til starten av smart-detect-blokken. Tar med projectViewUiState + 6 handlere.
const startMarker = "// PROJECT-VIEW V2 ACTIONS (Sesjon 3)";
const endMarker = "// Smart-detect på textarea-input i import-modal.";
const startIdx = htmlSrc.indexOf(startMarker);
const endIdx = htmlSrc.indexOf(endMarker);
if (startIdx < 0 || endIdx < 0) {
console.error("MARKERS_MISSING start=" + startIdx + " end=" + endIdx);
process.exit(2);
}
const block = htmlSrc.substring(startIdx, endIdx);
// Stubs som gir handlerne et minimums-miljø uten document/window.
const stubs = `
let __store_state = null;
const store = {
get state() { return __store_state; },
save: function () {}
};
function setStoreState(s) { __store_state = s; }
function findProject(id) {
const list = (store.state && store.state.projects) || [];
for (let i = 0; i < list.length; i++) if (list[i].id === id) return list[i];
return null;
}
let __renderCount = 0;
function scheduleRender() { __renderCount++; }
let __confirmAnswer = true;
function confirm() { return __confirmAnswer; }
function setConfirmAnswer(b) { __confirmAnswer = b; }
function renderCount() { return __renderCount; }
function resetRenderCount() { __renderCount = 0; }
const ACTIONS = {};
`;
const wrapped = stubs + block + "\nreturn { ACTIONS, setStoreState, setConfirmAnswer, renderCount, resetRenderCount, projectViewUiState };";
let api;
try {
api = (new Function(wrapped))();
} catch (e) {
console.error("EVAL_FAILED: " + e.message);
process.exit(3);
}
function emit(ok, desc) { console.log((ok ? "PASS" : "FAIL") + "\t" + desc); }
function freshState(opts) {
const o = opts || {};
return {
schemaVersion: 1,
dataVersion: 3,
activeProjectId: "p1",
projects: [{
id: "p1",
name: "Demo",
artifacts: {
classify: { commandId: "classify", raw_markdown: "RAW_CLASSIFY", parsed: { risk_level: "minimal" }, verdict: "go", keyStats: [], importedAt: "x", updatedAt: "x" },
ros: { commandId: "ros", raw_markdown: "RAW_ROS", parsed: { threats: [] }, verdict: "approved", keyStats: [], importedAt: "x", updatedAt: "x" }
},
reports: {
classify: { raw_markdown: "RAW_CLASSIFY", parsed: { risk_level: "minimal" } },
ros: { raw_markdown: "RAW_ROS", parsed: { threats: [] } }
}
}],
ui: o.ui || {}
};
}
// ---- API ----
emit(typeof api.ACTIONS === "object" && api.ACTIONS !== null, "ACTIONS-objekt eksponert");
emit(typeof api.ACTIONS["project-select-artifact"] === "function", "project-select-artifact handler finnes");
emit(typeof api.ACTIONS["project-show-overview"] === "function", "project-show-overview handler finnes");
emit(typeof api.ACTIONS["import-open"] === "function", "import-open handler finnes");
emit(typeof api.ACTIONS["import-close"] === "function", "import-close handler finnes");
emit(typeof api.ACTIONS["artifact-reimport"] === "function", "artifact-reimport handler finnes");
emit(typeof api.ACTIONS["artifact-delete"] === "function", "artifact-delete handler finnes");
// ---- project-select-artifact ----
(function () {
const state = freshState();
api.setStoreState(state);
api.resetRenderCount();
api.ACTIONS["project-select-artifact"]({}, { dataset: { artifactId: "classify" } });
const ok = state.ui.projectView && state.ui.projectView.selectedArtifactId === "classify"
&& api.renderCount() === 1;
emit(ok, "project-select-artifact setter selectedArtifactId og trigger scheduleRender (got=" + (state.ui.projectView && state.ui.projectView.selectedArtifactId) + ")");
})();
// ---- project-select-artifact uten artifactId — no-op ----
(function () {
const state = freshState({ ui: { projectView: { selectedArtifactId: "ros", searchQuery: "" }, importModal: { open: false, prefillCommandId: "", prefillMarkdown: "" } } });
api.setStoreState(state);
api.resetRenderCount();
api.ACTIONS["project-select-artifact"]({}, { dataset: {} });
const ok = state.ui.projectView.selectedArtifactId === "ros" && api.renderCount() === 0;
emit(ok, "project-select-artifact uten dataset.artifactId → no-op (selectedArtifactId beholdt, ingen render)");
})();
// ---- project-show-overview ----
(function () {
const state = freshState({ ui: { projectView: { selectedArtifactId: "classify", searchQuery: "" }, importModal: { open: false, prefillCommandId: "", prefillMarkdown: "" } } });
api.setStoreState(state);
api.resetRenderCount();
api.ACTIONS["project-show-overview"]({}, {});
const ok = state.ui.projectView.selectedArtifactId === null && api.renderCount() === 1;
emit(ok, "project-show-overview tømmer selectedArtifactId");
})();
// ---- import-open med prefill-command (eksisterende artifact gir prefillMarkdown) ----
(function () {
const state = freshState();
api.setStoreState(state);
api.resetRenderCount();
api.ACTIONS["import-open"]({}, { dataset: { prefillCommand: "ros" } });
const m = state.ui.importModal;
const ok = m && m.open === true && m.prefillCommandId === "ros"
&& m.prefillMarkdown === "RAW_ROS" && api.renderCount() === 1;
emit(ok, "import-open med prefillCommand=\"ros\" åpner modal + prefyller raw_markdown");
})();
// ---- import-open uten prefill-command ----
(function () {
const state = freshState();
api.setStoreState(state);
api.resetRenderCount();
api.ACTIONS["import-open"]({}, { dataset: {} });
const m = state.ui.importModal;
const ok = m && m.open === true && m.prefillCommandId === "" && m.prefillMarkdown === "";
emit(ok, "import-open uten prefill → modal åpnet, ingen prefyll");
})();
// ---- import-close ----
(function () {
const state = freshState({ ui: { projectView: { selectedArtifactId: null, searchQuery: "" }, importModal: { open: true, prefillCommandId: "ros", prefillMarkdown: "RAW_ROS" } } });
api.setStoreState(state);
api.resetRenderCount();
api.ACTIONS["import-close"]({}, {});
const m = state.ui.importModal;
const ok = m.open === false && m.prefillCommandId === "" && m.prefillMarkdown === "" && api.renderCount() === 1;
emit(ok, "import-close tilbakestiller modal-state");
})();
// ---- artifact-reimport prefyller modal med eksisterende markdown ----
(function () {
const state = freshState();
api.setStoreState(state);
api.resetRenderCount();
api.ACTIONS["artifact-reimport"]({}, { dataset: { command: "classify" } });
const m = state.ui.importModal;
const ok = m.open === true && m.prefillCommandId === "classify" && m.prefillMarkdown === "RAW_CLASSIFY";
emit(ok, "artifact-reimport åpner modal med prefill fra eksisterende artifact");
})();
// ---- artifact-delete med confirm=false → no-op ----
(function () {
const state = freshState();
api.setStoreState(state);
api.setConfirmAnswer(false);
api.resetRenderCount();
api.ACTIONS["artifact-delete"]({}, { dataset: { command: "classify" } });
const ok = state.projects[0].artifacts.classify !== undefined && api.renderCount() === 0;
emit(ok, "artifact-delete med confirm=false → artifact bevart, ingen render");
})();
// ---- artifact-delete med confirm=true → sletter + clearer selectedArtifactId ----
(function () {
const state = freshState({ ui: { projectView: { selectedArtifactId: "classify", searchQuery: "" }, importModal: { open: false, prefillCommandId: "", prefillMarkdown: "" } } });
api.setStoreState(state);
api.setConfirmAnswer(true);
api.resetRenderCount();
api.ACTIONS["artifact-delete"]({}, { dataset: { command: "classify" } });
const p = state.projects[0];
const ok = p.artifacts.classify === undefined && p.reports.classify === undefined
&& state.ui.projectView.selectedArtifactId === null && api.renderCount() === 1;
emit(ok, "artifact-delete med confirm=true sletter artifact + reports + clearer selectedArtifactId");
})();
// ---- artifact-delete bevarer andre artifacts ----
(function () {
const state = freshState();
api.setStoreState(state);
api.setConfirmAnswer(true);
api.ACTIONS["artifact-delete"]({}, { dataset: { command: "ros" } });
const p = state.projects[0];
const ok = p.artifacts.classify !== undefined && p.artifacts.ros === undefined;
emit(ok, "artifact-delete sletter bare den ene — andre artifacts bevart");
})();
// ---- projectViewUiState initialiserer state-grener idempotent ----
(function () {
const state = { projects: [], ui: {} };
api.setStoreState(state);
const ui1 = api.projectViewUiState();
const ui2 = api.projectViewUiState();
const ok = ui1 === ui2
&& ui1.projectView && ui1.projectView.selectedArtifactId === null && ui1.projectView.searchQuery === ""
&& ui1.importModal && ui1.importModal.open === false;
emit(ok, "projectViewUiState initialiserer projectView + importModal idempotent");
})();
' "$HTML_FILE" 2>&1) || NODE_RC=$?
if [ "${NODE_RC:-0}" -ne 0 ]; then
fail "node-eval feilet (rc=${NODE_RC:-0}): $NODE_OUT"
print_summary; exit 1
fi
while IFS=$'\t' read -r status desc; do
case "$status" in
PASS) pass "$desc" ;;
FAIL) fail "$desc" ;;
WARN) warn "$desc" ;;
esac
done <<< "$NODE_OUT"
print_summary

View file

@ -0,0 +1,218 @@
#!/bin/bash
# test-playground-fingerprints.sh — Playground v3 inferCommandIdFromMarkdown
#
# Verifiserer:
# 1. PROJECT_VIEW_V2_BEGIN/END-markørene finnes
# 2. inferCommandIdFromMarkdown matcher hver av 17 test-fixtures mot
# egen commandId med confidence >= 0.6 (true-positive matrise)
# 3. False-positive immunity:
# - tom streng → null
# - 100 tegn lorem ipsum → null
# - kun "# Hello"-header → null
# - mixed content med kun classify-header → matcher classify
# 4. Sanity-asserts på COMMAND_FINGERPRINTS-shape og fingerprintScore-API
#
# Bash 3.2-kompatibel. Bruker node til JS-eval; ingen npm-deps.
set -euo pipefail
PLUGIN_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
HTML_FILE="$PLUGIN_ROOT/playground/ms-ai-architect-playground.html"
FIXTURE_DIR="$PLUGIN_ROOT/playground/test-fixtures"
# shellcheck disable=SC1091
source "$PLUGIN_ROOT/tests/lib/e2e-helpers.sh"
init_suite "Playground v3 — inferCommandIdFromMarkdown fingerprints"
# ---- 1. Filer eksisterer ----
if [ ! -f "$HTML_FILE" ]; then
fail "HTML-fila finnes ikke: $HTML_FILE"
print_summary; exit 1
fi
pass "HTML-fil finnes: $(basename "$HTML_FILE")"
if [ ! -d "$FIXTURE_DIR" ]; then
fail "Fixture-mappe mangler: $FIXTURE_DIR"
print_summary; exit 1
fi
pass "Fixture-mappe finnes"
# ---- 2. PROJECT_VIEW_V2-markører eksisterer ----
if grep -q "PROJECT_VIEW_V2_BEGIN" "$HTML_FILE" && grep -q "PROJECT_VIEW_V2_END" "$HTML_FILE"; then
pass "PROJECT_VIEW_V2_BEGIN/END markører finnes"
else
fail "Mangler PROJECT_VIEW_V2-markører i HTML"
print_summary; exit 1
fi
# ---- 3. Kjør hele test-matrisen i én node-prosess (effektivt) ----
# Node-skriptet:
# - ekstraherer PROJECT_VIEW_V2-blokken
# - stubber dependencies
# - leser alle 17 fixture-filer
# - kjører true-positive + anti-match + sanity-tester
# - skriver én linje per assert: "PASS|FAIL <description>"
NODE_OUT=$(node -e '
const fs = require("fs");
const path = require("path");
const htmlPath = process.argv[1];
const fixtureDir = process.argv[2];
const html = fs.readFileSync(htmlPath, "utf8");
const beginMarker = "// === PROJECT_VIEW_V2_BEGIN ===";
const endMarker = "// === PROJECT_VIEW_V2_END ===";
const beginIdx = html.indexOf(beginMarker);
const endIdx = html.indexOf(endMarker);
if (beginIdx < 0 || endIdx < 0) {
console.error("MARKER_MISSING");
process.exit(2);
}
const block = html.substring(beginIdx, endIdx + endMarker.length);
// 17 produces_report-renderers per PROJECT_VIEW_CONFIG.renderers.
const RENDERER_IDS = ["renderAiActPyramid","renderRequirements","renderTransparency","renderFria","renderConformity","renderDpia","renderSecurity","renderRos","renderReview","renderCost","renderLicense","renderMigrate","renderAdr","renderSummary","renderPoc","renderUtredning","renderCompare"];
const rendererStubs = RENDERER_IDS.map(function (n) { return n + ": function () {}"; }).join(", ");
// CATALOG.commands: 17 produces_report=true entries med id, label, category, renderer.
const COMMANDS = [
{ id: "classify", category: "regulatory", label: "EU AI Act — Klassifisering", renderer: "renderAiActPyramid", produces_report: true, report_archetype: "aiact" },
{ id: "requirements", category: "regulatory", label: "EU AI Act — Krav per risiko", renderer: "renderRequirements", produces_report: true, report_archetype: "requirements-list" },
{ id: "transparency", category: "regulatory", label: "Transparensnotis (Art. 13/50)", renderer: "renderTransparency", produces_report: true, report_archetype: "text-document" },
{ id: "frimpact", category: "regulatory", label: "FRIA (Art. 27)", renderer: "renderFria", produces_report: true, report_archetype: "fria" },
{ id: "conformity", category: "regulatory", label: "Samsvarsvurdering (Art. 43)", renderer: "renderConformity", produces_report: true, report_archetype: "conformity-checklist" },
{ id: "dpia", category: "regulatory", label: "DPIA / PVK", renderer: "renderDpia", produces_report: true, report_archetype: "matrix-risk" },
{ id: "security", category: "security", label: "Sikkerhetsvurdering (6×5)", renderer: "renderSecurity", produces_report: true, report_archetype: "matrix-risk-6x5" },
{ id: "ros", category: "security", label: "ROS-analyse", renderer: "renderRos", produces_report: true, report_archetype: "matrix-risk" },
{ id: "review", category: "security", label: "Arkitekturgjennomgang", renderer: "renderReview", produces_report: true, report_archetype: "findings" },
{ id: "cost", category: "economy", label: "Kostnadsestimat", renderer: "renderCost", produces_report: true, report_archetype: "cost-distribution" },
{ id: "license", category: "economy", label: "Lisenskartlegging", renderer: "renderLicense", produces_report: true, report_archetype: "scenario-comparison" },
{ id: "migrate", category: "economy", label: "Migrasjonsplan", renderer: "renderMigrate", produces_report: true, report_archetype: "phase-plan" },
{ id: "adr", category: "documentation", label: "ADR", renderer: "renderAdr", produces_report: true, report_archetype: "adr" },
{ id: "summary", category: "documentation", label: "Beslutningsnotat", renderer: "renderSummary", produces_report: true, report_archetype: "verdict" },
{ id: "poc", category: "documentation", label: "POC-plan", renderer: "renderPoc", produces_report: true, report_archetype: "phase-plan" },
{ id: "utredning", category: "documentation", label: "Utredning", renderer: "renderUtredning", produces_report: true, report_archetype: "utredning" },
{ id: "compare", category: "documentation", label: "Plattformsammenligning", renderer: "renderCompare", produces_report: true, report_archetype: "scenario-comparison" }
];
const stubs = `
const window = {};
function escapeHtml(s) { return String(s == null ? "" : s); }
function escapeAttr(s) { return escapeHtml(s); }
function renderPageShell(opts, body) { return "<header>" + (opts && opts.title || "") + "</header>" + (body || ""); }
function renderVerdictPill(v) { return "<span class=\\"verdict-pill\\" data-verdict=\\"" + v + "\\">" + v + "</span>"; }
function renderKeyStatsGrid(s) { return "<div class=\\"key-stats\\">" + (s && s.length || 0) + "</div>"; }
function inferVerdict() { return "n-a"; }
function inferKeyStats() { return []; }
const PARSERS = {};
const RENDERERS = { ` + rendererStubs + ` };
const CATALOG = { commands: ` + JSON.stringify(COMMANDS) + ` };
const ACTIONS = {};
const store = { state: { ui: {}, projects: [], activeProjectId: null }, save: function () {} };
function findProject() { return null; }
function scheduleRender() {}
`;
const wrapped = stubs + block + "\nreturn { inferCommandIdFromMarkdown, fingerprintScore, COMMAND_FINGERPRINTS };";
let api;
try {
api = (new Function(wrapped))();
} catch (e) {
console.error("EVAL_FAILED: " + e.message);
process.exit(3);
}
function emit(ok, desc) {
console.log((ok ? "PASS" : "FAIL") + "\t" + desc);
}
// ---- Sanity-asserts (5) ----
emit(typeof api.inferCommandIdFromMarkdown === "function", "inferCommandIdFromMarkdown er funksjon");
emit(typeof api.fingerprintScore === "function", "fingerprintScore er funksjon");
emit(typeof api.COMMAND_FINGERPRINTS === "object" && api.COMMAND_FINGERPRINTS !== null, "COMMAND_FINGERPRINTS er objekt");
const cfKeys = Object.keys(api.COMMAND_FINGERPRINTS);
emit(cfKeys.length === 17, "COMMAND_FINGERPRINTS har 17 entries (got " + cfKeys.length + ")");
let allShapeOk = true;
for (const k of cfKeys) {
const v = api.COMMAND_FINGERPRINTS[k];
if (!v || !Array.isArray(v.headers) || !Array.isArray(v.keywords)) {
allShapeOk = false; break;
}
}
emit(allShapeOk, "Hver fingerprint har headers[] og keywords[]");
// ---- True-positive matrise (17 fixtures) ----
//
// Kjente fingerprint/fixture-gap (oppdaget av denne testen, fix i sesjon 5):
// - requirements.md har header "# EU AI Act — Krav for høyrisiko" som ikke
// matcher /^\s*#\s*(AI\s*Act-?krav|Krav per|Requirements)/i. Annex IV-
// omtale i tabellen gjør at conformity vinner med 0.76.
// - license.md har header "# Lisens-kapabilitetsmatrise" som ikke matcher
// /^\s*#\s*(Lisens(kart)?legging|License\s*Mapping)/i.
// v1.15.0 (sesjon 5): begge gap-er lukket — requirements.headers og
// license.headers utvidet i COMMAND_FINGERPRINTS. KNOWN_GAP_FIXTURES tømt.
const KNOWN_GAP_FIXTURES = {};
const expectedIds = ["classify","requirements","transparency","frimpact","conformity","dpia","security","ros","review","cost","license","migrate","adr","summary","poc","utredning","compare"];
for (const cid of expectedIds) {
const fxPath = path.join(fixtureDir, cid + ".md");
let text = "";
try { text = fs.readFileSync(fxPath, "utf8"); } catch (e) {
emit(false, "fixture " + cid + ".md kunne ikke leses: " + e.message);
continue;
}
const r = api.inferCommandIdFromMarkdown(text, {});
const matchedCid = r && r.commandId;
const conf = r && r.confidence;
const ok = r && r.commandId === cid && r.confidence >= 0.6;
if (ok) {
emit(true, "fixture " + cid + ".md → " + matchedCid + " conf=" + conf.toFixed(2));
} else if (KNOWN_GAP_FIXTURES[cid]) {
console.log("WARN\tfixture " + cid + ".md → " + (matchedCid || "null") +
" (KNOWN_GAP — fingerprint dekker ikke fixture-header; fix i sesjon 5)");
} else {
emit(false, "fixture " + cid + ".md → " + (matchedCid || "null") +
(conf != null ? " conf=" + conf.toFixed(2) : ""));
}
}
// ---- Anti-match (4) ----
emit(api.inferCommandIdFromMarkdown("", {}) === null,
"tom streng → null");
emit(api.inferCommandIdFromMarkdown(null, {}) === null,
"null input → null");
const lorem = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim";
emit(api.inferCommandIdFromMarkdown(lorem, {}) === null,
"100 tegn lorem ipsum → null (got " + JSON.stringify(api.inferCommandIdFromMarkdown(lorem, {})) + ")");
emit(api.inferCommandIdFromMarkdown("# Hello\n\nIngen relevant tekst.", {}) === null,
"kun \"# Hello\"-header → null");
// ---- Mixed content: dominant header vinner ----
const mixed = "# EU AI Act — Klassifisering\n\nDette inneholder også owasp og prompt injection-referanser men headeren er klassifisering.";
const mixedR = api.inferCommandIdFromMarkdown(mixed, {});
emit(mixedR !== null && mixedR.commandId === "classify",
"mixed content med classify-header → " + (mixedR && mixedR.commandId));
// ---- fingerprintScore-API direkte ----
emit(api.fingerprintScore("any text", null) === 0,
"fingerprintScore(null spec) === 0");
emit(api.fingerprintScore("", api.COMMAND_FINGERPRINTS.classify) === 0,
"fingerprintScore(tom tekst) === 0");
' "$HTML_FILE" "$FIXTURE_DIR" 2>&1) || NODE_RC=$?
if [ "${NODE_RC:-0}" -ne 0 ]; then
fail "node-eval feilet (rc=${NODE_RC:-0}): $NODE_OUT"
print_summary; exit 1
fi
# Parse PASS/FAIL/WARN-linjer fra node-output
while IFS=$'\t' read -r status desc; do
case "$status" in
PASS) pass "$desc" ;;
FAIL) fail "$desc" ;;
WARN) warn "$desc" ;;
esac
done <<< "$NODE_OUT"
print_summary

View file

@ -122,8 +122,8 @@ api.migrateDataVersion(stateB, api.defaultArchetypeFor);
const a = JSON.stringify(stateA); const a = JSON.stringify(stateA);
const b = JSON.stringify(stateB); const b = JSON.stringify(stateB);
if (stateA.dataVersion !== 2) { if (stateA.dataVersion !== 3) {
console.error("DATA_VERSION_NOT_BUMPED"); console.error("DATA_VERSION_NOT_BUMPED got=" + stateA.dataVersion);
process.exit(4); process.exit(4);
} }
@ -141,8 +141,22 @@ for (const p of (stateA.projects || [])) {
if (verdictsAdded === 0) { console.error("NO_VERDICTS_ADDED"); process.exit(5); } if (verdictsAdded === 0) { console.error("NO_VERDICTS_ADDED"); process.exit(5); }
if (statsAdded === 0) { console.error("NO_KEYSTATS_ADDED"); process.exit(6); } if (statsAdded === 0) { console.error("NO_KEYSTATS_ADDED"); process.exit(6); }
// Sesjon 3: v2→v3-migrasjon må produsere project.artifacts med v3-shape.
let artifactsBuilt = 0;
for (const p of (stateA.projects || [])) {
if (!p.artifacts) continue;
for (const id of Object.keys(p.artifacts)) {
const a = p.artifacts[id];
if (a && a.commandId === id && typeof a.raw_markdown === "string"
&& a.importedAt && a.updatedAt) {
artifactsBuilt++;
}
}
}
if (artifactsBuilt === 0) { console.error("NO_ARTIFACTS_BUILT"); process.exit(8); }
if (a === b) { if (a === b) {
console.log("IDEMPOTENT verdicts=" + verdictsAdded + " stats=" + statsAdded); console.log("IDEMPOTENT verdicts=" + verdictsAdded + " stats=" + statsAdded + " artifacts=" + artifactsBuilt);
} else { } else {
console.error("NOT_IDEMPOTENT"); console.error("NOT_IDEMPOTENT");
process.exit(7); process.exit(7);
@ -155,7 +169,7 @@ else
fail "Idempotency-test feilet: $IDEMPOTENCY_RESULT" fail "Idempotency-test feilet: $IDEMPOTENCY_RESULT"
fi fi
# ---- 6. dataVersion bumpes til 2 ved første kjøring ---- # ---- 6. dataVersion bumpes til 3 ved første kjøring (v?→v3 kjede) ----
DV_RESULT=$(node -e ' DV_RESULT=$(node -e '
const fs = require("fs"); const fs = require("fs");
const html = fs.readFileSync(process.argv[1], "utf8"); const html = fs.readFileSync(process.argv[1], "utf8");
@ -176,10 +190,256 @@ api.migrateDataVersion(state, api.defaultArchetypeFor);
console.log(state.dataVersion); console.log(state.dataVersion);
' "$HTML_FILE" 2>&1) || true ' "$HTML_FILE" 2>&1) || true
if [ "$DV_RESULT" = "2" ]; then if [ "$DV_RESULT" = "3" ]; then
pass "dataVersion bumpes til 2" pass "dataVersion bumpes til 3 (kjede v?→v3)"
else else
fail "dataVersion ble ikke bumpet til 2 (got '$DV_RESULT')" fail "dataVersion ble ikke bumpet til 3 (got '$DV_RESULT')"
fi fi
# ---- 7. v2→v3 produserer artifacts uten å miste reports ----
V3_SHAPE=$(node -e '
const fs = require("fs");
const html = fs.readFileSync(process.argv[1], "utf8");
const begin = html.indexOf("// === V2_FOUNDATION_BEGIN ===");
const end = html.indexOf("// === V2_FOUNDATION_END ===");
const block = html.substring(begin, end + 32);
const stubs = `
const window = {};
function escapeHtml(s) { return String(s == null ? "" : s); }
function escapeAttr(s) { return escapeHtml(s); }
const CATALOG = { commands: [
{ id: "classify", report_archetype: "aiact" }
]};
`;
const api = (new Function(stubs + block + "\nreturn { migrateDataVersion, defaultArchetypeFor };"))();
// Bygg en v2-state med ett prosjekt og én report som har parsed-data
const state = {
schemaVersion: 1,
dataVersion: 2,
projects: [{
id: "p1",
name: "Test",
createdAt: "2026-05-15T00:00:00.000Z",
reports: {
classify: { raw_markdown: "# klassifisering", parsed: { risk_level: "minimal", verdict: "go", keyStats: [] } }
}
}]
};
api.migrateDataVersion(state, api.defaultArchetypeFor);
const p = state.projects[0];
const hasReports = !!(p.reports && p.reports.classify);
const hasArtifacts = !!(p.artifacts && p.artifacts.classify);
const a = p.artifacts && p.artifacts.classify;
const shapeOk = a && a.commandId === "classify" && a.raw_markdown === "# klassifisering"
&& a.parsed && a.parsed.risk_level === "minimal" && a.verdict === "go"
&& Array.isArray(a.keyStats) && typeof a.importedAt === "string"
&& typeof a.updatedAt === "string";
console.log(JSON.stringify({ dataVersion: state.dataVersion, hasReports, hasArtifacts, shapeOk }));
' "$HTML_FILE" 2>&1) || true
if echo "$V3_SHAPE" | grep -q '"dataVersion":3'; then
pass "v2→v3 setter dataVersion=3"
else
fail "v2→v3 setter ikke dataVersion=3 ($V3_SHAPE)"
fi
if echo "$V3_SHAPE" | grep -q '"hasReports":true'; then
pass "v2→v3 bevarer project.reports (bakover-kompat v1.15.0)"
else
fail "v2→v3 mistet project.reports ($V3_SHAPE)"
fi
if echo "$V3_SHAPE" | grep -q '"hasArtifacts":true'; then
pass "v2→v3 bygger project.artifacts"
else
fail "v2→v3 bygde ikke project.artifacts ($V3_SHAPE)"
fi
if echo "$V3_SHAPE" | grep -q '"shapeOk":true'; then
pass "v2→v3 artifact-shape ({commandId, raw_markdown, parsed, verdict, keyStats, importedAt, updatedAt})"
else
fail "v2→v3 artifact-shape mismatch ($V3_SHAPE)"
fi
# ---- 8. v2→v3 kant-case-tester (sesjon 4) ----
# Tomt prosjekt, manglende reports, blandet state, idempotens-mutasjon.
EDGE_RESULT=$(node -e '
const fs = require("fs");
const html = fs.readFileSync(process.argv[1], "utf8");
const begin = html.indexOf("// === V2_FOUNDATION_BEGIN ===");
const end = html.indexOf("// === V2_FOUNDATION_END ===");
const block = html.substring(begin, end + 32);
const stubs = `
const window = {};
function escapeHtml(s) { return String(s == null ? "" : s); }
function escapeAttr(s) { return escapeHtml(s); }
const CATALOG = { commands: [
{ id: "classify", report_archetype: "aiact" },
{ id: "ros", report_archetype: "matrix-risk" },
{ id: "cost", report_archetype: "cost-distribution" },
{ id: "summary", report_archetype: "verdict" }
]};
`;
const api = (new Function(stubs + block + "\nreturn { migrateDataVersion, defaultArchetypeFor };"))();
function emit(key, ok, info) {
console.log("EDGE\t" + key + "\t" + (ok ? "PASS" : "FAIL") + "\t" + (info || ""));
}
// Case A: tomt prosjekt (reports={}).
(function () {
const state = {
schemaVersion: 1,
dataVersion: 2,
projects: [{ id: "pA", name: "Empty", createdAt: "2026-05-15T00:00:00.000Z", reports: {} }]
};
api.migrateDataVersion(state, api.defaultArchetypeFor);
const p = state.projects[0];
const ok = p.artifacts !== undefined && typeof p.artifacts === "object"
&& Object.keys(p.artifacts).length === 0
&& state.dataVersion === 3;
emit("empty-project", ok, "artifacts=" + JSON.stringify(p.artifacts) + " dv=" + state.dataVersion);
})();
// Case B: prosjekt uten reports-felt i det hele tatt.
(function () {
const state = {
schemaVersion: 1,
dataVersion: 2,
projects: [{ id: "pB", name: "NoReports", createdAt: "2026-05-15T00:00:00.000Z" }]
};
let threw = false;
try {
api.migrateDataVersion(state, api.defaultArchetypeFor);
} catch (e) { threw = true; }
const p = state.projects[0];
const ok = !threw && p.artifacts !== undefined && typeof p.artifacts === "object"
&& Object.keys(p.artifacts).length === 0;
emit("no-reports-field", ok, "threw=" + threw + " artifacts=" + JSON.stringify(p.artifacts));
})();
// Case C: blandet state — artifacts har 2 entries fra før, reports har 5.
// Migrering skal legge til de 3 manglende uten å overskrive eksisterende.
(function () {
const preNow = "2026-04-01T00:00:00.000Z";
const state = {
schemaVersion: 1,
dataVersion: 2,
projects: [{
id: "pC",
name: "Mixed",
createdAt: preNow,
artifacts: {
classify: {
commandId: "classify",
raw_markdown: "EXISTING_CLASSIFY",
parsed: { risk_level: "minimal" },
verdict: "go",
keyStats: [],
importedAt: preNow,
updatedAt: preNow,
_preExisting: true
},
ros: {
commandId: "ros",
raw_markdown: "EXISTING_ROS",
parsed: { threats: [] },
verdict: "approved",
keyStats: [],
importedAt: preNow,
updatedAt: preNow,
_preExisting: true
}
},
reports: {
classify: { raw_markdown: "NEW_CLASSIFY", parsed: { risk_level: "high" } },
ros: { raw_markdown: "NEW_ROS", parsed: { threats: [{ id: "T1" }] } },
cost: { raw_markdown: "NEW_COST", parsed: { p50: 1000 } },
summary: { raw_markdown: "NEW_SUMMARY", parsed: { verdict: "go" } }
}
}]
};
api.migrateDataVersion(state, api.defaultArchetypeFor);
const p = state.projects[0];
const cls = p.artifacts.classify;
const ros = p.artifacts.ros;
const cost = p.artifacts.cost;
const summary = p.artifacts.summary;
// Eksisterende artifacts bevart med _preExisting-flag og opprinnelig raw_markdown.
const classifyPreserved = cls && cls._preExisting === true && cls.raw_markdown === "EXISTING_CLASSIFY";
const rosPreserved = ros && ros._preExisting === true && ros.raw_markdown === "EXISTING_ROS";
// Nye artifacts bygget fra reports.
const costAdded = cost && cost.commandId === "cost" && cost.raw_markdown === "NEW_COST";
const summaryAdded = summary && summary.commandId === "summary" && summary.raw_markdown === "NEW_SUMMARY";
const ok = classifyPreserved && rosPreserved && costAdded && summaryAdded;
emit("mixed-state-merge", ok,
"cls_preserved=" + !!classifyPreserved + " ros_preserved=" + !!rosPreserved +
" cost_added=" + !!costAdded + " summary_added=" + !!summaryAdded);
})();
// Case D: idempotens etter mutasjon — sett _touched=true på en artifact, kjør
// migrasjon på nytt, sjekk at flagget er bevart (ikke overskrevet).
(function () {
const state = {
schemaVersion: 1,
dataVersion: 2,
projects: [{
id: "pD",
name: "Idempotent",
createdAt: "2026-04-01T00:00:00.000Z",
reports: {
classify: { raw_markdown: "# klassifisering", parsed: { risk_level: "minimal" } }
}
}]
};
// Første migrasjon bygger artifact.
api.migrateDataVersion(state, api.defaultArchetypeFor);
const p = state.projects[0];
p.artifacts.classify._touched = true;
p.artifacts.classify.raw_markdown = "MUTATED_AFTER_MIGRATION";
// Re-migrer — idempotent skal ikke overskrive.
api.migrateDataVersion(state, api.defaultArchetypeFor);
const a = state.projects[0].artifacts.classify;
const ok = a._touched === true && a.raw_markdown === "MUTATED_AFTER_MIGRATION";
emit("idempotent-after-mutation", ok,
"_touched=" + a._touched + " raw=" + JSON.stringify(a.raw_markdown));
})();
// Case E: dataVersion=3 fra før — migrasjon er no-op.
(function () {
const state = {
schemaVersion: 1,
dataVersion: 3,
projects: [{
id: "pE",
name: "AlreadyV3",
createdAt: "2026-04-01T00:00:00.000Z",
artifacts: {
cost: { commandId: "cost", raw_markdown: "EXISTING", parsed: { p50: 1 },
verdict: "go", keyStats: [], importedAt: "x", updatedAt: "x" }
}
}]
};
const beforeJson = JSON.stringify(state);
api.migrateDataVersion(state, api.defaultArchetypeFor);
const afterJson = JSON.stringify(state);
emit("already-v3-noop", beforeJson === afterJson, "diff=" + (beforeJson === afterJson ? "none" : "changed"));
})();
' "$HTML_FILE" 2>&1) || true
# Parse EDGE-resultater
while IFS=$'\t' read -r tag key status info; do
[ "$tag" = "EDGE" ] || continue
case "$key" in
empty-project) desc="v3 kant-case: tomt prosjekt (reports={}) → artifacts={} uten å feile" ;;
no-reports-field) desc="v3 kant-case: prosjekt uten reports-felt → artifacts={} opprettet" ;;
mixed-state-merge) desc="v3 kant-case: blandet state — eksisterende artifacts bevart, nye lagt til" ;;
idempotent-after-mutation) desc="v3 kant-case: idempotens etter mutasjon — _touched-flag bevart" ;;
already-v3-noop) desc="v3 kant-case: state allerede v3 → migrasjon er no-op" ;;
*) desc="v3 kant-case: $key" ;;
esac
if [ "$status" = "PASS" ]; then
pass "$desc"
else
fail "$desc ($info)"
fi
done <<< "$EDGE_RESULT"
print_summary print_summary

View file

@ -0,0 +1,323 @@
#!/bin/bash
# test-playground-projectview.sh — Playground v3 renderProjectView integration
#
# Verifiserer at renderProjectView + sub-renderers produserer korrekt HTML
# (som strenger — ingen DOM-deps) mot en inline demo-state-snippet.
#
# Dekker 4 view-tilstander:
# - overview (selectedArtifactId null)
# - artifact (filled) (selectedArtifactId = 'classify')
# - empty (sidebar-treff) (selectedArtifactId = 'frimpact' — mangler)
# - import-modal-open (top-state, orthogonal)
#
# Pluss:
# - renderArtifactNav-søk filtrerer
# - renderProjectOverview har 4 verdict-tiles, top-risks, next-actions, missing
# - renderImportModal har 17 dropdown-options + prefill
#
# Bash 3.2-kompatibel. Bruker node til JS-eval. Ingen npm-deps.
set -euo pipefail
PLUGIN_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
HTML_FILE="$PLUGIN_ROOT/playground/ms-ai-architect-playground.html"
# shellcheck disable=SC1091
source "$PLUGIN_ROOT/tests/lib/e2e-helpers.sh"
init_suite "Playground v3 — renderProjectView integration"
if [ ! -f "$HTML_FILE" ]; then
fail "HTML-fila finnes ikke: $HTML_FILE"
print_summary; exit 1
fi
pass "HTML-fil finnes: $(basename "$HTML_FILE")"
if grep -q "PROJECT_VIEW_V2_BEGIN" "$HTML_FILE" && grep -q "PROJECT_VIEW_V2_END" "$HTML_FILE"; then
pass "PROJECT_VIEW_V2_BEGIN/END markører finnes"
else
fail "Mangler PROJECT_VIEW_V2-markører i HTML"
print_summary; exit 1
fi
NODE_OUT=$(node -e '
const fs = require("fs");
const htmlPath = process.argv[1];
const htmlSrc = fs.readFileSync(htmlPath, "utf8");
const beginMarker = "// === PROJECT_VIEW_V2_BEGIN ===";
const endMarker = "// === PROJECT_VIEW_V2_END ===";
const beginIdx = htmlSrc.indexOf(beginMarker);
const endIdx = htmlSrc.indexOf(endMarker);
if (beginIdx < 0 || endIdx < 0) {
console.error("MARKER_MISSING"); process.exit(2);
}
const block = htmlSrc.substring(beginIdx, endIdx + endMarker.length);
// 17 produces_report-renderers per PROJECT_VIEW_CONFIG.renderers.
// Hver stub returnerer en deterministisk HTML-streng vi kan asserte mot.
const RENDERER_IDS = ["renderAiActPyramid","renderRequirements","renderTransparency","renderFria","renderConformity","renderDpia","renderSecurity","renderRos","renderReview","renderCost","renderLicense","renderMigrate","renderAdr","renderSummary","renderPoc","renderUtredning","renderCompare"];
const rendererStubs = RENDERER_IDS.map(function (n) {
return n + ": function (data, slot) { if (slot) slot.innerHTML = \"<div class=\\\"stub-\" + " + JSON.stringify(n) + " + \"\\\">\" + (data && data.title || \"stub\") + \"</div>\"; }";
}).join(", ");
const COMMANDS = [
{ id: "classify", category: "regulatory", label: "EU AI Act — Klassifisering", description: "EU AI Act klass.", renderer: "renderAiActPyramid", produces_report: true, report_archetype: "aiact" },
{ id: "requirements", category: "regulatory", label: "EU AI Act — Krav", description: "Krav per risiko", renderer: "renderRequirements", produces_report: true, report_archetype: "requirements-list" },
{ id: "transparency", category: "regulatory", label: "Transparensnotis (Art. 13/50)", description: "Art. 13/50", renderer: "renderTransparency", produces_report: true, report_archetype: "text-document" },
{ id: "frimpact", category: "regulatory", label: "FRIA (Art. 27)", description: "FRIA", renderer: "renderFria", produces_report: true, report_archetype: "fria" },
{ id: "conformity", category: "regulatory", label: "Samsvarsvurdering (Art. 43)", description: "Annex IV", renderer: "renderConformity", produces_report: true, report_archetype: "conformity-checklist" },
{ id: "dpia", category: "regulatory", label: "DPIA / PVK", description: "PVK", renderer: "renderDpia", produces_report: true, report_archetype: "matrix-risk" },
{ id: "security", category: "security", label: "Sikkerhetsvurdering (6×5)", description: "6×5 scoring", renderer: "renderSecurity", produces_report: true, report_archetype: "matrix-risk-6x5" },
{ id: "ros", category: "security", label: "ROS-analyse", description: "NS 5814", renderer: "renderRos", produces_report: true, report_archetype: "matrix-risk" },
{ id: "review", category: "security", label: "Arkitekturgjennomgang", description: "Digdir/NSM", renderer: "renderReview", produces_report: true, report_archetype: "findings" },
{ id: "cost", category: "economy", label: "Kostnadsestimat", description: "P10/P50/P90 NOK", renderer: "renderCost", produces_report: true, report_archetype: "cost-distribution" },
{ id: "license", category: "economy", label: "Lisenskartlegging", description: "M365-lisenser", renderer: "renderLicense", produces_report: true, report_archetype: "scenario-comparison" },
{ id: "migrate", category: "economy", label: "Migrasjonsplan", description: "Fase-plan", renderer: "renderMigrate", produces_report: true, report_archetype: "phase-plan" },
{ id: "adr", category: "documentation", label: "ADR", description: "MADR v3.0", renderer: "renderAdr", produces_report: true, report_archetype: "adr" },
{ id: "summary", category: "documentation", label: "Beslutningsnotat", description: "Sammendrag", renderer: "renderSummary", produces_report: true, report_archetype: "verdict" },
{ id: "poc", category: "documentation", label: "POC-plan", description: "Suksesskriterier", renderer: "renderPoc", produces_report: true, report_archetype: "phase-plan" },
{ id: "utredning", category: "documentation", label: "Utredning", description: "Utredningsinstr.", renderer: "renderUtredning", produces_report: true, report_archetype: "utredning" },
{ id: "compare", category: "documentation", label: "Plattformsammenligning", description: "Plattformer", renderer: "renderCompare", produces_report: true, report_archetype: "scenario-comparison" }
];
// Demo state med 5 fylte artifacts + ui i forskjellige tilstander.
function buildDemoState(opts) {
const o = opts || {};
const now = "2026-05-15T10:00:00.000Z";
return {
dataVersion: 3,
schemaVersion: 1,
activeProjectId: "p1",
projects: [{
id: "p1",
name: "Acme: Kunde-chatbot",
description: "AI-system for objekt-deteksjon i sensordata.",
createdAt: "2026-04-01T08:00:00.000Z",
artifacts: {
classify: {
commandId: "classify",
raw_markdown: "# EU AI Act — Klassifisering",
parsed: { title: "Klassifisering", risk_level: "Høy", role: "Provider og Deployer" },
verdict: "warning",
keyStats: [{ label: "Risikonivå", value: "Høy" }],
importedAt: now, updatedAt: now
},
ros: {
commandId: "ros",
raw_markdown: "# ROS-analyse",
parsed: { title: "ROS", threats: [
{ id: "T1", description: "Modell-bias", severity: "Høy" },
{ id: "T2", description: "Privacy leak", severity: "Kritisk" },
{ id: "T3", description: "Hallusinerte fakta", severity: "Medium" }
]},
verdict: "warning",
keyStats: [],
importedAt: now, updatedAt: now
},
security: {
commandId: "security",
raw_markdown: "# Sikkerhetsvurdering",
parsed: { title: "Security", findings: [
{ id: "S1", finding: "Manglende DLP", severity: "Kritisk" },
{ id: "S2", finding: "Audit ufullstendig", severity: "Høy" }
]},
verdict: "block",
keyStats: [],
importedAt: now, updatedAt: now
},
dpia: {
commandId: "dpia",
raw_markdown: "# DPIA",
parsed: { title: "DPIA", threats: [] },
verdict: "go-with-conditions",
keyStats: [],
importedAt: now, updatedAt: now
},
cost: {
commandId: "cost",
raw_markdown: "# Kostnadsestimat",
parsed: { title: "Kostnad", p10: 78000, p50: 142000, p90: 285000 },
verdict: "approved",
keyStats: [{ label: "P50/mnd", value: "142 000 NOK" }],
importedAt: now, updatedAt: now
}
},
reports: {}
}],
ui: {
projectView: {
selectedArtifactId: o.selectedArtifactId === undefined ? null : o.selectedArtifactId,
searchQuery: o.searchQuery || ""
},
importModal: {
open: !!o.importOpen,
prefillCommandId: o.prefillCommandId || "",
prefillMarkdown: o.prefillMarkdown || ""
}
}
};
}
const stubs = `
const window = {};
function escapeHtml(s) {
return String(s == null ? "" : s)
.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
}
function escapeAttr(s) { return escapeHtml(s); }
function renderPageShell(opts, body) {
const t = (opts && opts.title) || "";
const e = (opts && opts.eyebrow) || "";
const v = (opts && opts.verdict) || "";
return "<header class=\\"page__header\\" data-eyebrow=\\"" + escapeAttr(e) + "\\" data-verdict=\\"" + escapeAttr(v) + "\\"><h1>" + escapeHtml(t) + "</h1></header>" + (body || "");
}
function renderVerdictPill(v) {
return "<span class=\\"verdict-pill\\" data-verdict=\\"" + escapeAttr(String(v || "").toLowerCase()) + "\\">" + escapeHtml(String(v || "").toUpperCase()) + "</span>";
}
function renderKeyStatsGrid(s) { return "<div class=\\"key-stats\\">" + (Array.isArray(s) ? s.length : 0) + "</div>"; }
function inferVerdict() { return "n-a"; }
function inferKeyStats() { return []; }
const PARSERS = {};
const RENDERERS = { ` + rendererStubs + ` };
const CATALOG = { commands: ` + JSON.stringify(COMMANDS) + ` };
const ACTIONS = {};
let __STORE_STATE = null;
const store = {
get state() { return __STORE_STATE; },
save: function () {}
};
function setStoreState(s) { __STORE_STATE = s; }
function findProject(id) {
const list = (store.state && store.state.projects) || [];
for (let i = 0; i < list.length; i++) if (list[i].id === id) return list[i];
return null;
}
function scheduleRender() {}
`;
const wrapped = stubs + block + "\nreturn { renderProjectView, renderProjectHeader, renderArtifactNav, renderArtifactNavItem, renderProjectMain, renderProjectOverview, renderProjectArtifact, renderEmptyArtifactPrompt, renderImportModal, PROJECT_VIEW_CONFIG, setStoreState };";
let api;
try {
api = (new Function(wrapped))();
} catch (e) {
console.error("EVAL_FAILED: " + e.message);
process.exit(3);
}
function emit(ok, desc) { console.log((ok ? "PASS" : "FAIL") + "\t" + desc); }
// ---- API-eksponering ----
emit(typeof api.renderProjectView === "function", "renderProjectView er funksjon");
emit(typeof api.renderProjectOverview === "function", "renderProjectOverview er funksjon");
emit(typeof api.renderImportModal === "function", "renderImportModal er funksjon");
emit(typeof api.PROJECT_VIEW_CONFIG === "object" && api.PROJECT_VIEW_CONFIG !== null, "PROJECT_VIEW_CONFIG eksponert");
// ---- View 1: overview (selectedArtifactId null) ----
let demo = buildDemoState({});
api.setStoreState(demo);
let project = demo.projects[0];
let html = api.renderProjectView(project, api.PROJECT_VIEW_CONFIG);
emit(html.indexOf("class=\"project-view\"") !== -1 && html.indexOf("data-view=\"overview\"") !== -1,
"overview: .project-view rot med data-view=\"overview\"");
emit(html.indexOf("Acme: Kunde-chatbot") !== -1,
"overview: prosjekt-navn rendres i header");
emit(html.indexOf("data-action=\"import-open\"") !== -1,
"overview: Importer-rapport-knapp finnes");
// Alle 4 kategori-grupper i sidebaren
emit(html.indexOf("Regulatorisk") !== -1 && html.indexOf("Risiko &amp; sikkerhet") !== -1 && html.indexOf("Økonomi") !== -1 && html.indexOf("Dokumentasjon") !== -1,
"overview: alle 4 kategori-labels rendres i nav");
// Overview-tiles (verdict-grid)
emit(html.indexOf("project-overview__verdict-tile") !== -1,
"overview: project-overview__verdict-tile finnes");
const tileCount = (html.match(/project-overview__verdict-tile/g) || []).length;
emit(tileCount >= 4, "overview: minst 4 verdict-tiles (got " + tileCount + ")");
// Top-risks (3 fra ros + 2 fra security = 5)
emit(html.indexOf("top-risks") !== -1 && html.indexOf("Privacy leak") !== -1,
"overview: top-risks-listen inkluderer ROS-trussel");
// Next-actions / missing reports
emit(html.indexOf("project-overview__next-actions") !== -1 || html.indexOf("empty-hint") !== -1,
"overview: next-actions-seksjon rendres");
emit(html.indexOf("project-overview__missing-reports") !== -1 || html.indexOf("Alle må-ha-rapporter er importert") !== -1,
"overview: missing-reports-seksjon rendres");
// ---- View 2: artifact (filled) ----
demo = buildDemoState({ selectedArtifactId: "classify" });
api.setStoreState(demo);
project = demo.projects[0];
html = api.renderProjectView(project, api.PROJECT_VIEW_CONFIG);
emit(html.indexOf("data-view=\"artifact\"") !== -1,
"artifact: data-view=\"artifact\"");
emit(html.indexOf("project-view__artifact") !== -1 && html.indexOf("data-artifact=\"classify\"") !== -1,
"artifact: .project-view__artifact med data-artifact=\"classify\"");
emit(html.indexOf("project-view__artifact-title") !== -1 && html.indexOf("EU AI Act — Klassifisering") !== -1,
"artifact: command-label vises i artifact-header");
emit(html.indexOf("data-action=\"artifact-reimport\"") !== -1 && html.indexOf("data-action=\"artifact-delete\"") !== -1,
"artifact: reimport + delete-action-knapper finnes");
// ---- View 3: empty (sidebar-treff uten artifact) ----
demo = buildDemoState({ selectedArtifactId: "frimpact" });
api.setStoreState(demo);
project = demo.projects[0];
html = api.renderProjectView(project, api.PROJECT_VIEW_CONFIG);
emit(html.indexOf("data-view=\"empty\"") !== -1,
"empty: data-view=\"empty\"");
emit(html.indexOf("empty-artifact-prompt") !== -1 && html.indexOf("data-command=\"frimpact\"") !== -1,
"empty: empty-artifact-prompt for frimpact");
emit(html.indexOf("data-prefill-command=\"frimpact\"") !== -1,
"empty: Importer-knapp har data-prefill-command=\"frimpact\"");
// ---- View 4: import-modal-open ----
demo = buildDemoState({ importOpen: true, prefillCommandId: "ros", prefillMarkdown: "# ROS-analyse\nDemo" });
api.setStoreState(demo);
project = demo.projects[0];
html = api.renderProjectView(project, api.PROJECT_VIEW_CONFIG);
emit(html.indexOf("data-import-modal") !== -1,
"import-modal: [data-import-modal] rendres når open=true");
// Dropdown har 17 options + tom default = 18 <option>-tags
const optionMatches = html.match(/<option /g) || [];
emit(optionMatches.length >= 17,
"import-modal: dropdown har 17+ options (got " + optionMatches.length + ")");
emit(html.indexOf("value=\"ros\" selected") !== -1 || html.indexOf("value=\"ros\" selected") !== -1,
"import-modal: prefillCommandId=\"ros\" gir selected option");
emit(html.indexOf("# ROS-analyse") !== -1 || html.indexOf("ROS-analyse") !== -1,
"import-modal: prefillMarkdown rendres i textarea");
// ---- Modal IKKE rendret når open=false ----
demo = buildDemoState({});
api.setStoreState(demo);
project = demo.projects[0];
html = api.renderProjectView(project, api.PROJECT_VIEW_CONFIG);
emit(html.indexOf("data-import-modal") === -1,
"import-modal: ikke rendret når importModal.open=false");
// ---- renderArtifactNav-søk filtrerer ----
demo = buildDemoState({ searchQuery: "ros" });
api.setStoreState(demo);
project = demo.projects[0];
const navHtml = api.renderArtifactNav(project, api.PROJECT_VIEW_CONFIG, null, "ros");
emit(navHtml.indexOf("data-artifact-id=\"ros\"") !== -1,
"nav-filter: ROS-element finnes ved søk=\"ros\"");
emit(navHtml.indexOf("data-artifact-id=\"cost\"") === -1,
"nav-filter: cost-element filtrert ut ved søk=\"ros\"");
// ---- renderProjectView med null project ----
emit(api.renderProjectView(null, api.PROJECT_VIEW_CONFIG).indexOf("Ingen prosjekt valgt") !== -1,
"guard: null project → empty-hint");
' "$HTML_FILE" 2>&1) || NODE_RC=$?
if [ "${NODE_RC:-0}" -ne 0 ]; then
fail "node-eval feilet (rc=${NODE_RC:-0}): $NODE_OUT"
print_summary; exit 1
fi
while IFS=$'\t' read -r status desc; do
case "$status" in
PASS) pass "$desc" ;;
FAIL) fail "$desc" ;;
WARN) warn "$desc" ;;
esac
done <<< "$NODE_OUT"
print_summary

View file

@ -223,20 +223,23 @@ else
fi fi
# ------------------------------------------------------- # -------------------------------------------------------
# 13. data-report-slot per rapport-produserende command (17 stk) # 13. v1.15.0: artifact-slot rendres dynamisk via renderProjectArtifact
# ------------------------------------------------------- # -------------------------------------------------------
# v2 hadde per-command data-report-slot="..." på alle 17 cards. v3 har én
# .project-main-zone der renderProjectArtifact mounter den valgte artefakten.
# Sjekk i stedet at RENDERERS routing-objektet er wired for alle 17.
REPORT_CMDS="classify requirements transparency frimpact conformity dpia security ros review cost license migrate adr summary poc utredning compare" REPORT_CMDS="classify requirements transparency frimpact conformity dpia security ros review cost license migrate adr summary poc utredning compare"
slot_hits=0 renderer_hits=0
for c in $REPORT_CMDS; do for c in $REPORT_CMDS; do
if grep -qE "data-report-slot=[\"']${c}[\"']" "$HTML_FILE"; then # PROJECT_VIEW_CONFIG.renderers[<cid>] = RENDERERS.renderXxx
pass "data-report-slot=\"${c}\" markup til stede" if grep -qE "^[[:space:]]+${c}:[[:space:]]+RENDERERS\." "$HTML_FILE"; then
slot_hits=$((slot_hits + 1)) pass "PROJECT_VIEW_CONFIG.renderers.${c} wired"
renderer_hits=$((renderer_hits + 1))
else else
warn "data-report-slot=\"${c}\" finnes ikke i statisk markup (kan rendres dynamisk)" fail "PROJECT_VIEW_CONFIG.renderers.${c} mangler"
fi fi
done done
# Slot rendrer dynamisk via render-funksjoner — warn kun, ingen fail pass "v3 renderer-routing wired ($renderer_hits/17)"
pass "Report-slot-stikkprøve fullført ($slot_hits/17 statiske; resterende rendres dynamisk)"
# ------------------------------------------------------- # -------------------------------------------------------
# 14. report_archetype-routing-felt i CATALOG-data # 14. report_archetype-routing-felt i CATALOG-data

View file

@ -75,3 +75,20 @@ Cycle archival: `/okr:oppsett arkiver` — moves `syklus/` to `historikk/`, gene
/okr:oppsett arkiver ──→ cycle archival + retrospektiv-generering /okr:oppsett arkiver ──→ cycle archival + retrospektiv-generering
SessionStart ──→ coaching-hook.mjs (proactive coaching) SessionStart ──→ coaching-hook.mjs (proactive coaching)
``` ```
## Communication patterns
### Linking to local files
When pointing to local files in responses, always use markdown link syntax with a descriptive name:
- Use `[Human-friendly name](file:///absolute/path)` — never bare `file:///...` URLs or autolinks `<file://...>`.
- Always use absolute paths. Never `~/` or relative paths.
- For multiple files, render as a bullet list of named markdown links.
Why: bare `file://` URLs only render the first as clickable across multiple lines. Named markdown links make each entry independently clickable and look cleaner.
Example:
- [Brief](file:///Users/ktg/.../brief.html)
- [Research summary](file:///Users/ktg/.../research/summary.md)

View file

@ -1,7 +1,7 @@
{ {
"name": "voyage", "name": "voyage",
"description": "Voyage — brief, research, plan, execute, review, continue. Contract-driven Claude Code pipeline; renders produced artifacts to HTML + link, annotate via the /playground plugin.", "description": "Voyage — brief, research, plan, execute, review, continue. Contract-driven Claude Code pipeline. /trekbrief, /trekplan, and /trekreview each end by building a self-contained operator-annotation HTML (scripts/annotate.mjs, modelled on claude-code-100x): select text or click any element, pick intent (Fiks/Endre/Spørsmål), write comment, copy structured prompt, paste back, Claude revises the .md.",
"version": "5.0.0", "version": "5.1.1",
"author": { "author": {
"name": "Kjell Tore Guttormsen" "name": "Kjell Tore Guttormsen"
}, },

View file

@ -4,6 +4,316 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
## v5.1.1 — 2026-05-14 — Remediation patch (11/12 review findings closed)
Additive. No breaking changes against v5.1.0. Closes 11 of 12 BLOCKER/MAJOR/MINOR findings from the v5.1.0 review (sesjon 5, review.md SHA range `8cbb33e..8f4b79c`). SC8 dogfood gate (#5) is scheduled for sesjon 8 as a fresh-CC-session operator action — its closure cannot happen inside the v5.1.1 execute session.
### Bug fixes (load-bearing)
- **#8 YAML-number bypass closed.** `lib/validators/brief-validator.mjs` now coerces `brief_version` to string before comparison; both `brief_version: 2.1` (parsed as Number) and `brief_version: "2.1"` (parsed as String) trigger the v5.1+ sequencing gate. Previously the unquoted form silently bypassed the gate. Three regression tests added in `tests/validators/brief-validator.test.mjs`.
- **#11 Doc-consistency pin lock-in.** `tests/lib/doc-consistency.test.mjs` pins `templates/trekbrief-template.md` to emit `brief_version: "2.1"` (quoted) so future template edits can't reintroduce the bypass.
### Wiring (closes #9 + #12)
- **`lib/profiles/phase-signal-resolver.mjs` helper** (new). Reads `brief.phase_signals` (or `phase_signals_partial`) and the active profile's `phase_models`, returns the resolved `{effort, model, source}` per phase. Composition rule: brief signal wins per-phase, profile fills gaps. The resolver controls only the orchestrator and the model parameter at `Agent`-spawn sites — sub-agents otherwise read `model:` from their own `agents/*.md` frontmatter (still pinned to `opus`).
- **4 downstream commands wired.** `/trekplan`, `/trekresearch`, `/trekreview`, `/trekexecute` each invoke the resolver in their Phase 1 (or equivalent first-phase block), capture the result as `phase_signal_result`, and pass it to `Agent` tool calls explicitly. Per-command low-effort code-paths reuse existing `--quick`-equivalent logic.
- **`resolvePhaseModel` orchestrator-override added** (#9 part A). Profile-tier model picks are overridden when `brief.phase_signals[<phase>].model` is set. Non-interference verified by new test `tests/lib/profile-resolver.test.mjs` (Step 7 TDD pair, Red→Green).
- **`brief-validator --soft` gate required uniformly** in `/trekresearch` + `/trekexecute` (previously only `/trekplan` + `/trekreview`). `BRIEF_V51_MISSING_SIGNALS` halts all 4 commands with a clear hint pointing back to `/trekbrief`.
### Test refactor (closes #1 #2 #3 #4 #6 #7 #10)
- **Runtime SC1 walk for `tests/commands/trekbrief.test.mjs`** (#1). Replaces prose-grep with `resolvePhaseSignal` invariant: returns non-null for all 4 entries in `PHASE_SIGNAL_PHASES` when applied to a fixture brief with a committed `phase_signals` block; returns gate-fire when applied to a v2.1 brief missing the block.
- **Per-tier resolver-output + missing-signals falsification** (#2 #3 #6 #10). `tests/commands/trekplan.test.mjs`, `trekresearch.test.mjs`, `trekreview.test.mjs`, `trekexecute.test.mjs` each refactored from doc-pin pattern to runtime assertion against 4 new fixture briefs (`tests/fixtures/briefs/{low,standard,high,partial}.md`).
- **Dedicated SC5 profile-resolver non-interference test** (#7). `tests/lib/profile-resolver.test.mjs` asserts that brief-emitted signals are not overridden by `VOYAGE_PROFILE`, and that `--profile` does not silently override brief-emitted answers.
- **#4 SC5 invariant** locked at resolver level rather than test-strategy level (no scope expansion — see `source_findings` audit trail in plan.md frontmatter).
- **Total test delta:** +12 tests (539 baseline + 12 + 4 doc-pins from Step 10 = 555 expected; actual 578 because the existing tests grew underneath us during the v5.1.0→v5.1.1 cycle). 0 fail, 2 skipped.
### Documentation (closes #5 scheduling + Decision B high-effort)
- **#5 SC8 dogfood scheduling.** `REMEMBER.md` (gitignored) gains a `## v5.1.1 sesjon 8 — DOGFOOD GATE OBSERVATIONS` reserved placeholder with concrete procedure: fresh CC-session → real v5.1-eligible task → Phase 3.5 observation → 5 specific questions to answer. The actual observation is a sesjon 8 manual operator action; sesjon 7 produces scaffolding only.
- **Decision B high-effort behavior locked per command** (operator-confirmed, decisions.local.json):
- `/trekplan` high: gemini-bridge plan-review Pass 2 on the post-revision plan (replaces fragile plan-critic doubling per risk-assessor finding — line numbers shift after revisions, breaking dedup triplets).
- `/trekresearch` high: full swarm + `contrarian-researcher` AND `gemini-bridge` always-on regardless of normal triggering rules.
- `/trekreview` high: skip Pass 3 reasonableness filter; coordinator applies `PLAN_EXECUTE_DRIFT` normalization for unknown rule_keys (preserves original in `original_rule_key`).
- `/trekexecute` high: `gates_mode = 'closed'` automatically; explicit `--gates` flag still wins.
- **Brief Non-Goal amendment** (`.claude/projects/2026-05-13-trekflow-solo-lane/brief.md`, gitignored): low/high effort tiers now locked in v5.1.1; standard remains fallthrough default. Continue + brief phases remain effort-less.
- **Brief SC1 amendment** (same file, gitignored): SC1 "4 AskUserQuestion calls" interpreted as the resolver-invariant `resolvePhaseSignal` returns non-null for all 4 entries in `PHASE_SIGNAL_PHASES`. The literal AskUserQuestion mocking interpretation would require a state-machine harness module (significant scope expansion); the resolver-invariant interpretation is authorized as equivalent. **Operator-confirmed via Pre-Step-10 autonomy gate.**
### Known scheduling
SC8 (#5) dogfood-gate closure happens in sesjon 8 (fresh CC-session, manual `/trekbrief` walk-through). v5.1.1 push to Forgejo gated on sesjon 9 `/trekreview` returning `ALLOW`. If sesjon 9 returns `BLOCK`, loop back to sesjon 6 (new remediation plan).
### Source findings audit trail
Plan-frontmatter `source_findings:` lists the 12 review-finding IDs each step closes. Audit trail traces `review.md``/trekplan --brief review.md` (Handover 6) → `plan.md``/trekexecute` → individual commits.
## v5.1.0 — 2026-05-13 — Per-phase effort + model dialog
Additive. No breaking changes. Forward-compat with all v5.0.x briefs.
### Why
The voyage pipeline runs a single profile-tier setting for every task. For
typo fixes and small bugfixes the full `brief → research → plan → execute
→ review` ceremony is over-engineered; for risky migrations the same
profile-tier is too thin. v5.1 hands ceremony-level back to the operator
per phase in the same dialog that produces the brief — without removing
the disciplined defaults that protect high-stakes work. Independent of
v4.1's profile system: composition happens at the command level (brief
signal wins per-phase, profile fills gaps). No `/trekflow`, no helper
module, no per-command effort dictionary — composition is documented
prose in each downstream command.
### Added
- **`/trekbrief` Phase 3.5** — between Phase 3 completeness exit and
Phase 4 draft, 4 tier-coupled `AskUserQuestion` calls commit an effort
level (`low | standard | high`) and an optional `model` (`sonnet |
opus`) per downstream phase (`research`, `plan`, `execute`, `review`).
Tier mapping: `low → {effort: low, model: sonnet}`, `standard →
{effort: standard}` (model omitted; composition falls through to
profile), `high → {effort: high, model: opus}`. Force-stop pattern
(Phase 4f verbatim) records `phase_signals_partial: true` instead.
`--quick` skips Phase 3.5 entirely and auto-writes
`phase_signals_partial: true`.
- **`brief-validator` extension** — 6 new issue codes:
`BRIEF_INVALID_PHASE_SIGNALS`, `BRIEF_INVALID_PHASE_SIGNAL_PHASE`,
`BRIEF_INVALID_EFFORT`, `BRIEF_INVALID_MODEL`,
`BRIEF_SIGNALS_MUTUALLY_EXCLUSIVE`, `BRIEF_V51_MISSING_SIGNALS` +
exported `PHASE_SIGNAL_PHASES` + `EFFORT_LEVELS` constants. The
`BASE_ALLOWED_MODELS` const in `lib/validators/profile-validator.mjs`
was promoted to `export const` so the brief validator can re-use it.
- **HANDOVER-CONTRACTS amendments** — Handover 1 gets 5 inserts:
versioning row → `2.1`, two new schema-table rows (`phase_signals`,
`phase_signals_partial`), v5.1 sequencing-gate validation row,
versioning-paragraph expansion explaining the version-conditional
gate, 6 new failure-mode bullets.
- **Template bump**`templates/trekbrief-template.md` → `brief_version
2.1` with a default `phase_signals:` block (4 phases × `effort:
standard`, model omitted) and a commented `phase_signals_partial:
true` line showing the force-stop alternative.
- **Composition rule (v5.1)** — new `## Composition rule (v5.1)`
sub-section in each of `commands/{trekplan,trekresearch,trekexecute,
trekreview}.md`. Documents `effort_for_phase = brief.phase_signals[
<phase>]?.effort ?? 'standard'` and `model_for_phase =
brief.phase_signals[<phase>]?.model ?? profile.phase_models[<phase>]`.
Per command: `effort == low` activates that command's existing
`--quick`-equivalent code-path (`/trekplan` skips Phase 5 agent swarm,
`/trekresearch` inline research, `/trekreview` correctness-only,
`/trekexecute` `--gates open` + sequential-only).
- **Sequencing-gate surface** in 4 downstream commands — when
`brief-validator.mjs` returns `BRIEF_V51_MISSING_SIGNALS` in `errors`,
halt with a one-line user-readable message pointing back to
`/trekbrief`. Enforcement is validator-only.
- **5 new minimal command test files** under `tests/commands/`
`trekbrief.test.mjs` (3 cases), `trekplan.test.mjs` /
`trekresearch.test.mjs` / `trekreview.test.mjs` (2 cases each),
`trekexecute.test.mjs` (2 cases). Pattern D (read .md, assert prose
patterns). Verifies sequencing-gate surface + low-effort prose.
- **5 new doc-consistency pins** — template `brief_version 2.1` +
`phase_signals:` block, HANDOVER schema rows, voyage CLAUDE.md +
README.md mention `phase_signals`.
- **2 new fixtures**`tests/fixtures/brief-with-phase-signals.md` +
`brief-without-phase-signals.md` (backward-compat).
### Changed
- `brief_version` bumped `2.0 → 2.1`. The bump exists because v2.1
activates the **version-conditional sequencing gate** — the only check
in the brief validator that triggers on `brief_version` rather than
field-presence. The forward-compat policy still applies to the field
itself (unknown frontmatter keys flow through).
### Notes
- Test count grows by ≥ 17 new cases minimum: 6 brief-validator + 11
command-test minimums. Realistic delta is ~25 new cases (Step 6 adds 5
doc-consistency pins on top). Target ≥ 533 pass at Step 10 verify.
- `MIGRATION.md` was deliberately NOT created — v5.1 is an additive
minor (brief_version 2.0 → 2.1, not major). v5.4 may promote
`phase_signals` from optional to required (breaking change → 3.0).
- High-effort behaviors for `/trekplan` / `/trekresearch` /
`/trekreview` are deferred to v5.1.1 per brief Non-Goal ("No complete
per-phase effort dictionary"). v5.1 locks only the low-effort floor.
- `phase_signals_present` stats emission is also deferred to v5.1.1
(opt-in observability per Research 03 Q5).
## v5.0.3 — 2026-05-13 — Annotation UX matches the claude-code-100x reference
**No new breaking changes beyond v5.0.0.** Forks consuming v5.0.2's
annotation HTML keep working — the file path and entry point are
unchanged. The internal localStorage key bumps from `voyage-annotate:` to
`voyage-annotate:v2:` to avoid mixing the v5.0.2 shape (line-click,
freeform notes) with the v5.0.3 shape (intent-tagged annotations).
### Why
v5.0.2 shipped a too-simple annotation surface: click a line, write a
freeform note, save. The operator pointed at the existing
`claude-code-100x/build-site.js` annotation system as the actual
reference — pencil-toggle mode, text-selection capture, three intent
categories (**Fiks** / **Endre** / **Spørsmål**), a popover form at the
cursor, structured markdown export with intent labels. v5.0.3 brings
`scripts/annotate.mjs` up to that pattern.
This reference had been mentioned by the operator early in the
conversation; the iteration through v5.0.0 / v5.0.1 / v5.0.2 reflects me
reading past it and trying alternatives instead of just matching it. The
loss is real and is documented here in plain terms so future maintainers
don't repeat it.
### Changed
- **`scripts/annotate.mjs`** — rewritten to match the
`claude-code-100x/build-site.js` UX:
- **Article rendering** — markdown is rendered to proper HTML elements
(`<h1>`/`<p>`/`<ul>`/`<li>`/`<table>`/`<blockquote>`/`<pre>`), not as
line-numbered raw lines. Document reads as a normal article.
- **Annotatable elements** — every heading, paragraph, list item, table
cell, blockquote, and code block gets a stable `data-anchor-id`.
- **Pencil-toggle button** in the topbar — annotation mode default ON.
Toggle OFF to read normally and follow links.
- **Click any annotatable element** (in mode) → opens a form popover
at the cursor with: section context (auto-detected from nearest
h1/h2), anchored snippet (the exact selected substring via
`window.getSelection()` if any text is highlighted, else the
element's text content up to 200 chars), three intent buttons
(**Fiks** / **Endre** / **Spørsmål**), comment textarea, Cancel +
Save. Save is disabled until an intent is picked.
- **Sidebar panel** — collapsed by default; "Show annotations" button
in the topbar opens it. Annotations grouped by section, sorted by
document order. Each card shows the intent badge (colored by
category), the anchored snippet, the operator comment, and a delete
button. Click a card to scroll the article to that element + flash
highlight.
- **Copy Prompt** — structured markdown:
`### N. [Intent] Section: <section>` + `Quote: «<snippet>»` +
`Comment: <text>`. Copies to clipboard.
- **Clear all** — wipes every annotation for the current artifact
(after confirm).
- **Persistence**`localStorage` key `voyage-annotate:v2:<abs path>`.
Refresh/close/reopen the same HTML keeps every annotation.
- **Toast feedback** for save / copy / clear.
- **`tests/scripts/annotate.test.mjs`** — refreshed for the v5.0.3 shape:
pins the three intent buttons (`data-intent="fiks"` / `"endre"` /
`"spørsmål"`), form popover, selection capture, section auto-detect,
`voyage-annotate:v2:` storage key prefix, `data-anchor-id` coverage,
Copy Prompt + Clear all affordances, and the markdown renderer's
heading / list / table / blockquote / code-fence output. 12 tests
(up from 10), all passing.
### Notes
- The producing commands (`/trekbrief` Step 4g, `/trekplan` Phase 10,
`/trekreview` Phase 8) call `scripts/annotate.mjs` the same way as in
v5.0.2 — no change to their wiring beyond the build-output now being
the v5.0.3 interactive surface.
- `npm test`: 518 tests, 516 pass, 0 fail, 2 skipped (up from 516 — 2
new annotate tests for hostile-content escape + renderMarkdown table/
blockquote coverage).
- Reference: `~/repos/claude-code-100x/claude-code-100x/build-site.js`
lines 14312255 (annotation UI section).
- Version bump 5.0.2 → 5.0.3 in `.claude-plugin/plugin.json`,
`package.json`, `package-lock.json`, plugin `README.md` badge.
## v5.0.2 — 2026-05-13 — Operator-driven annotation HTML (the actual fix)
**No new breaking changes beyond v5.0.0.** Forks that consumed the v5.0.1
`/playground document-critique` invocation from the producing commands'
final report should switch to opening the `.html` that `scripts/annotate.mjs`
now produces directly.
### Why
v5.0.0 added a read-only `scripts/render-artifact.mjs` HTML render that
didn't afford annotation. v5.0.1 deleted that and pointed operators at
`/playground document-critique` instead — but the `document-critique`
template pre-generates **Claude's** suggestions and asks the operator to
approve/reject them. The operator asked for the opposite: a surface where
**they** select content and write **their own** notes, then ship those
notes back to Claude. v5.0.1 still missed the actual ask.
v5.0.2 ships `scripts/annotate.mjs` — a small, focused, zero-dependency
Node script that 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 attach their own note, keeps a sidebar of
all notes (editable + deletable, persisted in `localStorage` per artifact
path so refresh doesn't lose work), and exposes a "Copy Prompt" button
that gathers every note into one structured prompt. The operator copies
that prompt and pastes it back into Claude; Claude revises the `.md`
freehand from the notes. **One file → one HTML → click + write notes →
copy prompt → paste back.** No Claude-generated suggestions in the loop.
The operator drives every annotation.
This is the v4.2/v4.3 *concept* (operator-driven annotation) without the
broken v4.2/v4.3 UX, without the 388 KB SPA, without `/trekrevise`,
without anchor parsers + Handover 8 + the JSON batch round-trip. ~430
lines of self-contained `.mjs`. Zero npm deps. Deterministic.
### Added
- **`scripts/annotate.mjs`** — operator-annotation HTML generator. Takes `<artifact.md>`, writes `<artifact>.html` (or `--out <file>`). Self-contained, design-system-aligned (light + dark + print), zero external network, deterministic. CLI: `node scripts/annotate.mjs <artifact.md> [--out <file.html>]`. Also `npm run annotate -- <artifact.md>`.
- **`tests/scripts/annotate.test.mjs`** (10 tests) — self-contained HTML shape, no external `<link>`/`<script src>`, inline script parses, source content + path embedded, HTML escaping in title + body (XSS surface), determinism, default output path, arg parsing, and the operator-driven affordances (Click any line, Your annotations sidebar, Copy Prompt, Clear all, localStorage).
- **`npm run annotate`** convenience script.
### Changed
- **`commands/trekbrief.md` Step 4g, `commands/trekplan.md` Phase 10, `commands/trekreview.md` Phase 8** — each now runs `scripts/annotate.mjs` after the artifact is final and prints the resulting `file://<abs path>` link with explicit "Click any line to add YOUR OWN note" instructions. The v5.0.1 `/playground build a document-critique playground for …` line is removed from all three.
- **`tests/lib/doc-consistency.test.mjs`** — replaced the v5.0.1 `/playground` pins with v5.0.2 pins: `scripts/annotate.mjs` exists; producing commands invoke it; producing commands no longer print the v5.0.1 `/playground document-critique` line; producing commands signal operator-driven annotation in their prose; CHANGELOG has a v5.0.2 entry.
- **Plugin `CLAUDE.md` + `README.md` + root `CLAUDE.md` + root `README.md` + `.claude-plugin/marketplace.json`** — voyage description updated from "v5.0.1 /playground invocation" to "v5.0.2 operator-annotation HTML (`scripts/annotate.mjs`)".
### Notes
- `/playground` is unchanged — the official `claude-plugins-official` `playground` skill is great for the Claude-leads, operator-reacts flow; it just wasn't the right tool for operator-leads, Claude-reacts.
- `npm test`: 516 tests, 514 pass, 0 fail, 2 skipped (up from 503 — 10 new `annotate.test.mjs` tests + 3 net new doc-consistency pins).
- Version bump 5.0.1 → 5.0.2 in `.claude-plugin/plugin.json`, `package.json`, `package-lock.json`, plugin `README.md` badge.
## v5.0.1 — 2026-05-13 — Drop the standalone HTML render; print a literal /playground invocation
**No new breaking changes beyond v5.0.0.** Forks that consumed
`scripts/render-artifact.mjs` directly (or invoked `npm run render`) must
remove that integration. Nothing else moves.
### Why
v5.0.0 had `/trekbrief`, `/trekplan`, and `/trekreview` each finish by
*both* rendering a read-only `{artifact}.html` view (via the new
`scripts/render-artifact.mjs`) *and* printing a vague instruction to "run
the `/playground` plugin (`document-critique` template) on the `.md` and
paste the prompt back". In practice the operator saw two HTMLs in their
project dir, no annotation UI on the rendered `.html`, and had to guess
the right `/playground` invocation. The read-only `.html` added confusion
without affording annotation — it duplicated work the `/playground`
HTML already does (formatted document on the left, annotations on the
right, Copy Prompt button at the bottom).
v5.0.1 deletes the redundant render and makes the printed `/playground`
invocation literal and copy-paste-ready. One paste from the operator
launches the `playground` skill, which loads its `document-critique`
template, reads the `.md`, builds the interactive HTML, opens it. Mark
suggestions, click Copy Prompt, paste back. Done.
### Removed
- **`scripts/render-artifact.mjs`** — the v5.0.0 standalone Markdown→HTML renderer (~280 lines, zero deps). Redundant with `/playground`'s HTML.
- **`tests/scripts/render-artifact.test.mjs`** (and the now-empty `tests/scripts/` dir).
- **`npm run render`** script alias in `package.json`.
- All references to `render-artifact.mjs`, `brief.html`, `plan.html`, `review.html` in `CLAUDE.md` (plugin + root), `README.md` (plugin + root), `.claude-plugin/marketplace.json`, and the three command files' final-output blocks.
### Changed
- **`commands/trekbrief.md` Step 4g (Finalize), `commands/trekplan.md` Phase 10 (Present and refine), `commands/trekreview.md` Phase 9 (Present summary)** — each now ends by printing a single boxed block with the literal text `/playground build a document-critique playground for {abs_path}` and a one-paragraph explanation of the paste-mark-copy-paste loop. The literal string is pinned by `tests/lib/doc-consistency.test.mjs` so it cannot soften back into "run the `/playground` plugin" without a test failure.
- **`tests/lib/doc-consistency.test.mjs`** — replaced the v5.0.0 `render-artifact.mjs exists` + `producing commands reference render-artifact.mjs` pins with v5.0.1 pins: `render-artifact.mjs` *no longer* exists; producing commands include the literal `/playground build a document-critique playground for` invocation; producing commands no longer reference `render-artifact.mjs`; `package.json scripts.render` is gone; CHANGELOG has both v5.0.0 and v5.0.1 entries.
- **Plugin `CLAUDE.md`** — "Render-and-link (v5.0.0)" paragraph rewritten to "Post-command annotation invocation (v5.0.1)" explaining the literal-paste contract; project-directory contract no longer lists `.html` siblings; "State" section's project-root inventory no longer lists `.html` files.
- **Plugin `README.md`** — "Rendered artifacts & annotation (v5.0.0)" section rewritten to "Reviewing and annotating artifacts (v5.0.1)" with a worked example of the printed output and a "What v5.0.1 changed from v5.0.0" sub-note. Top-of-README one-liner + bottom "Known limitations" note updated.
- **Root `CLAUDE.md`** + **root `README.md`** + **`.claude-plugin/marketplace.json`** — voyage description updated to v5.0.1 + the one-paste invocation model.
### Notes
- `/playground` is the `playground` skill from `claude-plugins-official`. It must be installed in the operator's environment for the printed command to work. If it isn't, the same effect is achievable by pasting the `.md` content into Claude with "review this and suggest changes" — manual freehand revision.
- `npm test`: 503 tests, 501 pass, 0 fail, 2 skipped (down from 509 — 8 `render-artifact.test.mjs` tests removed; the doc-consistency pins were updated to v5.0.1 contracts, net +2 tests).
- Version bump 5.0.0 → 5.0.1 in `.claude-plugin/plugin.json`, `package.json`, `package-lock.json`, plugin `README.md` badge.
## v5.0.0 — 2026-05-12 — Remove the bespoke playground; render artifacts to HTML + link ## v5.0.0 — 2026-05-12 — Remove the bespoke playground; render artifacts to HTML + link
**Breaking.** `/trekrevise` is removed. The `playground/` directory, Handover 8 **Breaking.** `/trekrevise` is removed. The `playground/` directory, Handover 8

View file

@ -6,6 +6,10 @@ Voyage — a contract-driven Claude Code pipeline: brief, research, plan, execut
> **v3.0.0 — architect step extracted from this plugin.** The plan command still auto-discovers `architecture/overview.md` if present, so any compatible producer (architect plugin no longer publicly distributed; the architecture/overview.md slot remains available for any compatible producer) plugs into the same slot. See [CHANGELOG.md](CHANGELOG.md) for migration history. > **v3.0.0 — architect step extracted from this plugin.** The plan command still auto-discovers `architecture/overview.md` if present, so any compatible producer (architect plugin no longer publicly distributed; the architecture/overview.md slot remains available for any compatible producer) plugs into the same slot. See [CHANGELOG.md](CHANGELOG.md) for migration history.
> **Trinity context (2026-05-13, informational).** Voyage is Tier 1 (per-task) of a three-tier architecture in active design under the author's private marketplace: Tier 2 `app-creator` (per-app — "what does the app need, what's the next brief?") produces briefs Voyage consumes; Tier 3 `app-factory` (per-portfolio — "which app needs me now?") aggregates state across multiple app-creator instances. Both are pre-implementation and will ship to Forgejo when ready. **Asymmetry is a hard invariant:** Voyage stays unaware of Tier 2/3. Handover 1 (brief format) is the only integration point — any compatible producer can feed Voyage, app-creator is not privileged. Brief-schema changes are therefore breaking changes for downstream consumers, formalized as a public contract in v5.4.
> **Cross-cutting invariant: brief framing must match operator intent (2026-05-15).** Etablert etter residiv. Briefen er pipelinens source of truth; operatørens intent lever i hodet + i memory-filer (`feedback_*`, `project_*`); pipelinen tvinger ikke alignment. Høyere reasoning-kraft polerer feil premiss istedenfor å utfordre det. **Tre lag av forsvar (input-siden), alle BLOCKER ved brudd når v5.5 shipper:** (1) eksplisitt `framing: preserve|refine|replace|new-direction` i brief-frontmatter, `AskUserQuestion`-validert før brief-prosa skrives; (2) memory-alignment check som ny dimensjon i `brief-reviewer` — sammenlikner brief-prosa mot relevante memory-filer og rapporterer eksplisitte motsigelser; (3) obligatorisk `## TL;DR`-seksjon (≤ 5 linjer) øverst i `brief.md`. Implementeres i v5.5 — se [ROADMAP.md](ROADMAP.md). Inntil shipping: operatør må manuelt sjekke at briefens framingord ikke motsier intent, særlig etter avvist iterasjon hvor "delta fra forrige" er en farlig default-ankring.
## Commands ## Commands
| Command | Description | Model | | Command | Description | Model |
@ -16,7 +20,7 @@ Voyage — a contract-driven Claude Code pipeline: brief, research, plan, execut
| `/trekexecute` | Execute — disciplined plan/session-spec executor with failure recovery | opus | | `/trekexecute` | Execute — disciplined plan/session-spec executor with failure recovery | opus |
| `/trekreview` | Review — independent post-hoc review of delivered code against the brief. Produces `review.md` with severity-tagged findings (Handover 6) | opus | | `/trekreview` | Review — independent post-hoc review of delivered code against the brief. Produces `review.md` with severity-tagged findings (Handover 6) | opus |
| `/trekcontinue` | Continue — resumes the next session of a multi-session voyage project. Reads `.session-state.local.json` (Handover 7) and immediately begins executing | opus | | `/trekcontinue` | Continue — resumes the next session of a multi-session voyage project. Reads `.session-state.local.json` (Handover 7) and immediately begins executing | opus |
| `/trekendsession` | End-session — mark the current session complete and write session-state pointing at the next session. Helper for informal multi-session flows | sonnet | | `/trekendsession` | End-session — mark the current session complete and write session-state pointing at the next session. Helper for informal multi-session flows | opus |
### /trekbrief modes ### /trekbrief modes
@ -107,26 +111,26 @@ The triage gate is deterministic — path-pattern classifier produces `{file →
| planning-orchestrator | opus | Inline reference documentation for the planning pipeline workflow (brief-driven) | | planning-orchestrator | opus | Inline reference documentation for the planning pipeline workflow (brief-driven) |
| research-orchestrator | opus | Inline reference documentation for the research pipeline workflow | | research-orchestrator | opus | Inline reference documentation for the research pipeline workflow |
| review-orchestrator | opus | Inline reference documentation for the review pipeline workflow | | review-orchestrator | opus | Inline reference documentation for the review pipeline workflow |
| architecture-mapper | sonnet | Codebase structure, tech stack, patterns | | architecture-mapper | opus | Codebase structure, tech stack, patterns |
| dependency-tracer | sonnet | Import chains, data flow, side effects | | dependency-tracer | opus | Import chains, data flow, side effects |
| task-finder | sonnet | Task-relevant files, functions, reuse candidates | | task-finder | opus | Task-relevant files, functions, reuse candidates |
| risk-assessor | sonnet | Risks, edge cases, failure modes | | risk-assessor | opus | Risks, edge cases, failure modes |
| test-strategist | sonnet | Test patterns, coverage gaps, strategy | | test-strategist | opus | Test patterns, coverage gaps, strategy |
| git-historian | sonnet | Recent changes, ownership, hot files | | git-historian | opus | Recent changes, ownership, hot files |
| research-scout | sonnet | External docs for unfamiliar tech (conditional, planning only) | | research-scout | opus | External docs for unfamiliar tech (conditional, planning only) |
| convention-scanner | sonnet | Coding conventions: naming, style, error handling, test patterns | | convention-scanner | opus | Coding conventions: naming, style, error handling, test patterns |
| brief-reviewer | sonnet | Task brief quality (5 dimensions: completeness, consistency, testability, scope clarity, research plan validity) | | brief-reviewer | opus | Task brief quality (5 dimensions: completeness, consistency, testability, scope clarity, research plan validity) |
| brief-conformance-reviewer | sonnet | Brief conformance review (SC + Non-Goal traceability) | | brief-conformance-reviewer | opus | Brief conformance review (SC + Non-Goal traceability) |
| code-correctness-reviewer | sonnet | Code correctness review (7 dimensions) | | code-correctness-reviewer | opus | Code correctness review (7 dimensions) |
| review-coordinator | sonnet | Judge Agent — dedup + reasonableness filter + verdict | | review-coordinator | opus | Judge Agent — dedup + reasonableness filter + verdict |
| plan-critic | sonnet | Adversarial plan review (9 dimensions) | | plan-critic | opus | Adversarial plan review (9 dimensions) |
| scope-guardian | sonnet | Scope alignment (creep + gaps) | | scope-guardian | opus | Scope alignment (creep + gaps) |
| session-decomposer | sonnet | Splits plans into headless sessions with dependency graph | | session-decomposer | opus | Splits plans into headless sessions with dependency graph |
| docs-researcher | sonnet | Official documentation, RFCs, vendor docs (Tavily, MS Learn) | | docs-researcher | opus | Official documentation, RFCs, vendor docs (Tavily, MS Learn) |
| community-researcher | sonnet | Community experience: issues, blogs, discussions | | community-researcher | opus | Community experience: issues, blogs, discussions |
| security-researcher | sonnet | CVEs, audit history, supply chain risks | | security-researcher | opus | CVEs, audit history, supply chain risks |
| contrarian-researcher | sonnet | Counter-evidence, overlooked alternatives | | contrarian-researcher | opus | Counter-evidence, overlooked alternatives |
| gemini-bridge | sonnet | Gemini Deep Research second opinion (conditional) | | gemini-bridge | opus | Gemini Deep Research second opinion (conditional) |
## Quality infrastructure (v3.4.0) ## Quality infrastructure (v3.4.0)
@ -187,9 +191,9 @@ Three built-in model profiles plus operator-defined `<custom>.yaml`. Each profil
| Profile | Brief | Research | Plan | Execute | Review | Continue | Use case | | Profile | Brief | Research | Plan | Execute | Review | Continue | Use case |
|---------|-------|----------|------|---------|--------|----------|----------| |---------|-------|----------|------|---------|--------|----------|----------|
| `economy` | sonnet | sonnet | sonnet | sonnet | sonnet | sonnet | Lowest cost; high-confidence small-scope tasks | | `economy` | sonnet | sonnet | sonnet | sonnet | sonnet | sonnet | Lowest cost; high-confidence small-scope tasks (operator-opt-in via `--profile economy`) |
| `balanced` (default) | sonnet | sonnet | opus | sonnet | opus | sonnet | Default — opus where reasoning depth pays off | | `balanced` | sonnet | sonnet | opus | sonnet | opus | sonnet | Mixed — opus where reasoning depth pays off (operator-opt-in via `--profile balanced`) |
| `premium` | opus | sonnet | opus | sonnet | opus | sonnet | Critical-path planning + review when budget allows | | `premium` (default) | opus | opus | opus | opus | opus | opus | Maximum quality — Opus on every phase. Default since 2026-05-13 operator request; also the hardcoded resolver default at `lib/profiles/resolver.mjs:145` |
### Lookup order ### Lookup order
@ -220,7 +224,9 @@ Local Docker Compose stack: `examples/observability/`. Operator docs: `docs/obse
## Architecture ## Architecture
**Brief:** 7-phase workflow: Parse mode → Create project dir → Phase 3 completeness loop (section-driven, no question cap) → Phase 4 draft/review/revise with `brief-reviewer` as stop-gate (max 3 iterations; gate = all dimensions ≥ 4 and research plan = 5) → Finalize (`brief.md` on pass, or `brief_quality: partial` on cap/force-stop) → Manual/auto opt-in → Stats. Always interactive. Auto mode runs research + plan inline in the main context (v2.4.0). **Brief:** 7-phase workflow: Parse mode → Create project dir → Phase 3 completeness loop (section-driven, no question cap) → Phase 3.5 per-phase effort dialog (v5.1) → Phase 4 draft/review/revise with `brief-reviewer` as stop-gate (max 3 iterations; gate = all dimensions ≥ 4 and research plan = 5) → Finalize (`brief.md` on pass, or `brief_quality: partial` on cap/force-stop) → Manual/auto opt-in → Stats. Always interactive. Auto mode runs research + plan inline in the main context (v2.4.0).
**Phase 3.5 (v5.1) — adaptive-depth signals:** Between Phase 3 completeness exit and Phase 4 draft, the operator commits an effort level (`low | standard | high`) and an optional `model` (`sonnet | opus`) per downstream phase (`research`, `plan`, `execute`, `review`) via 4 tier-coupled `AskUserQuestion` calls. The choices land in `brief.md` frontmatter as `phase_signals:` (a list of `{phase, effort?, model?}` entries) when committed, or `phase_signals_partial: true` when the operator force-stops. `brief_version: 2.1` activates the **sequencing gate**: validator emits `BRIEF_V51_MISSING_SIGNALS` if a 2.1-versioned brief lacks both fields. Downstream commands surface a friendly hint pointing back to `/trekbrief` — enforcement is validator-only. Composition is documented prose in each downstream command's `## Composition rule (v5.1)` section: `brief.phase_signals[phase] > profile.phase_models[phase]`. The brief signal wins per-phase when present; the profile fills gaps. `effort == low` activates each command's existing `--quick`-equivalent code-path (`/trekexecute` low-effort = `--gates open` + sequential-only). High-effort behavior is deferred to v5.1.1 per brief Non-Goal.
**Research:** Foreground workflow (v2.4.0): Parse mode → Interview → Parallel research swarm (5 local + 4 external + 1 bridge, spawned from main context) → Follow-ups → Triangulation → Synthesis + brief → Stats. With `--project`, writes to `{dir}/research/NN-slug.md`. **Research:** Foreground workflow (v2.4.0): Parse mode → Interview → Parallel research swarm (5 local + 4 external + 1 bridge, spawned from main context) → Follow-ups → Triangulation → Synthesis + brief → Stats. With `--project`, writes to `{dir}/research/NN-slug.md`.
@ -232,36 +238,40 @@ Local Docker Compose stack: `examples/observability/`. Operator docs: `docs/obse
**Continue:** `/trekcontinue` reads `{dir}/.session-state.local.json` (Handover 7), validates schema-v1 via `session-state-validator`, narrates a 3-line summary (project / next-session-label / brief-path), and immediately begins executing the next session. Auto-discovers active project state files under `.claude/projects/*/.session-state.local.json` if no explicit `<project-dir>` argument. Operator-invoked only — never auto-loaded via SessionStart. The `/trekendsession` helper is the informal-flow producer: writes the same state file for ad-hoc multi-session handovers that don't run through `/trekexecute`. **Continue:** `/trekcontinue` reads `{dir}/.session-state.local.json` (Handover 7), validates schema-v1 via `session-state-validator`, narrates a 3-line summary (project / next-session-label / brief-path), and immediately begins executing the next session. Auto-discovers active project state files under `.claude/projects/*/.session-state.local.json` if no explicit `<project-dir>` argument. Operator-invoked only — never auto-loaded via SessionStart. The `/trekendsession` helper is the informal-flow producer: writes the same state file for ad-hoc multi-session handovers that don't run through `/trekexecute`.
**Render-and-link (v5.0.0):** the last step of `/trekbrief`, `/trekplan`, and `/trekreview` renders the just-written `.md` artifact to a self-contained `.html` in the same project directory (`scripts/render-artifact.mjs` — zero npm deps, zero external network, design-system-styled, frontmatter folded into a `<details>` block) and prints the `file://` link. To annotate, the operator runs the official `/playground` plugin (`document-critique` template) on the `.md` and pastes the generated prompt back into the conversation; Claude revises the artifact freehand. This replaces the v4.2/v4.3 bespoke playground SPA + `/trekrevise` + Handover 8 (annotation → revision), all removed in v5.0.0 — see [CHANGELOG.md](CHANGELOG.md) § v5.0.0 for why (the bespoke playground duplicated capabilities the official `/playground` plugin already provides). **Operator-UX guarantee (since v5.0.2):** `/trekbrief`, `/trekplan`, and `/trekreview` MUST always emit (a) a plain `file://<abs path>` URL AND (b) a copy-pasteable `open file://<abs path>` command in the final report block. The file:// URL must use an ABSOLUTE path (not relative or `~/`-prefixed) so terminals with cmd+click support (Ghostty, iTerm2, modern Terminal.app) can resolve it without shell interpretation. This is a non-negotiable operator-UX contract — the doc-consistency test pins both forms in all three commands' final report blocks.
**Operator-annotation HTML (v5.0.3):** the last step of `/trekbrief`, `/trekplan`, and `/trekreview` runs `scripts/annotate.mjs` against the just-written `.md` and prints the resulting `file://<abs path>` link. The HTML is self-contained (zero npm deps, zero external network, design-system-styled, light + dark + print) and modelled on `~/repos/claude-code-100x/claude-code-100x/build-site.js` (lines 14312255). The operator opens the file, the document renders as a proper article (headings / paragraphs / lists / tables / code / quotes — every element gets a stable `data-anchor-id`). In annotation mode (default ON, pencil-toggle in topbar), the operator can **select any text or click any element** → a form popover opens at the cursor with: section context auto-detected from nearest h1/h2, the anchored snippet (selection if any, else element text), **three intent buttons (Fiks / Endre / Spørsmål)**, comment textarea, Save/Cancel. The sidebar (Show annotations button) lists every annotation grouped by section with intent badge + snippet + comment + delete; clicking a card scrolls to and flashes the source element. **Copy Prompt** assembles a structured markdown (`### N. [Intent] Section: <…>` + `Quote: «…»` + `Comment: …`) and copies to clipboard. Persistence: `localStorage` keyed on absolute artifact path (`voyage-annotate:v2:<abs path>`). v5.0.0 removed the v4.2/v4.3 bespoke playground SPA + `/trekrevise` + Handover 8; 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 intents); v5.0.3 matches the claude-code-100x reference the operator first pointed at, with pencil-toggle / selection capture / intent categories / popover form / structured export. See [CHANGELOG.md](CHANGELOG.md) § v5.0.3.
**Security:** 4-layer defense-in-depth: plugin hooks (pre-bash-executor, pre-write-executor), prompt-level denylist (works in headless sessions), pre-execution plan scan (Phase 2.4), scoped `--allowedTools` replacing `--dangerously-skip-permissions`. Hard Rules 14-16 enforce verify command security, repo-boundary writes, and sensitive path protection. **Security:** 4-layer defense-in-depth: plugin hooks (pre-bash-executor, pre-write-executor), prompt-level denylist (works in headless sessions), pre-execution plan scan (Phase 2.4), scoped `--allowedTools` replacing `--dangerously-skip-permissions`. Hard Rules 14-16 enforce verify command security, repo-boundary writes, and sensitive path protection.
**Pipeline:** `/trekbrief` produces the task brief. `/trekresearch --project <dir>` fills in `{dir}/research/`. `/trekplan --project <dir>` reads brief + research to produce `{dir}/plan.md` (and auto-discovers `{dir}/architecture/overview.md` if an opt-in upstream architect plugin produced one). `/trekexecute --project <dir>` executes and writes `{dir}/progress.json`. `/trekreview --project <dir>` produces `{dir}/review.md`. `/trekbrief`, `/trekplan`, and `/trekreview` each render their artifact to `{dir}/{artifact}.html` and print the link (annotate via the `/playground` plugin). All artifacts live in one project directory. **Pipeline:** `/trekbrief` produces the task brief. `/trekresearch --project <dir>` fills in `{dir}/research/`. `/trekplan --project <dir>` reads brief + research to produce `{dir}/plan.md` (and auto-discovers `{dir}/architecture/overview.md` if an opt-in upstream architect plugin produced one). `/trekexecute --project <dir>` executes and writes `{dir}/progress.json`. `/trekreview --project <dir>` produces `{dir}/review.md`. `/trekbrief`, `/trekplan`, and `/trekreview` each end by running `scripts/annotate.mjs` on the just-written artifact, producing `{dir}/{artifact}.html` — a self-contained operator-annotation surface — and printing the `file://` link. The operator opens it, clicks lines, writes their own notes, copies a structured prompt, pastes back, Claude revises the `.md`. All artifacts live in one project directory.
**Project-directory contract (v3.0.0):** trekplan owns the directory layout below. The `architecture/` subdirectory is opt-in and produced by an opt-in upstream architect plugin (not bundled) — the architect plugin is no longer publicly distributed, but the `architecture/overview.md` slot remains available for any compatible producer. **Project-directory contract (v3.0.0):** trekplan owns the directory layout below. The `architecture/` subdirectory is opt-in and produced by an opt-in upstream architect plugin (not bundled) — the architect plugin is no longer publicly distributed, but the `architecture/overview.md` slot remains available for any compatible producer.
``` ```
.claude/projects/{YYYY-MM-DD}-{slug}/ .claude/projects/{YYYY-MM-DD}-{slug}/
brief.md ← trekbrief writes; everyone reads brief.md ← trekbrief writes; everyone reads
brief.html ← trekbrief renders (self-contained; for browser viewing / /playground) brief.html ← trekbrief annotates (operator-annotation HTML, gitignored, re-buildable from brief.md)
research/*.md ← trekresearch writes; plan + architect read research/*.md ← trekresearch writes; plan + architect read
architecture/ ← OPT-IN, owned by an opt-in upstream architect plugin (not bundled) architecture/ ← OPT-IN, owned by an opt-in upstream architect plugin (not bundled)
overview.md overview.md
gaps.md gaps.md
plan.md ← trekplan writes; trekexecute reads plan.md ← trekplan writes; trekexecute reads
plan.html ← trekplan renders plan.html ← trekplan annotates
progress.json ← trekexecute writes progress.json ← trekexecute writes
review.md ← trekreview writes; trekplan reads (Handover 6) review.md ← trekreview writes; trekplan reads (Handover 6)
review.html ← trekreview renders review.html ← trekreview annotates
``` ```
The `.html` files (`brief.html`, `plan.html`, `review.html`) are produced by `scripts/annotate.mjs` and live alongside their `.md` siblings in the project directory. They are re-buildable from the `.md` source at any time (deterministic, byte-identical output on re-run), so they are conventionally gitignored along with the rest of `.claude/projects/`. Operator annotations live in browser `localStorage` keyed on the absolute artifact path — they survive refresh and browser-close, but are local to the operator's machine.
No code-level dependency between plugins — the contract is filesystem-level only. No code-level dependency between plugins — the contract is filesystem-level only.
## State ## State
All artifacts in one project directory (default): All artifacts in one project directory (default):
- Project root: `.claude/projects/{YYYY-MM-DD}-{slug}/` - Project root: `.claude/projects/{YYYY-MM-DD}-{slug}/`
- `brief.md` + `brief.html` (task brief from `/trekbrief`; `.html` is the self-contained rendered view) - `brief.md` + `brief.html` (task brief from `/trekbrief`; `.html` is the operator-annotation surface from `scripts/annotate.mjs`)
- `research/{NN}-{slug}.md` (research briefs from `/trekresearch --project`) - `research/{NN}-{slug}.md` (research briefs from `/trekresearch --project`)
- `architecture/overview.md` + `architecture/gaps.md` (opt-in, produced by an opt-in upstream architect plugin, not bundled) - `architecture/overview.md` + `architecture/gaps.md` (opt-in, produced by an opt-in upstream architect plugin, not bundled)
- `plan.md` + `plan.html` (from `/trekplan --project`) - `plan.md` + `plan.html` (from `/trekplan --project`)
@ -293,3 +303,20 @@ Stats:
- **Session state**`.session-state.local.json` per project. **Handover 7** — produced by any session-end mechanism (`/trekexecute` Phase 8/2.55/4, `/trekendsession` helper, future graceful-handoff v2.2). Consumed by `/trekcontinue` to resume the next session in a fresh chat. Schema v1 is forward-compat (unknown top-level keys ignored). Never committed (gitignored via `*.local.json`). - **Session state**`.session-state.local.json` per project. **Handover 7** — produced by any session-end mechanism (`/trekexecute` Phase 8/2.55/4, `/trekendsession` helper, future graceful-handoff v2.2). Consumed by `/trekcontinue` to resume the next session in a fresh chat. Schema v1 is forward-compat (unknown top-level keys ignored). Never committed (gitignored via `*.local.json`).
A project typically has 1 task brief, 0N research briefs, 0 or 1 architecture note, 0N reviews (one per review iteration), and 0 or 1 session-state file (overwritten on every session-end). A project typically has 1 task brief, 0N research briefs, 0 or 1 architecture note, 0N reviews (one per review iteration), and 0 or 1 session-state file (overwritten on every session-end).
## Communication patterns
### Linking to local files
When pointing to local files in responses, always use markdown link syntax with a descriptive name:
- Use `[Human-friendly name](file:///absolute/path)` — never bare `file:///...` URLs or autolinks `<file://...>`.
- Always use absolute paths. Never `~/` or relative paths.
- For multiple files, render as a bullet list of named markdown links.
Why: bare `file://` URLs only render the first as clickable across multiple lines. Named markdown links make each entry independently clickable and look cleaner.
Example:
- [Brief](file:///Users/ktg/.../brief.html)
- [Research summary](file:///Users/ktg/.../research/summary.md)

View file

@ -1,6 +1,6 @@
# trekplan — Brief, Research, Plan, Execute, Review, Continue # trekplan — Brief, Research, Plan, Execute, Review, Continue
![Version](https://img.shields.io/badge/version-5.0.0-blue) ![Version](https://img.shields.io/badge/version-5.1.1-blue)
![License](https://img.shields.io/badge/license-MIT-green) ![License](https://img.shields.io/badge/license-MIT-green)
![Platform](https://img.shields.io/badge/platform-Claude%20Code-purple) ![Platform](https://img.shields.io/badge/platform-Claude%20Code-purple)
@ -10,6 +10,15 @@
A [Claude Code](https://docs.anthropic.com/en/docs/claude-code) plugin for deep implementation planning, multi-source research, autonomous execution, independent post-hoc review, and zero-friction multi-session resumption. Six commands, one pipeline: A [Claude Code](https://docs.anthropic.com/en/docs/claude-code) plugin for deep implementation planning, multi-source research, autonomous execution, independent post-hoc review, and zero-friction multi-session resumption. Six commands, one pipeline:
> **What's new in v5.1.1** — Remediation patch closing 11 of 12 findings from the v5.1.0 review (SC8 dogfood gate scheduled for sesjon 8). Lukker:
> - **Bug fixes (load-bearing):** YAML-number bypass in `brief-validator` (#8) + doc-consistency pin lock-in (#11) so the gate fires for both quoted and unquoted `brief_version`.
> - **Wiring:** `phase-signal-resolver` helper wired into all 4 downstream commands (#9) with TDD pair `resolvePhaseModel` + profile-resolver non-interference test (#4 SC5); `brief-validator` gate required uniformly in `/trekresearch` + `/trekexecute` (#12).
> - **Test refactor:** runtime SC1 walk for trekbrief (#1) + per-tier resolver-output + missing-signals falsification for `/trekplan`/`/trekresearch`/`/trekreview`/`/trekexecute` (#2 #3 #6 #10) + dedicated SC5 test (#7).
> - **Documentation:** Dogfood-gate scheduling in REMEMBER (#5, sesjon 8 manual) + Decision B high-effort behavior per command + brief Non-Goal/SC1 amendments + coordinator high-effort normalization.
> v5.1.1 is additive — no breaking changes against v5.1.0.
>
> **What v5.1 introduced**`/trekbrief` Phase 3.5 commits per-phase `phase_signals` (effort + optional model for `research`/`plan`/`execute`/`review`) to `brief.md` frontmatter. `brief_version: 2.1` activates a validator-side sequencing gate (`BRIEF_V51_MISSING_SIGNALS`) so downstream commands halt with a friendly hint when signals are missing. Composition rule per downstream command: brief signal wins per-phase, profile fills gaps. `effort == low` activates the existing `--quick`-equivalent code-path in each command (`/trekexecute` low-effort = `--gates open` + sequential). Additive — no breaking changes; pre-2.1 briefs still validate.
| Command | What it does | | Command | What it does |
|---------|-------------| |---------|-------------|
| **`/trekbrief`** | Brief — interactive interview produces a task brief with explicit research plan | | **`/trekbrief`** | Brief — interactive interview produces a task brief with explicit research plan |
@ -19,9 +28,9 @@ A [Claude Code](https://docs.anthropic.com/en/docs/claude-code) plugin for deep
| **`/trekreview`** | Review — independent post-hoc review of delivered code against the brief, severity-tagged findings | | **`/trekreview`** | Review — independent post-hoc review of delivered code against the brief, severity-tagged findings |
| **`/trekcontinue`** | Continue — read `.session-state.local.json` and resume the next session in a multi-session project | | **`/trekcontinue`** | Continue — read `.session-state.local.json` and resume the next session in a multi-session project |
`/trekbrief`, `/trekplan`, and `/trekreview` also render their artifact to a self-contained `.html` next to it and print the `file://` link — annotate via the official `/playground` plugin (`document-critique`) and paste its prompt back. `/trekbrief`, `/trekplan`, and `/trekreview` each end by running `scripts/annotate.mjs` against the just-written artifact and printing the resulting `file://<abs path>` link. The operator opens the HTML in a browser, clicks any line of the document, writes their own note in the inline textarea, watches a sidebar of all notes (editable, deletable, persisted in browser `localStorage`), and clicks "Copy Prompt" to get one structured prompt that they paste back into Claude — Claude then revises the `.md` from the notes. **The operator drives every annotation.** See [Reviewing and annotating artifacts](#reviewing-and-annotating-artifacts-v502).
Every artifact lives in one project directory: `.claude/projects/{YYYY-MM-DD}-{slug}/` contains `brief.md` (+ `brief.html`), `research/NN-*.md`, `plan.md` (+ `plan.html`), `sessions/`, `progress.json`, and `review.md` (+ `review.html`). Every artifact lives in one project directory: `.claude/projects/{YYYY-MM-DD}-{slug}/` contains `brief.md`, `research/NN-*.md`, `plan.md`, `sessions/`, `progress.json`, and `review.md`.
### Division of labor ### Division of labor
@ -503,37 +512,81 @@ Both arguments are required. No interactive prompt — headless-safe.
--- ---
## Rendered artifacts & annotation (v5.0.0) ## Reviewing and annotating artifacts (v5.0.3)
`/trekbrief`, `/trekplan`, and `/trekreview` each finish by rendering their `/trekbrief`, `/trekplan`, and `/trekreview` each end by running
just-written `.md` to a self-contained `.html` next to it `scripts/annotate.mjs` against the just-written `.md` and printing the
(`{project_dir}/brief.html`, `plan.html`, `review.html`) and printing the resulting `file://<abs path>` link. After they finish you see something
`file://` link. The renderer (`scripts/render-artifact.mjs`) is a small, like:
zero-dependency Node script: it folds frontmatter into a `<details>` block,
puts code fences in styled `<pre>`, renders tables/lists/links, and inlines a
compact design-system-aligned stylesheet. **No external network, no build
step, no telemetry.** Two runs on the same input produce byte-identical HTML.
To **annotate** an artifact, run the official `/playground` plugin ```
(`document-critique` template) on the `.md` file and paste the prompt it Brief written: .claude/projects/2026-05-13-foo/brief.md
generates back into the conversation — Claude then revises the artifact Annotation HTML: file:///abs/path/.claude/projects/2026-05-13-foo/brief.html
freehand from your notes. The `/playground` plugin already produces clean,
self-contained single-file HTML for exactly this; voyage no longer ships its
own annotation UI.
```bash ────────────────────────────────────────────────────────────────────
# Render any artifact manually (the producing commands do this automatically): To review and annotate this brief, open the HTML above in a browser:
node plugins/voyage/scripts/render-artifact.mjs \
.claude/projects/2026-05-09-feature/plan.md open file:///abs/path/.claude/projects/2026-05-13-foo/brief.html
# → writes .claude/projects/2026-05-09-feature/plan.html, prints the path
Click any line to add YOUR OWN note. The sidebar collects every note,
the "Copy Prompt" button gathers them into one structured prompt.
Paste that prompt back into this chat and Claude revises brief.md
from your notes. Annotations persist in your browser if you close
the tab and reopen the same file.
────────────────────────────────────────────────────────────────────
``` ```
> **Removed in v5.0.0.** v4.2/v4.3 shipped a ~388 KB bespoke playground SPA + You run `open` (or click the `file://` link in your terminal), the HTML
> `/trekrevise` + Handover 8 (annotation → revision). A browser walkthrough opens in your default browser. The annotation UX is modelled on
> found it borderline unusable, and it duplicated the official `/playground` `claude-code-100x/build-site.js`:
> plugin's `document-critique` / `diff-review` templates. All of it — the SPA,
> the command, the supporting `lib/` modules, the anchor parser, the Playwright - **Topbar:** pencil-toggle button — annotation mode default ON. Click
> e2e suite — was deleted. See [CHANGELOG.md](CHANGELOG.md) § v5.0.0. to turn off (then you read the article normally, follow links, etc.).
A second button opens the sidebar panel.
- **Article body:** the artifact rendered as a proper article — headings,
paragraphs, lists, tables, code blocks, blockquotes. Hover any element
in mode and it highlights. To anchor on a specific phrase, **select
the text first**, then click. Otherwise the whole element becomes the
anchor.
- **Form popover** appears at the cursor with:
- **Section** (auto-detected from the nearest h1/h2 above).
- **Anchored to** — the exact text you selected, or the element's
first ~200 chars if you didn't select.
- **Three intent buttons:** **Fiks** (something is wrong — fix it),
**Endre** (change the wording / content), **Spørsmål** (an open
question — clarify or answer). Colored: red / orange / blue.
- **Comment** textarea (optional but helpful).
- **Cancel** / **Save**. Save stays disabled until you pick an intent.
Shortcut: `⌘Enter` to save, `Esc` to cancel.
- **Annotated elements** get an amber highlight + a number badge in the
margin showing how many annotations target that element.
- **Sidebar panel** (Show annotations) — every annotation grouped by
section, in document order. Each card shows the intent badge
(colored), the anchored snippet (mono-quote), the comment text, and a
delete button. Click a card to scroll the article to that element and
flash it.
- **Copy Prompt** at the foot of the panel — assembles every annotation
into one structured markdown prompt and copies it to your clipboard.
- **Clear all** wipes every annotation (after confirm).
- **Persistence:** every annotation is saved to browser `localStorage`
keyed on the artifact's absolute path (`voyage-annotate:v2:<abs path>`).
Refresh the tab or close the browser and re-open — your work is there.
You select / click, pick intent, write comment, repeat. When you're
done, Copy Prompt, paste back into this chat. Claude revises the `.md`
freehand from your notes. **The operator drives every annotation.**
Claude never pre-generates suggestions in this flow.
> **What v5.0.3 changed from v5.0.2.** v5.0.2 was operator-led but the UX
> was too thin — click a line, type a freeform note, save. The reference
> the operator pointed at (`~/repos/claude-code-100x/claude-code-100x/build-site.js`)
> already had the right pattern: pencil-toggle, selection capture, three
> intent categories, popover form, structured markdown export. v5.0.3
> rebuilds `scripts/annotate.mjs` against that reference. v5.0.0 / v5.0.1
> / v5.0.2 are all superseded; only the v5.0.0 removals (bespoke
> playground SPA, `/trekrevise`, Handover 8, supporting `lib/` modules,
> Playwright e2e + devDeps) stay. See [CHANGELOG.md](CHANGELOG.md)
> § v5.0.0 → § v5.0.3.
--- ---
@ -667,7 +720,7 @@ The `pre-compact-flush.mjs` hook directly fixes the documented P0 in `docs/treke
**Infrastructure-as-code (IaC) gets reduced value.** The exploration agents are designed for application code. Terraform, Helm, Pulumi, CDK projects will get a plan, but agents like `architecture-mapper` and `test-strategist` produce less useful output for IaC. Use trekplan for the structural plan, then supplement IaC-specific steps manually. **Infrastructure-as-code (IaC) gets reduced value.** The exploration agents are designed for application code. Terraform, Helm, Pulumi, CDK projects will get a plan, but agents like `architecture-mapper` and `test-strategist` produce less useful output for IaC. Use trekplan for the structural plan, then supplement IaC-specific steps manually.
**Rendered HTML is read-only.** `scripts/render-artifact.mjs` produces a static, self-contained view for browsing — it is not an editor. To revise an artifact from operator feedback, run the `/playground` plugin (`document-critique`) on the `.md` and paste its prompt back. The markdown subset the renderer supports covers what the artifact templates emit (headings, lists, code fences, tables, links, blockquotes, bold/italic, inline code); exotic markdown extensions are not rendered. **Annotation HTML requires a desktop browser.** `scripts/annotate.mjs` produces a single self-contained `.html` file you open with `file://` in any modern browser (Chrome / Safari / Firefox / Edge — last two versions). No CDN, no server, no npm runtime deps. State persists in `localStorage` so closing and re-opening the tab keeps your work, but it's local to one browser on one machine — not synced anywhere. If you want to annotate without a browser, paste the `.md` into Claude with "comments inline below" and write notes in chat — same end result, just without the visual surface.
## Installation ## Installation

View file

@ -21,7 +21,7 @@ description: |
Direct architecture analysis request triggers the agent. Direct architecture analysis request triggers the agent.
</commentary> </commentary>
</example> </example>
model: sonnet model: opus
color: cyan color: cyan
tools: ["Read", "Glob", "Grep", "Bash"] tools: ["Read", "Glob", "Grep", "Bash"]
--- ---

View file

@ -5,7 +5,7 @@ description: |
against the task brief — every Success Criterion must trace to delivered against the task brief — every Success Criterion must trace to delivered
code, every Non-Goal must remain unbuilt. Emits findings with rule_keys code, every Non-Goal must remain unbuilt. Emits findings with rule_keys
from the canonical RULE_CATALOGUE. Never praises. from the canonical RULE_CATALOGUE. Never praises.
model: sonnet model: opus
color: magenta color: magenta
tools: ["Read", "Glob", "Grep"] tools: ["Read", "Glob", "Grep"]
--- ---

View file

@ -23,7 +23,7 @@ description: |
Brief review request triggers the agent. Brief review request triggers the agent.
</commentary> </commentary>
</example> </example>
model: sonnet model: opus
color: magenta color: magenta
tools: ["Read", "Glob", "Grep"] tools: ["Read", "Glob", "Grep"]
--- ---

View file

@ -6,7 +6,7 @@ description: |
cross-file regressions, test coverage gaps, placeholder code, security cross-file regressions, test coverage gaps, placeholder code, security
surface, hidden dependencies. Cites file:line for every finding. Never surface, hidden dependencies. Cites file:line for every finding. Never
praises. praises.
model: sonnet model: opus
color: red color: red
tools: ["Read", "Glob", "Grep"] tools: ["Read", "Glob", "Grep"]
--- ---

View file

@ -24,7 +24,7 @@ description: |
finds the practical signal that helps teams make adoption decisions. finds the practical signal that helps teams make adoption decisions.
</commentary> </commentary>
</example> </example>
model: sonnet model: opus
color: green color: green
tools: ["WebSearch", "WebFetch", "mcp__tavily__tavily_search", "mcp__tavily__tavily_research"] tools: ["WebSearch", "WebFetch", "mcp__tavily__tavily_search", "mcp__tavily__tavily_research"]
--- ---

View file

@ -24,7 +24,7 @@ description: |
but to ensure the final recommendation is genuinely considered. but to ensure the final recommendation is genuinely considered.
</commentary> </commentary>
</example> </example>
model: sonnet model: opus
color: red color: red
tools: ["WebSearch", "WebFetch", "mcp__tavily__tavily_search", "mcp__tavily__tavily_research"] tools: ["WebSearch", "WebFetch", "mcp__tavily__tavily_search", "mcp__tavily__tavily_research"]
--- ---

View file

@ -23,7 +23,7 @@ description: |
Direct convention discovery request triggers the agent. Direct convention discovery request triggers the agent.
</commentary> </commentary>
</example> </example>
model: sonnet model: opus
color: yellow color: yellow
tools: ["Read", "Glob", "Grep", "Bash"] tools: ["Read", "Glob", "Grep", "Bash"]
--- ---

View file

@ -21,7 +21,7 @@ description: |
Impact analysis request triggers the agent. Impact analysis request triggers the agent.
</commentary> </commentary>
</example> </example>
model: sonnet model: opus
color: blue color: blue
tools: ["Read", "Glob", "Grep", "Bash"] tools: ["Read", "Glob", "Grep", "Bash"]
--- ---

View file

@ -23,7 +23,7 @@ description: |
microsoft_docs_fetch) that docs-researcher uses for higher-quality results. microsoft_docs_fetch) that docs-researcher uses for higher-quality results.
</commentary> </commentary>
</example> </example>
model: sonnet model: opus
color: blue color: blue
tools: ["WebSearch", "WebFetch", "Read", "mcp__tavily__tavily_search", "mcp__tavily__tavily_research", "mcp__microsoft-learn__microsoft_docs_search", "mcp__microsoft-learn__microsoft_docs_fetch"] tools: ["WebSearch", "WebFetch", "Read", "mcp__tavily__tavily_search", "mcp__tavily__tavily_research", "mcp__microsoft-learn__microsoft_docs_search", "mcp__microsoft-learn__microsoft_docs_fetch"]
--- ---

View file

@ -24,7 +24,7 @@ description: |
Direct request for Gemini research on a complex architectural question triggers the agent. Direct request for Gemini research on a complex architectural question triggers the agent.
</commentary> </commentary>
</example> </example>
model: sonnet model: opus
color: magenta color: magenta
tools: ["mcp__gemini-mcp__gemini_deep_research", "mcp__gemini-mcp__gemini_get_research_status", "mcp__gemini-mcp__gemini_get_research_result", "mcp__gemini-mcp__gemini_research_followup"] tools: ["mcp__gemini-mcp__gemini_deep_research", "mcp__gemini-mcp__gemini_get_research_status", "mcp__gemini-mcp__gemini_get_research_result", "mcp__gemini-mcp__gemini_research_followup"]
--- ---

View file

@ -21,7 +21,7 @@ description: |
Git history analysis request triggers the agent. Git history analysis request triggers the agent.
</commentary> </commentary>
</example> </example>
model: sonnet model: opus
color: yellow color: yellow
tools: ["Bash", "Read", "Glob", "Grep"] tools: ["Bash", "Read", "Glob", "Grep"]
--- ---

View file

@ -21,7 +21,7 @@ description: |
Plan review request triggers the agent. Plan review request triggers the agent.
</commentary> </commentary>
</example> </example>
model: sonnet model: opus
color: red color: red
tools: ["Read", "Glob", "Grep"] tools: ["Read", "Glob", "Grep"]
--- ---

View file

@ -137,7 +137,7 @@ medium: default, large: default) rather than dropping agents.
| `research-scout` | Conditional | Conditional | Conditional | External docs (only when unfamiliar tech detected AND not covered by briefs) | | `research-scout` | Conditional | Conditional | Conditional | External docs (only when unfamiliar tech detected AND not covered by briefs) |
| `convention-scanner` | No | Yes | Yes | Coding conventions, naming, style, test patterns | | `convention-scanner` | No | Yes | Yes | Coding conventions, naming, style, test patterns |
**Convention Scanner** — use the `convention-scanner` plugin agent (model: "sonnet") **Convention Scanner** — use the `convention-scanner` plugin agent (model: "opus")
for medium+ codebases only. Pass the task description as context. for medium+ codebases only. Pass the task description as context.
**research-scout** — launch conditionally if the task involves technologies, APIs, **research-scout** — launch conditionally if the task involves technologies, APIs,

View file

@ -21,7 +21,7 @@ description: |
Research request for external technology triggers the agent. Research request for external technology triggers the agent.
</commentary> </commentary>
</example> </example>
model: sonnet model: opus
color: blue color: blue
tools: ["WebSearch", "WebFetch", "Read"] tools: ["WebSearch", "WebFetch", "Read"]
--- ---

View file

@ -6,7 +6,7 @@ description: |
applies BOUNDED operations: deduplication, severity ranking, HubSpot applies BOUNDED operations: deduplication, severity ranking, HubSpot
Judge filters, Cloudflare reasonableness filter, verdict computation. Judge filters, Cloudflare reasonableness filter, verdict computation.
Synthesis-level inference across files is forbidden in v1.0. Synthesis-level inference across files is forbidden in v1.0.
model: sonnet model: opus
color: yellow color: yellow
tools: ["Read", "Glob", "Grep"] tools: ["Read", "Glob", "Grep"]
--- ---
@ -112,6 +112,18 @@ Drop findings that fail ANY of these tests:
In `quick` mode, skip this pass entirely. Note the skip in the In `quick` mode, skip this pass entirely. Note the skip in the
Executive Summary so the reader knows reasonableness was not applied. Executive Summary so the reader knows reasonableness was not applied.
**High-effort normalization (v5.1.1):** When the review is invoked
under high-effort mode (`phase_signals[review].effort: high`), Pass 3
reasonableness filtering is bypassed. To prevent unknown rule_keys
from polluting downstream plans, the coordinator MUST substitute any
rule_key not exported from `lib/review/rule-catalogue.mjs:RULE_KEYS`
with the literal string `PLAN_EXECUTE_DRIFT` (the most general drift
category from the 12-entry catalogue). The original rule_key is
preserved in the finding's `original_rule_key` field for diagnostic
purposes. This normalization happens BEFORE writing review.md,
ensuring all `rule_key` values in the final review match the
catalogue.
### Pass 4 — Compute verdict ### Pass 4 — Compute verdict
Count findings by severity AFTER dedup and filtering. Verdict thresholds: Count findings by severity AFTER dedup and filtering. Verdict thresholds:

View file

@ -21,7 +21,7 @@ description: |
Risk analysis request triggers the agent. Risk analysis request triggers the agent.
</commentary> </commentary>
</example> </example>
model: sonnet model: opus
color: yellow color: yellow
tools: ["Read", "Glob", "Grep", "Bash"] tools: ["Read", "Glob", "Grep", "Bash"]
--- ---

View file

@ -21,7 +21,7 @@ description: |
Scope verification request triggers the agent. Scope verification request triggers the agent.
</commentary> </commentary>
</example> </example>
model: sonnet model: opus
color: magenta color: magenta
tools: ["Read", "Glob", "Grep"] tools: ["Read", "Glob", "Grep"]
--- ---

View file

@ -23,7 +23,7 @@ description: |
using CVE databases, OWASP categories, and verified audit reports. using CVE databases, OWASP categories, and verified audit reports.
</commentary> </commentary>
</example> </example>
model: sonnet model: opus
color: red color: red
tools: ["WebSearch", "WebFetch", "mcp__tavily__tavily_search", "mcp__tavily__tavily_research"] tools: ["WebSearch", "WebFetch", "mcp__tavily__tavily_search", "mcp__tavily__tavily_research"]
--- ---

View file

@ -22,7 +22,7 @@ description: |
Plan decomposition request for parallel headless execution. Plan decomposition request for parallel headless execution.
</commentary> </commentary>
</example> </example>
model: sonnet model: opus
color: green color: green
tools: ["Read", "Glob", "Grep", "Write"] tools: ["Read", "Glob", "Grep", "Write"]
--- ---

View file

@ -22,7 +22,7 @@ description: |
Direct code discovery request triggers the agent. Direct code discovery request triggers the agent.
</commentary> </commentary>
</example> </example>
model: sonnet model: opus
color: green color: green
tools: ["Read", "Glob", "Grep", "Bash"] tools: ["Read", "Glob", "Grep", "Bash"]
--- ---

View file

@ -21,7 +21,7 @@ description: |
Test planning request triggers the agent. Test planning request triggers the agent.
</commentary> </commentary>
</example> </example>
model: sonnet model: opus
color: green color: green
tools: ["Read", "Glob", "Grep", "Bash"] tools: ["Read", "Glob", "Grep", "Bash"]
--- ---

View file

@ -288,6 +288,118 @@ Phase 3 complete: {N} questions asked across {M} sections.
Proceeding to draft and review. Proceeding to draft and review.
``` ```
## Phase 3.5 — Per-phase effort dialog
Phase 3.5 is the v5.1 entry-point for **adaptive-depth execution**. After
Phase 3 has gathered intent / goal / success criteria / research plan, the
operator commits an effort level and (optional) model per downstream phase
(`research`, `plan`, `execute`, `review`). The committed signals are written
to brief frontmatter as a `phase_signals:` list that the four downstream
commands read via their `## Composition rule (v5.1)` section.
### State requirements
Before entering Phase 3.5 the following must be populated:
- `state.intent` — the Phase 3 Intent answer (1+ paragraph)
- `state.goal` — the Phase 3 Goal answer
- `state.success_criteria` — at least one falsifiable SC
- `state.research_plan.topics` — list (may be empty)
If any are absent: skip Phase 3.5 entirely and write `phase_signals_partial:
true` to the draft frontmatter. Do not block.
### --quick mode
If the operator launched with `--quick`: skip Phase 3.5 entirely and
auto-write `phase_signals_partial: true` to draft frontmatter. The brief
will satisfy the v5.1 sequencing gate without going through the dialog.
### Default-derivation heuristic (LLM judgment, not algorithmic)
Before each phase question, propose a default tier marked `(default)`. Use
these signals — they are weak heuristics, not rules:
- `research_topics_count` → high (`high`), low (`low`), absent (`low`)
- `sc_count` (count of falsifiable SCs) → high (≥6 ⇒ `high`), low (≤2 ⇒ `low`)
- Goal complexity: keywords like "rewrite", "migration", "refactor across",
"new platform" ⇒ `high`; "typo", "small bugfix", "docs touch-up" ⇒ `low`
- Otherwise: `standard`
Mix these into one proposed default per phase. Document the proposed tier
in the question body so the operator sees why it was picked.
### The loop — 4 tier-coupled AskUserQuestion calls
Loop over `[research, plan, execute, review]` in order. For each phase,
issue one `AskUserQuestion` with 3 options:
| Option | Maps to phase_signals entry |
|--------|----------------------------|
| **Low effort** | `{phase: <name>, effort: low, model: sonnet}` |
| **Standard (default)** | `{phase: <name>, effort: standard}` *(model omitted — composition falls through to profile)* |
| **High effort** | `{phase: <name>, effort: high, model: opus}` |
The proposed tier per phase (from the default-derivation heuristic) MUST be
labelled `(default)` in the option list so the operator can one-click
accept. Commit the chosen tier immediately to an in-memory `effort_state`
dict — no bulk summary-before-commit. The loop is interruptible.
The mapping table is canonical:
- `low → {effort: low, model: sonnet}` (force sonnet for the low-cost path)
- `standard → {effort: standard}` (model omitted; composition rule resolves via profile)
- `high → {effort: high, model: opus}` (force opus for the high-confidence path)
### Force-stop handling
If during any of the four `AskUserQuestion` calls the operator says "stop",
"skip", "enough", "just write it", or similar, do NOT exit silently — apply
the Phase 4f force-stop pattern verbatim:
```
You stopped before committing per-phase signals. Remaining phases:
- {list of phases not yet answered}
The brief will still be valid (v5.1 supports `phase_signals_partial: true`
as a force-stop record). Downstream commands will fall back to the profile
resolver for the un-committed phases.
Continue anyway?
```
Then `AskUserQuestion`:
| Option | Action |
|--------|--------|
| **Answer one more phase** | Return to the next un-answered phase question. |
| **Stop now (record partial)** | Drop any in-progress `effort_state` and set `phase_signals_partial: true` in draft frontmatter. Mutually exclusive with `phase_signals`. Break Phase 3.5. |
This pattern matches Step 4f (line 436-458) so the force-stop UX is
identical across both surfaces.
### Hand-off to Phase 4a
If `effort_state` is fully populated (4 commits, no force-stop): write a
`phase_signals:` block to draft frontmatter — one entry per phase,
preserving the canonical-mapping form above. Omit `model:` for standard
tier (composition falls through to profile).
If `phase_signals_partial: true` was set: write that single line to draft
frontmatter and skip the `phase_signals:` block (mutually exclusive per
validator).
Phase 4a (Step 4a — Draft in memory) reads from `effort_state` /
`phase_signals_partial` and incorporates the appropriate frontmatter block
into the draft brief.
### Sequencing gate (downstream)
`brief_version: 2.1` activates the validator's sequencing gate. If the
final brief reaches `/trekplan`, `/trekresearch`, `/trekexecute`, or
`/trekreview` WITHOUT `phase_signals` and WITHOUT `phase_signals_partial:
true`, the validator emits `BRIEF_V51_MISSING_SIGNALS` and the command
halts with a friendly hint pointing back to `/trekbrief`.
## Phase 4 — Draft, review, and revise ## Phase 4 — Draft, review, and revise
Phase 4 runs a **draft → brief-reviewer → revise** loop. The draft is Phase 4 runs a **draft → brief-reviewer → revise** loop. The draft is
@ -483,30 +595,47 @@ If the validator returns errors, report them to the user and offer to
re-enter Phase 4 with the validator's hints in scope. If only warnings, re-enter Phase 4 with the validator's hints in scope. If only warnings,
note them in the final report. note them in the final report.
**Render to HTML + link (annotation via /playground):** after `brief.md` **Build the operator-annotation HTML, then print the report.** After the
is final, render it to a self-contained HTML view in the same directory: brief is validated, run `scripts/annotate.mjs` to produce a self-contained
HTML file the operator opens in their browser. The HTML renders the brief
with line numbers, lets the operator click any line to attach their own
note (not Claude-generated suggestions — the operator drives every
annotation), keeps a sidebar of all notes, persists state in localStorage,
and exposes a "Copy Prompt" button that generates a single structured
prompt with every note. The operator copies that prompt and pastes it
back into Claude; Claude revises `brief.md` freehand from the notes.
```bash ```bash
node ${CLAUDE_PLUGIN_ROOT}/scripts/render-artifact.mjs "{PROJECT_DIR}/brief.md" ANNOT_HTML=$(node ${CLAUDE_PLUGIN_ROOT}/scripts/annotate.mjs "{PROJECT_DIR}/brief.md" 2>&1)
# stdout is the absolute path to the .html on success.
``` ```
This writes `{PROJECT_DIR}/brief.html` — zero-network, design-system-styled If `annotate.mjs` exits non-zero, surface a one-line warning and continue
(frontmatter folded into a `<details>` block). If it exits non-zero, surface — the annotation HTML is a convenience, not a gate. The report below
a one-line warning and continue — the rendered view is a convenience, not a still mentions the (failed) path so the operator can debug.
gate.
Then print this block **verbatim** (substitute `{PROJECT_DIR}` and
`$ANNOT_HTML`):
Report:
``` ```
Brief written: {PROJECT_DIR}/brief.md Brief written: {PROJECT_DIR}/brief.md
Brief rendered: file://{abs path to brief.html} Annotation HTML: file://{$ANNOT_HTML}
Review iterations: {1..3} Review iterations: {1..3}
Final quality: {complete | partial} Final quality: {complete | partial}
Validator: {PASS | warnings(N)} Validator: {PASS | warnings(N)}
Research topics identified: {N} Research topics identified: {N}
To annotate: open brief.html, then run the `/playground` plugin ────────────────────────────────────────────────────────────────────
(document-critique template) on brief.md and paste the generated To review and annotate this brief, open the HTML above in a browser:
prompt back here. Claude revises brief.md freehand from your notes.
open file://{$ANNOT_HTML}
Click any line to add YOUR OWN note. The sidebar collects every note,
the "Copy Prompt" button gathers them into one structured prompt.
Paste that prompt back into this chat and Claude revises brief.md
from your notes. Annotations persist in your browser if you close
the tab and reopen the same file.
────────────────────────────────────────────────────────────────────
``` ```
## Phase 5 — Auto-orchestration opt-in (if research_topics > 0) ## Phase 5 — Auto-orchestration opt-in (if research_topics > 0)

View file

@ -2,7 +2,7 @@
name: trekendsession name: trekendsession
description: Mark the current session as complete and write session-state pointing at the next session. Helper for informal multi-session flows. description: Mark the current session as complete and write session-state pointing at the next session. Helper for informal multi-session flows.
argument-hint: "<next-brief-path> <next-label> | --help" argument-hint: "<next-brief-path> <next-label> | --help"
model: sonnet model: opus
--- ---
# Voyage End-Session Local v1.0 # Voyage End-Session Local v1.0

View file

@ -1519,6 +1519,62 @@ VOYAGE_PROFILE=balanced /trekexecute --project ...
Stats records emit `profile`, `phase_models`, and `profile_source` per Stats records emit `profile`, `phase_models`, and `profile_source` per
Phase 9 record. Phase 9 record.
## Composition rule (v5.1)
Independent of the profile system. When `brief.md` carries
`phase_signals` (brief_version ≥ 2.1), each downstream phase resolves
effort + model as:
```
effort_for_phase = brief.phase_signals[<phase>]?.effort ?? 'standard'
model_for_phase = brief.phase_signals[<phase>]?.model ?? profile.phase_models[<phase>]
```
The brief signal wins per-phase when present; the profile fills any
gaps. Composition is mechanically resolved via
`node ${CLAUDE_PLUGIN_ROOT}/lib/profiles/phase-signal-resolver.mjs`
invoked in Phase 2.4; the resolved JSON is captured as `phase_signal_result`
and consumed when picking the orchestration model + parallel-wave
strategy. The resolver controls only the orchestrator — sub-agents read
`model:` from their own `agents/*.md` frontmatter (still pinned to `opus`).
For `/trekexecute` specifically: `effort == 'low'` activates `--gates open`
+ sequential-only execution (no worktree-isolated parallel waves — runs
all sessions in a single foreground loop). `effort == 'standard'` (or
absent) → no change (default execution strategy applies). `effort == 'high'`
activates the high-effort behavior documented under `### High-effort
behavior (v5.1.1)` below.
### Sequencing gate surface
When `/trekexecute --project <dir>` is invoked, ALWAYS run
`brief-validator.mjs --soft --json` against `{dir}/brief.md`. If
`BRIEF_V51_MISSING_SIGNALS` appears in `errors` (brief_version ≥ 2.1
without `phase_signals` or `phase_signals_partial: true`), halt with:
`Brief is brief_version 2.1 but does not carry phase_signals — re-run
/trekbrief to commit them (Phase 3.5).` Enforcement is validator-only;
commands surface, don't re-enforce.
### High-effort behavior (v5.1.1)
When `phase_signal_result.effort == 'high'` for the `execute` phase,
set `gates_mode = 'closed'` automatically (equivalent to passing
`--gates closed` on the command line). All autonomy boundaries become
operator-stopping points: manifest-audit FAIL halts immediately, the
main-merge gate pauses for operator confirm, and Phase 7.5
manifest-audit runs in its strictest form (fails on ANY mismatch, not
just deltas above threshold). Operator explicit `--gates` flag still
wins over the brief signal — if the operator passes `--gates open` on
the command line while `phase_signal_result.effort == 'high'`, the
flag takes precedence and the override is logged to
`${CLAUDE_PLUGIN_DATA}/trekexecute-stats.jsonl` as
`{event: 'gates_mode_flag_override', flag: 'open', signal_effort: 'high'}`.
Standard effort (or absent): use `gates_mode = 'adaptive'` (the
default). Low effort: `gates_mode = 'open'` plus sequential-only
execution (no parallel waves) — existing `--quick`-equivalent
code-path.
## Hard rules ## Hard rules
1. **No AskUserQuestion for execution decisions.** All execution decisions come 1. **No AskUserQuestion for execution decisions.** All execution decisions come

Some files were not shown because too many files have changed in this diff Show more