Spec Visualization System — Build Plan
Context for fresh Claude: Shay wanted a dedicated, deep rework of how specs are visualized. Plain markdown wasn’t carrying the complexity in the real specs (phase roadmaps, SQL schemas, surface maps, action ladders, flow diagrams). This plan scopes the work. Do it after compact with full force.
Goal
Specs stay portable markdown (works in any editor, readable in GitHub, no MDX lock-in) — but gain ~14 first-class visualization components that activate when a spec uses the viz:<type> fenced-block convention.
The reader of /specs/artifact-platform shouldn’t see “a flat table of phases” — they should see a horizontal timeline with gates colored by status, useful-to-audience chips, and expand-to-see tasks under each. Same content, richer surface.
Rendering architecture
Syntax (in .md files)
Regular markdown paragraph.
```viz:phase-timeline
phases:
- id: 0
name: Stoka v1
useful_to: Visitors
status: shipped
- id: 1
name: Solo core
useful_to: You alone, as daily-use tool
status: active
gate_condition: Phase 0
Another regular paragraph. [[spec:messaging]] renders as a spec-chip inline.
The `viz:<type>` fenced block is parsed as YAML. The `[[spec:...]]`, `@handle`, and `{kind:...}` inline patterns get post-processed into chips.
### Pipeline
spec.md ↓ parseFrontmatter() → { data, body } ↓ splitIntoSegments(body) → [ { type: ’md’, content }, { type: ’viz’, vizType, props }, … ] ↓ for each segment: type=md → marked.parse() type=viz → route to via type registry ↓ Astro renders a stream of either
### Files to create
src/lib/specs/ ├─ index.ts ← loadSpecs() (exists; move here) ├─ segment-parser.ts ← NEW: splits body into md/viz segments ├─ viz-registry.ts ← NEW: maps ’viz:phase-timeline’ → PhaseTimeline component ├─ inline-processor.ts ← NEW: turns [[spec:x]] / @handle / {kind:x} into chip HTML └─ types.ts ← NEW: SpecSegment, VizBlock, etc.
src/components/specs/ ├─ PhaseTimeline.astro ← phase roadmap, horizontal, with gates ├─ SchemaERD.astro ← SQL schema entity-relationship diagram ├─ SurfaceMap.astro ← surface-card grid (Stoka Across Surfaces) ├─ ActionLadder.astro ← vertical escalation ladder (mod actions) ├─ LayerStack.astro ← stacked layers (Bon Appétit / prompt architecture) ├─ ApiRoutes.astro ← HTTP-colored route cards ├─ DecisionMatrix.astro ← comparison grid (visibility, channels×events) ├─ FlowDiagram.astro ← step-by-step flows (Telegram pairing, share cascade) ├─ Ruleset.astro ← rule-cards (mod triggers + action rungs) ├─ KindCards.astro ← kind cards (artifact kinds, inbox item kinds) ├─ Exchange.astro ← terminal-styled conversation mockup (Stoka golden examples) ├─ Callout.astro ← TBD / warning / note / info semantic blocks ├─ SpecChip.astro ← inline [[spec:slug]] rendering ├─ PseudonymChip.astro ← inline @handle rendering └─ KindChip.astro ← inline {kind:conversation} rendering
src/pages/specs/[slug].astro ← UPDATE: use segment renderer, not marked.parse() src/pages/admin/specs.astro ← UPDATE: same, with dark-theme variants of every component
Each viz component ships **two theme variants**: public (light-prose-friendly) and admin (dark-prose). Use `data-theme` attribute or CSS custom properties + a wrapper class.
## Component catalog (detailed)
### 1. `viz:phase-timeline`
Horizontal timeline. Each phase = a node; gates between phases shown as arrows with gate-condition text. Status colors: shipped (green-solid) · active (green-glowing) · queued (gray) · blocked (amber) · deferred (dimmed).
Data shape:
```yaml
phases:
- id: 1
name: Solo core
useful_to: You alone, as daily-use tool
status: queued
gate_condition: Phase 0 shipped
milestones: [optional list, shown in hover/expand]
2. viz:schema-erd
ERD for SQL schemas. Shows tables as boxes with column lists, relationships as connecting lines. Collapsible column details (click table → expand). Index/unique markers in-line. Kind-specific metadata JSON shown as sub-card.
Data shape:
tables:
- name: identity.artifacts
columns:
- name: id
type: UUID
pk: true
- name: pseudonym_id
type: UUID
fk: identity.pseudonyms.id
- name: visibility
type: TEXT
default: private
enum: [private, link_only, public_anonymous]
indexes:
- { on: [pseudonym_id, status] }
relationships:
- from: identity.artifacts.pseudonym_id
to: identity.pseudonyms.id
label: author
3. viz:surface-map
Card grid — one card per surface. Each card has: surface name (title), Layer 3 content source, Layer 4 context, “what Stoka does” summary. Visual tie to the “one brain many masks” concept via a shared brain-icon in the center of the grid + radial connections.
Data shape:
brain: Stoka (fixed identity + voice)
surfaces:
- name: Discovery terminal
layer_3: RAG chunks from published content
layer_4: page, history, interests
role: recommends content
- ...
4. viz:action-ladder
Vertical escalation ladder. Each rung numbered, with action name, description, reversibility indicator, admin-visibility level. Shown as actual ladder visual (rungs with increasing severity).
Data shape:
target: pseudonym | user
rungs:
- number: 1
name: Silent warn
description: Pseudonym sees notice
reversibility: automatic
- ...
5. viz:layer-stack
Stacked layers (Bon Appétit artifact granularity, prompt architecture Layers 0-5). Each layer has a label, description, visibility/depth marker. Click to expand showing layer-specific details. Bon Appétit version: “reader appetite” scrollbar on the right showing reader depth.
Data shape:
title: Bon Appétit granularity ladder
layers:
- name: Recipe card
subtitle: what you scan
description: headline · prompt · why it works · variations · tags
- name: Method notes
subtitle: expand once
description: what was tried · what failed · the moment it clicked
- ...
6. viz:api-routes
Route table/cards. HTTP method color badges (GET=blue, POST=green, PATCH=amber, DELETE=red). Auth/role badge per route. Description + example request/response (collapsible).
Data shape:
base: /api/dm
routes:
- method: POST
path: /threads
auth: user
purpose: start/resume by recipient handle
body: { recipient_handle: string, note?: string }
- ...
7. viz:decision-matrix
Comparison grid — rows = options, columns = properties. Each cell can be: boolean (✓/—), enum (colored pill), scalar (text), or link (to spec). Highlight the “recommended” row. Used for visibility tiers, notification channels-vs-events, model decisions.
Data shape:
rows: [Private, Link-only, Public anonymous]
columns: [Who sees it, Directory?, Stoka index?, Share-to-DM?]
cells:
Private: ["Only author", "No", "Personal only", "Yes, grant"]
...
highlight: Private # the default
8. viz:flow-diagram
Numbered step flow with branches. Each step is a node; arrows show progression. Branches (if/else) rendered as forks. Annotations along arrows (e.g., “if artifact is private: grant access”). Can be vertical or horizontal.
Data shape:
orientation: vertical
steps:
- id: 1
actor: User
action: Click share button
- id: 2
actor: Client
action: Opens recipient picker
- id: 3
actor: Client
action: POST /api/artifacts/:id/share
branches:
- condition: thread exists
next: 4a
- condition: thread does not exist
next: 4b
- ...
9. viz:ruleset
Card-per-rule for moderation or any rule-based system. Each card: trigger condition (with detection snippet), action ladder rung(s) it typically escalates to, threshold visibility (public/private).
Data shape:
rules:
- trigger: Report threshold
detection: "N user reports on a pseudonym within window"
rungs: [2, 3]
threshold_visibility: private
- ...
10. viz:kind-cards
Grid of cards representing taxonomic kinds (artifact kinds, inbox item kinds). Each card: kind icon, kind name, description, kind-specific metadata schema preview.
Data shape:
kinds:
- id: conversation
icon: chat-square # icon ID from a curated set
name: Conversation
description: Bon Appétit conversation extract — recipe + method + source + transcript
metadata_schema:
- { key: recipe, type: text }
- { key: why_it_works, type: text }
- { key: variations, type: array }
- ...
11. viz:exchange
Terminal-styled conversation mockup (for Stoka golden examples, Konan chat samples, DM previews). Monospace, speaker labels colored, cite blocks rendered inline.
Data shape:
speakers:
visitor: { color: muted, prefix: "visitor" }
stoka: { color: brand, prefix: "stoka" }
turns:
- speaker: visitor
text: "what is this?"
- speaker: stoka
text: "a terminal. i know everything published here. ask something or don't."
- speaker: visitor
text: "tell me about Debono"
- speaker: stoka
text: "it transcribes pharmacy lecture slides.\nthe interesting part is how it fails."
cite:
slug: debono-error-handling
quote: "The deck pipeline doesn't retry. It remembers what broke and why."
12. viz:callout
Semantic callouts with icon + color. Variants: tbd (yellow question-mark), warning (red triangle), note (blue circle), rule (emerald lock — for “design rule”), deferred (gray arrow-right).
Data shape (or use a shorter syntax):
variant: rule
title: The Unencumbered Bar
body: |
Every feature ships against this bar or doesn't ship.
Optimistic UI, no modals, keyboard-first, instant pseudonym switching.
13-15. Inline chips (processed in markdown, not viz blocks)
[[spec:messaging]]→ styled chip linking to/specs/messaging@ghost-7c0a→ pseudonym chip (gets hover-card in Phase 2+){kind:conversation}→ kind chip with icon{phase:1}→ phase chip with status color
Component visual language (shared)
- Color tokens: brand green
#10b981for primary accents, amber#fbbf24for warnings/TBDs, red for critical, muted surface grays for chrome. - Typography: Inter for body, JetBrains Mono for data (table content, schema types, handles, kinds). Rule: if it’s data, it’s mono.
- Iconography: Curated SVG set (Lucide-style). No emoji. Icons consistent across components.
- Border convention: 1px solid brand-dimmed for active, 1px dashed brand-xdim for placeholder/stub, 1px solid surface-border for neutral.
- Shadow convention: Soft elevation for cards, brand-glow only for active/selected states.
- Animation: Micro-transitions (120-180ms) on hover/select. No scroll animations, no parallax. Subtle.
Implementation phases
Phase A — Foundation (post-compact, first pass)
src/lib/specs/segment-parser.ts— splits spec body on\n```viz:<type>\n...\n```\nsrc/lib/specs/viz-registry.ts— type → component mapsrc/lib/specs/inline-processor.ts— chip transforms on markdown segments- Update
src/pages/specs/[slug].astroto use segment renderer - Update
src/pages/admin/specs.astroto use same (with dark variants) - Build 5 core components:
Callout,SpecChip,KindChip,PseudonymChip,PhaseTimeline(the highest-impact visuals first)
Phase B — The meat (post-compact, second pass)
SchemaERD,SurfaceMap,LayerStack,ActionLadder,FlowDiagram
Phase C — Final wave
ApiRoutes,DecisionMatrix,Ruleset,KindCards,Exchange
Phase D — Spec rewrites
- Rewrite
README.md+artifact-platform.md+messaging.mdto use viz blocks where they replace markdown tables / ASCII / SQL blocks. - Spot-adopt in
stoka-bot.mdwhere high-impact (Surface Map, Layer Stack, Exchange for golden examples).
Target specs for first visualization pass
In priority order (what will demo best):
artifact-platform.md— has the Bon Appétit layer stack (perfect LayerStack demo), visibility tiers (DecisionMatrix), full schema (SchemaERD), phase plan (PhaseTimeline), kinds list (KindCards)messaging.md— Telegram pairing flow (FlowDiagram), mod action ladder (ActionLadder), notification matrix (DecisionMatrix), surface map extension, mod triggers (Ruleset)stoka-bot.md— Surface Map section (SurfaceMap), prompt architecture Layers (LayerStack), golden examples (Exchange)
Admin surface specific
The admin /admin/specs page should let the author edit viz blocks with a structured editor (not raw YAML). Phase E (post all the above):
- Each viz block gets an “edit in structured mode” button
- Form renderer per viz type (drag-and-drop phases on PhaseTimeline, table builder for DecisionMatrix, etc.)
- Saves back as YAML into the viz block, preserving markdown elsewhere
This is Phase E because it’s a big editor lift and the viewing experience is the immediate win.
What NOT to do
- Don’t move specs into MDX. Portability is load-bearing — specs should read cleanly in GitHub, any .md previewer, any editor. Viz blocks degrade gracefully to YAML code blocks in dumb renderers.
- Don’t use a heavy chart library. Custom SVG + CSS for every component. Consistency of feel > speed to build.
- Don’t make it React. Stick to Astro components + vanilla JS for interactivity. The site doesn’t need a framework.
- Don’t retrofit every existing spec. New specs use new viz. Old specs get retrofitted opportunistically.
- Don’t over-animate. Micro-transitions only. Specs are read, not experienced.
Open questions
- Mermaid support? Mermaid could cover several viz types (flow diagram, ERD, timeline) with one library. But it’s opinionated and doesn’t match the site’s visual language. Leaning: custom components, not Mermaid. Revisit if component-count feels oppressive.
- Editor mode for public specs? Probably not — specs are authored, not user-generated. Admin-side editor is enough.
- Printable / PDF export? Nice-to-have for Phase 3+. Spec components need to degrade cleanly to static for print media query.
- Offline rendering? The
/specsbuild is static. No JS should be required for first paint — all viz components SSR to HTML. Interactivity (hover details, click-expand) is progressive enhancement.
Success criteria
- Reading
artifact-platform.mdon/specs/artifact-platformfeels like reading a well-designed technical book, not browsing a markdown file. - Shay can write new specs using viz blocks naturally, without thinking about rendering.
- Components are genuinely reusable — a schema change in
messaging.mdis a one-line YAML edit, not a rewrite. - Dark admin variant looks as polished as public light variant. Not a hack; a parallel theme.
When to attack
After compact. Full force. 14 components is a real chunk of work — don’t fragment across sessions if avoidable.