Graph primitive reuse — non-obvious findings
The portability promise from TASKSET 3 paid off: zero changes to the primitive were required to make it serve a second consumer.What changed
cairnet/src/components/charts/graph-primitive.tsx— verbatim copy oflore/src/components/charts/graph-primitive.tsx. Copy, not workspace import — sdk-js workspace linking still pending.cairnet/src/components/cairn/stone-thread-graph.tsx— new adapter mapping(root: Stone, replies: Stone[])to GraphPrimitive’s{ nodes, edges }shape. Click handler navigates to/stones/:id.cairnet/src/components/cairn/thread-graph-panel.tsx— small client-component disclosure wrapping the graph; default-closed, null-renders whenreplies.length === 0to avoid an inert single-node canvas.cairnet/src/app/stones/[id]/page.tsx— renders<ThreadGraphPanel>between the rootStoneCardand the reply list.
Decisions that ran against the spec, and why
1. Skipped the /explore graph. The campaign mentioned surfacing the primitive on/explore for “reaction-weighted clusters.” On inspection
that surface has no meaningful edges — /api/cairn/explore returns flat
top-N lists for trending types and trending agents, not relations. A
graph there would have been decorative theatre, violating the project’s
“no features beyond what the task requires” rule. If we later add
agent-to-agent connection signal (co-reaction, mention edges), revisit.
2. Adapter-only navigation, primitive stays route-free. The
adapter calls useRouter() and pushes; the primitive only knows
onNodeClick(id). This reproduces the LORE pattern (causality-graph)
and means a third app could adopt the primitive with its own router
without touching either app.
3. No type-color override. The primitive colors nodes by group
(center/upstream/downstream/neutral). It would be tempting to extend
node groups with stone-types so a “hypothesis” reply renders regolith
and a “question” reply renders dust. Tempting, then declined: extending
the group enum would be a primitive-level change, defeating the
portability claim. Stone type stays in meta and surfaces in the
hover tooltip only. If demand for type-tinted nodes proves out, the
right path is making the primitive accept a getNodeColor(node)
callback — a backwards-compatible extension, not a breaking enum.
4. Single-node graphs hidden. When a root has no replies, the panel
renders nothing rather than a lone center dot. Confirms the rule:
visualisation is signal, not chrome.
Footguns surfaced
useRouterin adapter, not primitive. The cairn adapter is a client component because of the router hook. The primitive is also a client component (interactive SVG). Both are correctly marked"use client". Forgetting either marker would surface as a build error rather than a runtime one — Next 14 is good about this.- Dual-source-of-truth risk for the primitive. With the file now
copied into both
lore/andcairnet/, drift is inevitable. The campaign already accepted this cost (“if reuse breaks, CAIRN forks a copy — acceptable cost”). The mitigation is the next item in the sdk-js workspace backlog: extract@devarno/sdk-chartsso both apps depend on a single artifact.
Verification done
grepof cairnet primitive imports: onlyreact. Zero LORE, cairnet-app, or api-client references.npx tsc --noEmitclean.npx next buildsucceeded;/stones/[id]route bundle grew 202 B → 2.8 kB, accounting for the primitive (~1.5 kB gzipped) + adapter + panel disclosure. All other route bundles unchanged.- Click navigation: tapping a reply node pushes
/stones/<reply-id>; tapping the root is a no-op (root === current).
What does NOT exist yet
- No
/exploregraph (intentional, see decision #1). - No nested-thread support — the adapter takes a flat
replies: Stone[], mirroring v0 pebble shape. Recursive nesting is a 5-line change in the adapter when pebble starts returning a tree. - No agent-graph visualisation — different problem (would need edges from co-reaction or follow signal that pebble doesn’t compute yet).
Verification checklist (campaign §“TASKSET 6”)
- Same primitive serves both LORE (causality graph) and CAIRN (thread map) — zero copy-paste of renderer logic; zero edits to the primitive in either repo.
- Adapter is the only file that knows about cairn types and cairn routes.
- Build green; bundle delta accounted for.