Compare commits
29 commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 69610d46bd | |||
| 6c94a1629f | |||
| d8882f5220 | |||
| 9affdca23e | |||
| c1b7bad389 | |||
| 07ae1e30e9 | |||
| 94c696fee6 | |||
| 1bb6a9d63b | |||
| 1f056752c1 | |||
| ce162e6c41 | |||
| 48e092d2bc | |||
| 4c85a2c22b | |||
| a67b5717c9 | |||
| 3ed2d84caa | |||
| 8f4b79cfc6 | |||
| dfe1986f06 | |||
| 6efcc62b68 | |||
| 113296d7de | |||
| 4504c9a8cf | |||
| d3975c441c | |||
| 56fed8f305 | |||
| 0655b57930 | |||
| bf68fe6f5f | |||
| 8cbb33e1fd | |||
| 4b5a3a24dd | |||
| c03695c97b | |||
| 9ba8b682ef | |||
| 8ea692bc60 | |||
| 2e0892cdaf |
|
|
@ -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",
|
||||||
|
|
|
||||||
23
CLAUDE.md
|
|
@ -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)
|
||||||
|
|
|
||||||
24
README.md
|
|
@ -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`.
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
18
plugins/claude-design/.claude-plugin/plugin.json
Normal 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"
|
||||||
|
]
|
||||||
|
}
|
||||||
17
plugins/claude-design/CHANGELOG.md
Normal 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).
|
||||||
60
plugins/claude-design/CLAUDE.md
Normal 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)
|
||||||
22
plugins/claude-design/README.md
Normal 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
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
666
plugins/llm-security/playground/vendor/playground-design-system/components-tier4-project-view.css
vendored
Normal 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);
|
||||||
|
}
|
||||||
|
|
@ -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 på <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 {
|
||||||
|
|
|
||||||
|
|
@ -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; }
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
|
|
||||||
2
plugins/ms-ai-architect/.gitignore
vendored
|
|
@ -1,4 +1,6 @@
|
||||||
*.local.md
|
*.local.md
|
||||||
|
*.local.html
|
||||||
|
*.local.json
|
||||||
.mcp.json
|
.mcp.json
|
||||||
.DS_Store
|
.DS_Store
|
||||||
.claude/
|
.claude/
|
||||||
|
|
|
||||||
|
|
@ -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"` på `.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)
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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)*
|
||||||
|
|
||||||

|

|
||||||

|

|
||||||

|

|
||||||

|

|
||||||
|
|
@ -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. |
|
||||||
|
|
|
||||||
|
After Width: | Height: | Size: 718 KiB |
|
After Width: | Height: | Size: 245 KiB |
|
After Width: | Height: | Size: 620 KiB |
|
After Width: | Height: | Size: 616 KiB |
|
After Width: | Height: | Size: 879 KiB |
|
After Width: | Height: | Size: 872 KiB |
|
After Width: | Height: | Size: 960 KiB |
|
After Width: | Height: | Size: 954 KiB |
|
After Width: | Height: | Size: 863 KiB |
|
After Width: | Height: | Size: 857 KiB |
|
After Width: | Height: | Size: 744 KiB |
|
After Width: | Height: | Size: 739 KiB |
|
After Width: | Height: | Size: 816 KiB |
|
After Width: | Height: | Size: 809 KiB |
|
After Width: | Height: | Size: 265 KiB |
|
After Width: | Height: | Size: 262 KiB |
|
After Width: | Height: | Size: 594 KiB |
|
After Width: | Height: | Size: 588 KiB |
|
After Width: | Height: | Size: 216 KiB |
|
After Width: | Height: | Size: 214 KiB |
|
After Width: | Height: | Size: 415 KiB |
|
After Width: | Height: | Size: 412 KiB |
|
After Width: | Height: | Size: 247 KiB |
|
After Width: | Height: | Size: 245 KiB |
|
After Width: | Height: | Size: 740 KiB |
|
After Width: | Height: | Size: 735 KiB |
|
After Width: | Height: | Size: 475 KiB |
|
After Width: | Height: | Size: 473 KiB |
|
After Width: | Height: | Size: 668 KiB |
|
After Width: | Height: | Size: 665 KiB |
|
After Width: | Height: | Size: 742 KiB |
|
After Width: | Height: | Size: 740 KiB |
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
666
plugins/ms-ai-architect/playground/vendor/playground-design-system/components-tier4-project-view.css
vendored
Normal 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);
|
||||||
|
}
|
||||||
|
|
@ -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; }
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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) => {
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
248
plugins/ms-ai-architect/tests/test-playground-actions.sh
Executable 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
|
||||||
218
plugins/ms-ai-architect/tests/test-playground-fingerprints.sh
Executable 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
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
323
plugins/ms-ai-architect/tests/test-playground-projectview.sh
Executable 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, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
||||||
|
}
|
||||||
|
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 & 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
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -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 1431–2255 (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
|
||||||
|
|
|
||||||
|
|
@ -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 1431–2255). 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, 0–N research briefs, 0 or 1 architecture note, 0–N reviews (one per review iteration), and 0 or 1 session-state file (overwritten on every session-end).
|
A project typically has 1 task brief, 0–N research briefs, 0 or 1 architecture note, 0–N 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)
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
# trekplan — Brief, Research, Plan, Execute, Review, Continue
|
# trekplan — Brief, Research, Plan, Execute, Review, Continue
|
||||||
|
|
||||||

|

|
||||||

|

|
||||||

|

|
||||||
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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"]
|
||||||
---
|
---
|
||||||
|
|
|
||||||
|
|
@ -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"]
|
||||||
---
|
---
|
||||||
|
|
|
||||||
|
|
@ -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"]
|
||||||
---
|
---
|
||||||
|
|
|
||||||
|
|
@ -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"]
|
||||||
---
|
---
|
||||||
|
|
|
||||||
|
|
@ -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"]
|
||||||
---
|
---
|
||||||
|
|
|
||||||
|
|
@ -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"]
|
||||||
---
|
---
|
||||||
|
|
|
||||||
|
|
@ -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"]
|
||||||
---
|
---
|
||||||
|
|
|
||||||
|
|
@ -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"]
|
||||||
---
|
---
|
||||||
|
|
|
||||||
|
|
@ -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"]
|
||||||
---
|
---
|
||||||
|
|
|
||||||
|
|
@ -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"]
|
||||||
---
|
---
|
||||||
|
|
|
||||||
|
|
@ -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"]
|
||||||
---
|
---
|
||||||
|
|
|
||||||
|
|
@ -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"]
|
||||||
---
|
---
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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"]
|
||||||
---
|
---
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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"]
|
||||||
---
|
---
|
||||||
|
|
|
||||||
|
|
@ -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"]
|
||||||
---
|
---
|
||||||
|
|
|
||||||
|
|
@ -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"]
|
||||||
---
|
---
|
||||||
|
|
|
||||||
|
|
@ -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"]
|
||||||
---
|
---
|
||||||
|
|
|
||||||
|
|
@ -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"]
|
||||||
---
|
---
|
||||||
|
|
|
||||||
|
|
@ -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"]
|
||||||
---
|
---
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||