Skip to content

feat(cmd/crucible): machine visualizer — scoped, granular, forge-themed render (D2)#186

Merged
joshua-temple merged 2 commits into
mainfrom
feat/machine-visualizer
Jun 18, 2026
Merged

feat(cmd/crucible): machine visualizer — scoped, granular, forge-themed render (D2)#186
joshua-temple merged 2 commits into
mainfrom
feat/machine-visualizer

Conversation

@joshua-temple

Copy link
Copy Markdown
Collaborator

Turns crucible render into a machine visualizer: project the IR through a view-model at a chosen scope and detail granularity, and render it as a polished, themed SVG via the embedded D2 engine (pure Go, no Chromium, no external Graphviz).

Why (pivot from go-graphviz)

The go-graphviz/DOT path (#185) hit a composition ceiling — sprawling layout, no clean containers, weak shape vocabulary. After research + spikes, D2 proved meaningfully better and embeds in-process in pure Go like go-graphviz did. This PR pivots the SVG backend to D2 and builds the visualizer on top.

What

Pipeline: IR → query (path/scope) → view-model projection (detail) → D2 emit → in-process SVG.

  • Scope: -from/-to (path A→X), -from only (reachable-from-A), or whole machine.
  • Path mode: -mode shortest|all|trace.
  • Detail granularity: -detail outline|guards|actions|lifecycle|full (default actions) — a cumulative ladder — refined by repeatable -show/-hide <dimension>. Lifecycle detail (entry/exit/invoke) renders as in-node compartments.
  • Distinct shapes per kind: initial dot · atomic 3D steel box · invoke hexagon · history circle · final double-ring · composite/parallel containers. The active path is highlighted in ember over cold-steel context.
  • Theming: -theme file.json overlays the embedded forge default palette.
  • New internal packages: viewmodel, query, render.

Backend swap: removes the go-graphviz dependency (and its bundled Graphviz). dot/mermaid text output is unchanged. -format png now errors with guidance to render -format svg and convert with resvg/rsvg-convert (SVG is the scalable primary).

Verification

  • go build / vet / test (4 packages) / golangci-lint — all green, including GOWORK=off (standalone consumer build holds after the dep swap).
  • Golden tests on the emitted D2 source (deterministic) across detail/scope/path; SVG asserted structurally (it isn't byte-deterministic) incl. a no-mauve / forge-color check.
  • End-to-end against the built binary: whole + path-scope SVG render, every detail level, dot/mermaid regression, png→usage error, unknown-endpoint→usage error.

Notes / honesty

Design + research artifacts live under ~/llm/plans/crucible/ (not committed, per repo convention).

…ed render

Project the IR through a view-model at a chosen scope and detail granularity and
render it as a themed SVG via the embedded D2 engine (pure Go, no Chromium, no
external Graphviz).

- Scope: -from/-to (path A→X), -from (reachable-from-A), or whole machine;
  -mode shortest|all|trace selects the path view.
- Detail: -detail outline|guards|actions|lifecycle|full (default actions), a
  cumulative ladder refined by repeatable -show/-hide <dimension>. Lifecycle
  detail (entry/exit/invoke) renders as in-node compartments.
- Distinct shapes per kind (initial dot, atomic 3D steel box, invoke hexagon,
  history circle, final double-ring, composite/parallel containers); the active
  path is highlighted in ember over cold-steel context.
- Theme: -theme file.json overlays the embedded "forge" default palette.
- New internal packages: viewmodel (projection), query (path/scope BFS over the
  IR), render (D2 emit + in-process SVG + post-process).

Replaces the previous WebAssembly Graphviz svg/png backend with D2 and removes
that dependency. `render -format png` now errors with guidance to render
-format svg and convert with resvg/rsvg-convert; dot/mermaid output is unchanged.
…ed render)

An edge whose label starts with `[` (e.g. an eventless transition carrying only a
guard, rendered as `[hasStock]`) made D2 parse the value as an array and fail to
compile ("edges cannot be assigned arrays"), so RenderSVG errored. The emitter's
quote() used a too-narrow blacklist that left such labels bare.

Replace it with a whitelist — bare only for ^[A-Za-z_][A-Za-z0-9_]*$, everything
else double-quoted (escaping backslash then quote). quote() is the single helper
used at every label/name/title/compartment site, so this fixes the whole
special-char class. Adds emit + end-to-end RenderSVG regression tests; existing
goldens unchanged (a new guard_only_edge golden covers the quoted forms).

Found by the granularity showcase (eventless+guard transition).
@joshua-temple joshua-temple merged commit 64b5001 into main Jun 18, 2026
121 checks passed
@joshua-temple joshua-temple deleted the feat/machine-visualizer branch June 18, 2026 01:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant