You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Codev's natural-language artifacts — specs, plans, reviews — need an interactive rendering and review surface. Today the only place architects can add review comments to these files is the VSCode source editor (line gutter +, shipped in #857). Two structural gaps follow:
vscode: add review comments from the markdown preview pane (hover-+ per block, no editor mode-switch) #859 discovered that VSCode's built-in markdown preview can't host the required interactions. The platform's previewScripts / markdownItPlugins / previewStyles contribution points are render-only with no back-channel messaging to the extension host. Comment-from-preview can't be added by extending the built-in preview; it requires owning the preview surface (CustomTextEditorProvider + own webview).
The dashboard has no spec/plan/review surface at all. Today's dashboard shows builders, PRs, and backlog items but offers no reading or review-comment affordance for the underlying artifacts. Architects working away from VSCode (meetings, different machines, eventually mobile) currently have no review path.
Both gaps share a root cause: there is no reusable layer for rendering Codev artifacts + adding interactive overlays. Building it once per surface (VSCode webview, dashboard route, future mobile wrapper) would mean three implementations to maintain and three places where the UX could diverge. Building it once as a shared package and adapting per surface is the only path that scales as the family of review-surface features (#858–#863) grows.
Proposal
A new package @cluesmith/codev-artifact-canvas at packages/artifact-canvas/ providing:
A markdown renderer with source-position metadata (markdown-it + data-line source mapping rule)
Interactive overlays for comment authoring (hover-+), navigation (TOC, marker minimap), and state surfaces (reading progress, AC progress, frontmatter badges)
Adapter interfaces for all I/O (file read/write, marker mutation, theme tokens) — implementations live in the host (VSCode extension, dashboard React route, future mobile)
React-based components for parity with the existing dashboard codebase; embeddable in VSCode webviews and future Capacitor/Tauri wrappers without re-architecture
Why "canvas"
The package is an interactive surface for acting on artifacts (annotate, navigate, mark progress) — not a passive viewer/reader. "Canvas" connotes the bidirectional read+act use case that the package exists to enable. Later phases use an actual HTML <canvas> element for the marker minimap (perf — same primitive VSCode's own editor minimap uses) and potentially for region-selection lassos that produce text-addressable line-range anchors. The name is accurate at v1 (metaphor) and becomes literal at v2 (#863's minimap).
Architectural shape
packages/artifact-canvas/ NEW
├── src/
│ ├── renderer/ markdown-it + data-line source mapping
│ ├── overlays/ hover-`+`, floating TOC, marker minimap
│ ├── widgets/ reading-progress, AC-progress, frontmatter badges
│ ├── panels/ review-summary list component (consumed as panel by hosts)
│ ├── adapters/ INTERFACES ONLY — no implementations
│ │ ├── FileAdapter.ts read document content, watch changes
│ │ ├── MarkerAdapter.ts read markers, write new markers, attribution
│ │ └── ThemeAdapter.ts resolve theme tokens
│ ├── components/ React components composing the above
│ └── index.ts public API surface
├── package.json
└── README.md
packages/vscode/ EXISTS — host
├── src/
│ └── artifact-canvas-host.ts NEW (in pir-859 re-plan after this lands):
│ CustomTextEditorProvider wrapping the package
│ - FileAdapter against vscode.workspace.fs
│ - MarkerAdapter against existing plan-review.ts logic
│ - ThemeAdapter against `--vscode-*` CSS variables
└── ...
packages/dashboard/ EXISTS — host (separate future issue)
├── src/
│ └── routes/artifact-canvas/ NEW (separate follow-up):
│ React route embedding the package
│ - FileAdapter against Tower REST endpoints
│ - MarkerAdapter against Tower mutation endpoint
│ - ThemeAdapter against dashboard design tokens
└── ...
Every interactive affordance the package surfaces must serialize its output to structured text in the source markdown (or clearly-delimited adjacent text files). This invariant exists because every annotation has two audiences who need to act on it precisely:
Team members reading the same file later (during review, after a context switch)
Claude as builder when spawned to address the feedback (annotations are typically requests for source changes)
Both audiences need text they can read deterministically. Freehand drawings, voice notes, image overlays — any output that requires interpretation rather than precise read — are out of scope for the package's annotation surfaces.
Concrete consequences:
Comment overlays serialize to text markers in the source markdown. The package is serialization-agnostic — hosts own the on-disk format via the MarkerAdapter. The existing vscode host convention (vscode: spec/plan review comments — polish pass (placeholders, reviews/ coverage, author identity, panel discoverability) #857) writes <!-- REVIEW(@author): text --> immediately after the targeted block, with the anchor line implicit from the marker's source position; this stays untouched. Future hosts (dashboard, mobile) may choose a different on-disk shape as long as the MarkerAdapter.list/add contract holds.
Region-anchored comments (if added later) carry {lineStart, lineEnd} in the in-memory ReviewMarker shape; on-disk serialization is again the host's call (could be a new comment form like <!-- REVIEW(@author, lines=N-M): text -->, could be a side-file entry — package doesn't dictate).
AC progress checkboxes mutate - [ ] ↔ - [x] in source
Canvas rendering choices (e.g. minimap, future lasso) are rendering and input primitives — the underlying data they read from and write to remains text
This filter applies to every dependent issue. The acceptance criteria for the package include a test asserting that no affordance produces output that isn't either (a) source-markdown text mutation, or (b) a clearly-delimited text artifact alongside the source.
Adapter contracts (skeleton — subject to plan-approval refinement)
interfaceFileAdapter{read(uri: string): Promise<string>;watch(uri: string,onChange: (content: string)=>void): Disposable;}interfaceMarkerAdapter{list(uri: string): Promise<ReviewMarker[]>;add(uri: string,line: number,text: string,author: string): Promise<void>;// Future: addRegion(uri, lineStart, lineEnd, text, author)// Future: setCheckbox(uri, line, checked) for AC progress}interfaceThemeAdapter{resolve(token: string): string;// e.g. resolve("background") → host-specific CSS variable or coloronChange(handler: ()=>void): Disposable;}interfaceReviewMarker{author: string;line: number;// Future: lineRange?: { start: number; end: number };text: string;raw: string;// host-provided opaque round-trip data (e.g. the original `<!-- REVIEW(...) -->` text for the vscode host; could be any other shape for other hosts) — the package treats this as a black box and hands it back unchanged on update/delete}
Build the package per the phase plan; no host integration yet (pir-859 re-plan does the VSCode adapter; separate future issue does the dashboard adapter)
review
Unit tests for renderer (data-line attribution), overlay (hover state, click → adapter.add), adapter interface compliance
verify
Confirm via a smoke-test host (could be a minimal VSCode webview or a Vite dev server route) that the package renders + accepts comments end-to-end
Why React
The dashboard is already React. VSCode webviews handle React fine (the dashboard ships as a React app served by Tower; the same bundle approach works inside a webview). Future mobile via Capacitor/Tauri uses React-compatible techniques. Framework-agnostic Web Components were considered but rejected — the dashboard React investment makes React-native components dramatically cheaper to embed in the dashboard route.
Rendering layer additions within the package; this is where the actual <canvas> first appears (marker minimap for perf)
Dashboard artifact-canvas route (NEW — separate future issue)
Becomes possible — same package, dashboard-side adapter
Future mobile artifact review
Becomes possible — same package, mobile-side adapter
Out of scope
VSCode host integration — handled by pir-859's re-plan after this lands. This issue ships the package; pir-859 ships the comment-from-preview feature consuming the package.
Dashboard host integration — separate future issue. Designed for from day 1 (adapter contracts make it cheap) but not implemented here.
Mobile host integration — designed for, not implemented.
Freehand sketch annotation — explicitly rejected by the text-as-source-of-truth invariant. Sketches can't be read precisely by team members OR by Claude-as-builder, so they fail the package's annotation contract.
Region-lasso anchoring (v3 candidate) — viable in principle (lasso produces text line ranges); not in scope for the package's v1.
Custom heading numbering, math rendering (KaTeX), Mermaid diagrams, code syntax highlighting — markdown-it core only for v1. Each is a follow-up if/when needed.
Why SPIR (not PIR, not AIR)
Package boundary is one-shot — get the adapter interfaces wrong and 6+ dependent issues need rework. The spec phase exists exactly to lock structural decisions before any code commits to them.
Cross-package blast radius — affects the codev monorepo's package layout, the dashboard's eventual consumption story, pir-859's re-plan dependency. PIR's plan-approval gate doesn't surface enough; SPIR's spec-approval makes the package boundary a deliberate contract.
Real spec decisions beyond what fits in a plan: package vs sub-packages within a folder; adapter sync vs async semantics; whether ThemeAdapter is push-based or pull-based; serialization format for region-anchored markers (forward compat); whether to ship a CSS-modules-based theme system or a pure CSS-variable approach.
PIR or AIR would force these decisions into plan-approval bundled with implementation tradeoffs, which is exactly the situation SPIR is designed to avoid.
Acceptance criteria
packages/artifact-canvas/ exists with package.json declaring @cluesmith/codev-artifact-canvas, peer dep on react/react-dom, dep on markdown-it.
Renderer produces HTML with data-line attributes on block tokens (paragraphs, headings, list items, code blocks); unit test covers attribution.
Comment overlay component renders a hover-+ affordance on rendered blocks; clicking it invokes a callback receiving {line: number}; the callback's text-input flow + write-back lives in the host adapter (not the package).
Three adapter interfaces (FileAdapter, MarkerAdapter, ThemeAdapter) are exported; the package has zero direct file-system, fetch, or VSCode API imports.
Theming via CSS custom properties; package supplies a default-theme stylesheet that maps to --codev-canvas-* variables; hosts override by providing host-specific values (e.g. --codev-canvas-foreground: var(--vscode-foreground) in VSCode).
A smoke-test host (Vite dev server or minimal VSCode webview, implementer's choice) demonstrates end-to-end: load a sample markdown, render, hover, click +, adapter receives the call, marker round-trips.
Build produces a CJS+ESM bundle suitable for both VSCode webview consumption (CJS or ESM) and dashboard Vite consumption (ESM).
No affordance in the package produces output that isn't either source-markdown text mutation OR a clearly-delimited text artifact alongside the source (text-as-source-of-truth invariant test).
README documents the adapter contracts + a host-implementation example.
Blocks: pir-859 (currently on HOLD); will be released to re-plan against this once the package ships.
Blocked by: nothing.
Coordinates with: existing packages/codev-types, packages/codev-core conventions for monorepo package shape.
Estimated scope
Package skeleton + markdown-it + data-line + hover-+ + adapter contracts + smoke-test host: ~600-1000 LOC of new package code, ~100-200 LOC of smoke-test scaffolding, plus tests.
Larger than the typical PIR but appropriate for SPIR.
Problem
Codev's natural-language artifacts — specs, plans, reviews — need an interactive rendering and review surface. Today the only place architects can add review comments to these files is the VSCode source editor (line gutter
+, shipped in #857). Two structural gaps follow:vscode: add review comments from the markdown preview pane (hover-+ per block, no editor mode-switch) #859 discovered that VSCode's built-in markdown preview can't host the required interactions. The platform's
previewScripts/markdownItPlugins/previewStylescontribution points are render-only with no back-channel messaging to the extension host. Comment-from-preview can't be added by extending the built-in preview; it requires owning the preview surface (CustomTextEditorProvider+ own webview).The dashboard has no spec/plan/review surface at all. Today's dashboard shows builders, PRs, and backlog items but offers no reading or review-comment affordance for the underlying artifacts. Architects working away from VSCode (meetings, different machines, eventually mobile) currently have no review path.
Both gaps share a root cause: there is no reusable layer for rendering Codev artifacts + adding interactive overlays. Building it once per surface (VSCode webview, dashboard route, future mobile wrapper) would mean three implementations to maintain and three places where the UX could diverge. Building it once as a shared package and adapting per surface is the only path that scales as the family of review-surface features (#858–#863) grows.
Proposal
A new package
@cluesmith/codev-artifact-canvasatpackages/artifact-canvas/providing:markdown-it+data-linesource mapping rule)+), navigation (TOC, marker minimap), and state surfaces (reading progress, AC progress, frontmatter badges)Why "canvas"
The package is an interactive surface for acting on artifacts (annotate, navigate, mark progress) — not a passive viewer/reader. "Canvas" connotes the bidirectional read+act use case that the package exists to enable. Later phases use an actual HTML
<canvas>element for the marker minimap (perf — same primitive VSCode's own editor minimap uses) and potentially for region-selection lassos that produce text-addressable line-range anchors. The name is accurate at v1 (metaphor) and becomes literal at v2 (#863's minimap).Architectural shape
Text-as-source-of-truth invariant (architectural guardrail)
Every interactive affordance the package surfaces must serialize its output to structured text in the source markdown (or clearly-delimited adjacent text files). This invariant exists because every annotation has two audiences who need to act on it precisely:
Both audiences need text they can read deterministically. Freehand drawings, voice notes, image overlays — any output that requires interpretation rather than precise read — are out of scope for the package's annotation surfaces.
Concrete consequences:
MarkerAdapter. The existing vscode host convention (vscode: spec/plan review comments — polish pass (placeholders, reviews/ coverage, author identity, panel discoverability) #857) writes<!-- REVIEW(@author): text -->immediately after the targeted block, with the anchor line implicit from the marker's source position; this stays untouched. Future hosts (dashboard, mobile) may choose a different on-disk shape as long as theMarkerAdapter.list/addcontract holds.{lineStart, lineEnd}in the in-memoryReviewMarkershape; on-disk serialization is again the host's call (could be a new comment form like<!-- REVIEW(@author, lines=N-M): text -->, could be a side-file entry — package doesn't dictate).- [ ]↔- [x]in sourceThis filter applies to every dependent issue. The acceptance criteria for the package include a test asserting that no affordance produces output that isn't either (a) source-markdown text mutation, or (b) a clearly-delimited text artifact alongside the source.
Adapter contracts (skeleton — subject to plan-approval refinement)
Phase decomposition (proposed for SPIR plan)
+overlay + InputBox-equivalent (host-driven via adapter), (3) MarkerAdapter wire-up + REVIEW marker writingWhy React
The dashboard is already React. VSCode webviews handle React fine (the dashboard ships as a React app served by Tower; the same bundle approach works inside a webview). Future mobile via Capacitor/Tauri uses React-compatible techniques. Framework-agnostic Web Components were considered but rejected — the dashboard React investment makes React-native components dramatically cheaper to embed in the dashboard route.
What this unlocks
<canvas>first appears (marker minimap for perf)Out of scope
vscode.diffcommand in vscode; can use a separate library in dashboard. Stays out of the package; reimplementing diff inside the package adds maintenance without benefit.Why SPIR (not PIR, not AIR)
PIR or AIR would force these decisions into plan-approval bundled with implementation tradeoffs, which is exactly the situation SPIR is designed to avoid.
Acceptance criteria
packages/artifact-canvas/exists with package.json declaring@cluesmith/codev-artifact-canvas, peer dep on react/react-dom, dep on markdown-it.data-lineattributes on block tokens (paragraphs, headings, list items, code blocks); unit test covers attribution.+affordance on rendered blocks; clicking it invokes a callback receiving{line: number}; the callback's text-input flow + write-back lives in the host adapter (not the package).--codev-canvas-*variables; hosts override by providing host-specific values (e.g.--codev-canvas-foreground: var(--vscode-foreground)in VSCode).+, adapter receives the call, marker round-trips.Dependencies
packages/codev-types,packages/codev-coreconventions for monorepo package shape.Estimated scope
++ adapter contracts + smoke-test host: ~600-1000 LOC of new package code, ~100-200 LOC of smoke-test scaffolding, plus tests.