Skip to content

Canvas substrate: edge language, node-kind registry, ComfyUI adaptations, and the MF7 seam #144

Description

@Travis-Gilbert

Design consolidation from the 2026-07-30 planning session, grounded in #143 and Travis-Gilbert/Theorem#385. Everything here sits above what those PRs land: they give us the model canvas, the program canvas, the playground contracts (node events + drain markers, pins, tweaks, published tools, durable parks, QuickJS/Extism authoring), the OKF model profile, and versioned registry projections. This issue is the shared-substrate layer that makes the two canvases one system, plus answers to three open design questions.

ComfyUI law stands: GPL, read-and-reimplement only. Everything below is pattern adaptation, zero code movement.

A. One substrate, many node kinds

Extract the canvas shell from @commonplace/model-canvas into a substrate layer (package split or internal module boundary, implementer's call) with a node-kind registry: one generic node shell (header, badges, ports, selection, collapse) hosting kind-specific bodies.

Initial kinds: model-card (the ERD card from the fork), program-node (catalog-driven per PG1, contracts from @commonplace/program-contracts), note-markdown (ComfyUI's Markdown Note pattern), group-frame (titled region, see C), and the JSON Canvas kinds (text/file/link/group) so the inspector rail's Z-layer can converge on the same substrate later. Kinds are registry entries, not bespoke components; adding a kind must not touch the shell.

B. The edge language (settling the "between Railway and ComfyUI" ask)

One geometry + dash system, two palettes:

  • Geometry: cubic bezier (curvilinear), never orthogonal elbows.
  • Stroke: 1.5px, stroke-linecap: round, stroke-dasharray: 0.1 6 → true round dots, the Railway feel. Hover/selected: width 2px and dash tightens to 4 3 (reads as solidifying without a hard state jump).
  • Program-canvas palette: edge takes the source port's shape-class OKLCH family (PG law, max 5 families), ComfyUI's legibility without its solid heaviness. Port dots wear the same family color.
  • Model-canvas palette: relation edges stay neutral ink, low chroma; information rides the relation chip (order_id = order_id) and cardinality glyphs (N:N badge kept from the fork, restyled to console tokens). Relations aren't typed flows; coloring them would spend the hue budget on nothing.
  • Motion: animated stroke-dashoffset only on running edges during program execution (flow direction made visible), driven by the #385 node-event stream; prefers-reduced-motion disables; cap concurrently animated edges (~40) and fall back to a static "active" width bump beyond it.
  • Connection preview: the drag line renders in the source family and the satisfaction function (PG's drag-release filter) dims non-satisfying targets live.

C. ComfyUI adaptations, mapped to what exists

Pattern Substrate mapping Status
Group frames with titles (Step1/Step2/Step3) group-frame kind; membership = geometry, persisted in the layout doc, never in content identity needs kind
Node id badges (#66) shell badge slot; id = catalog node id short-hash needs shell slot
Widgets-on-node (seed/steps/cfg steppers) parameter rows rendered from the generated FieldType/port types; same renderer map as the records surface cells needs UI; contracts exist
Show/hide advanced inputs per-kind port sections with a collapse, port visibility in layout doc needs UI
Bypass / mute already in #385 contracts (constructors default not-bypassed/not-muted); needs shell affordance + dimmed render states needs UI
Inline output preview on node playground per-node inspection over the event stream; large outputs via spill handles contracts exist
Drag-from-port quick add PG drag-release palette filter, already specced; wire on substrate specced
Double-click canvas search quick-add palette, same satisfaction filter, no port context needs UI
Reroute dots edge waypoints stored in layout doc; render as bare port-dots on the edge needs UI
Queue affordance run panel per the Langflow borrowings register; #385 events/drain markers are the transport contracts exist
Subgraph composition program-as-node (published programs in catalog, #385) is the semantic form; visual collapse-selection-to-program is a later authoring convenience semantic form exists

D. The registry question (does RustyRed schema need anything for data modeling?)

Mostly no, and specifically yes once. Declared object types + FieldType + relations already model the ERD; the OKF model profile round-trips it. The one addition worth making, Theorem-side: a provider facet on declared object types, native-view | declared-record | derived-program | connector, which is the general form of the fork's per-card SQL badge. The canvas renders it as the card badge, the pg-server native-views seam consumes it to know which types it can serve, derived-program links a type to the published program that materializes it (closing the loop between the two canvases), and OKF export carries it as an x-theorem-* extension. Node kinds stay in the catalog; the facet is the only registry change. Multiple node types the ComfyUI way is section A's registry, not a schema feature.

E. The MF7 seam (converting #385's honest block into a plan)

#385 correctly refused to fake it: theorem-canvas-compile compiles semantic CanvasDoc and Graph Lisp state and has no registry-ERD projection contract. Proposal: now, the ERD canvas subscribes to the tenant-scoped registry GraphQL door's change signal from #385 (versioned projections make this clean); next, a small spec adds a registry-ERD projection producer to canvas-compile as a proper surface kind fed through its existing invalidation, so the canvas joins the reactive system instead of polling beside it. MF7's acceptance then applies unchanged. Until that spec exists, nothing pretends to be compile-integrated.

Checklist

  • Substrate extraction with node-kind registry (A), model-card and program-node re-seated as kinds
  • Edge system per B, shared geometry, two palettes, motion law, satisfaction-aware preview
  • group-frame and note-markdown kinds
  • Shell slots: id badge, bypass/mute affordance and render states, advanced-port collapse
  • Widgets-on-node from generated types
  • Reroute waypoints in the layout doc
  • Double-click quick-add palette
  • Theorem: provider facet on declared object types (cross-repo, may split to its own issue)
  • Theorem: registry-ERD projection producer spec for canvas-compile (unblocks MF7)

Layout, membership, waypoints, and port visibility all persist in the canvas layout document through the object seam; none of it ever enters content identity, per the standing law.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions