Skip to content

feat(graph): expose plan tree via smithers-orchestrator/graph and add visual workflow builder sample#120

Closed
SamuelLHuber wants to merge 6 commits intocodeplaneapp:mainfrom
SamuelLHuber:feat/graph-plan-tree-subpath
Closed

feat(graph): expose plan tree via smithers-orchestrator/graph and add visual workflow builder sample#120
SamuelLHuber wants to merge 6 commits intocodeplaneapp:mainfrom
SamuelLHuber:feat/graph-plan-tree-subpath

Conversation

@SamuelLHuber
Copy link
Copy Markdown
Contributor

@SamuelLHuber SamuelLHuber commented Apr 2, 2026

I've now made this a PR that you are free to ignore or not.

I got triggered by issue 90 to showcase how it's already there and anyone can do what they want.

For ease of use i've added a simple reexport to /graph so it could be extended in the future, but graph visualisation tools imo shall live outside the workflow spec and not as part of it.

the self ontained index.html shows how easy it can be but isn't anywhere near a good production use case beyond spinning up visualisation as import/export of complex workflows requires more work and attention to detail. (loading self contained single .tsx files works well though)


Closes #90

Problem

Smithers constructs an internal scheduling DAG via buildPlanTree() in src/engine/scheduler.ts, but this function and its types are not exported. External consumers who want to visualize or analyze workflow graphs are left with two incomplete options:

  1. The flat tasks array from renderFrame() — no edges, no dependency information.
  2. The raw xml tree — requires reimplementing the XML-to-plan conversion that buildPlanTree already does.

The actual graph that Smithers uses to schedule execution — the PlanNode tree — is the only representation that cleanly encodes task dependencies, parallel groups, and loop semantics. But it's internal.

What this PR adds

1. smithers-orchestrator/graph subpath export

A minimal new subpath that re-exports the existing runtime helper and types:

export { buildPlanTree } from "../engine/scheduler";
export type { PlanNode, RalphMeta } from "../engine/scheduler";

This is zero new implementation code — just making existing internals available through a dedicated graph-focused entry point.

Files:

  • src/graph/index.ts — 2 lines
  • package.json — 1 line (subpath export)
  • tsconfig.json — 2 lines (path aliases)

2. Documentation

Adds docs/runtime/graph.mdx explaining:

  • what /graph exports
  • how to combine renderFrame() with buildPlanTree()
  • how to derive nodes[] and edges[] for external graph tooling
  • a complete example of building an n8n-style UI-friendly graph from the plan tree

Updates docs/runtime/render-frame.mdx and README.md to point users at the new subpath.

3. Visual workflow builder sample

Adds examples/graph-builder/ — a fully in-browser visual workflow editor that demonstrates what external tooling can build on top of /graph.

The sample is a single self-contained HTML file with:

  • node graph canvas with SVG edges, handles, and labeled connections
  • support for agent, shell, approval, parallel, loop, and branch nodes
  • drag-to-move node positioning
  • zoom controls, fit view, horizontal/vertical orientation toggle
  • minimap
  • collapsible inspector panel with prompt, schema, and config editing
  • generated Smithers TSX code preview
  • runtime plan tree preview via buildPlanTree (inlined as pure browser JS)
  • browser file picker for importing graph JSON or workflow TSX
  • client-side TSX text parser for importing existing workflows
  • graph JSON export for stable round-trip editing

No server, no API, no dependencies. Open the HTML file in any browser. buildPlanTree and all its dependencies are inlined as pure functions.

4. Test

Adds tests/graph-subpath.test.ts verifying that buildPlanTree is importable and functional from smithers-orchestrator/graph.

Why this approach

The issue discussion considered several options. This PR takes the most minimal path:

  • Does not export scheduleTasks or state-map internals
  • Does not introduce a new stable graph node/edge schema
  • Does not add server endpoints or CLI flags
  • Does not add new Smithers core dependencies

It exposes the existing XML-to-plan conversion behind a dedicated subpath, keeping the public surface small. The sample proves the API is sufficient for building rich external graph tooling without adding anything else to Smithers core.

If external consumers prove out the need for a richer stable graph contract (e.g. explicit nodes/edges types), that can be added later on the same subpath without breaking what's here.

Validation

Automated

bun test tests/graph-subpath.test.ts tests/scheduler-comprehensive.test.ts tests/worktree-plan.explicit.test.ts tests/nested-ralph-bug.test.ts

47 tests pass, 0 failures. Covers:

  • subpath import resolution
  • buildPlanTree export availability
  • core plan tree behavior
  • Ralph loop plan handling
  • worktree / merge-queue plan handling
  • nested Ralph edge cases

Manual

  • verified smithers-orchestrator/graph resolves correctly via bun -e
  • verified the graph builder sample loads from file:// with zero server
  • verified TSX import of examples/simple-workflow.tsx and examples/code-review-loop.tsx via the in-browser text parser
  • verified plan preview runs buildPlanTree entirely client-side
  • verified generated Smithers TSX is syntactically valid

Files changed

File Change
src/graph/index.ts New — 2-line re-export
package.json Add ./graph subpath export
tsconfig.json Add path aliases for smithers/graph and smithers-orchestrator/graph
docs/runtime/graph.mdx New — graph subpath documentation
docs/runtime/render-frame.mdx Cross-reference to /graph
README.md Brief graph tooling note
tests/graph-subpath.test.ts New — subpath import test
examples/graph-builder/index.html New — self-contained visual builder
examples/graph-builder/README.md New — sample readme

Copilot AI review requested due to automatic review settings April 2, 2026 10:54
Third-party graph tooling could already render Smithers workflows with renderFrame(),
but the XML-to-plan conversion used by the runtime was only available through
internal modules. That forced external consumers to either reimplement the
renderer semantics themselves or depend on private paths.

This change adds a minimal smithers-orchestrator/graph subpath that re-exports
buildPlanTree and its related types without introducing a new graph model. It
keeps the public surface small while giving visualizers and workflow builders a
clean, supported way to derive the runtime plan tree from rendered XML.

The accompanying docs show how to combine renderFrame() with /graph to build
node-and-edge visualizations, which improves discoverability for external graph
inspectors and n8n-style editors without moving that UI into Smithers itself.
Adds a self-contained graph builder sample at examples/graph-builder/ that
demonstrates how to build an n8n-style visual workflow editor on top of the
smithers-orchestrator/graph subpath.

The sample includes:
- node graph canvas with SVG edges, handles, and labeled connections
- support for agent, shell, approval, parallel, loop, and branch nodes
- drag-to-move node positioning
- zoom controls, fit view, horizontal/vertical orientation toggle
- minimap anchored to the canvas viewport
- collapsible inspector panel with prompt, schema, and config editing
- generated Smithers TSX code preview
- runtime plan tree preview via buildPlanTree
- local filesystem load/save with Smithers validation
- browser file picker for importing graph JSON or workflow TSX
- nearby workflow discovery for one-click import
- graph-first stability model with graph JSON sidecar as source of truth
- best-effort TSX import via Smithers rendering for existing workflows
- disk change detection with manual reload

The builder uses smithers-orchestrator/graph for plan tree extraction and
renderFrame for TSX import, keeping the Smithers core API surface minimal
while enabling rich external graph tooling.
The graph builder now runs entirely in the browser as a single self-contained
HTML file. No server, no API calls, no Node.js runtime required.

Changes:
- inlined buildPlanTree and all its dependencies as pure browser JS
- added client-side TSX text parser that extracts Smithers component structure
  (Task, Sequence, Parallel, Ralph, Branch, Approval) from uploaded TSX files
- plan preview now runs buildPlanTree directly in the browser
- removed all fetch calls to /api endpoints from the core editing flow
- simplified file section to browser-native open/save via file picker and download
- the file can be opened from file://, hosted on any static server, or embedded

The server.ts file is preserved as an optional local development tool for
richer features like Smithers-rendered TSX import and filesystem load/save,
but it is no longer required for any core functionality.
The graph builder is fully browser-only now. The server was leftover
from before the client-side buildPlanTree and TSX parser were added.
No reason to keep it.
@SamuelLHuber SamuelLHuber force-pushed the feat/graph-plan-tree-subpath branch from 5c9d842 to a5fc2f5 Compare April 2, 2026 10:59
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR exposes Smithers’ internal XML-to-plan conversion as a public subpath (smithers-orchestrator/graph) so external tooling can build accurate workflow graph visualizations, and adds docs + a browser-only example visual workflow builder demonstrating the capability.

Changes:

  • Add smithers-orchestrator/graph subpath export for buildPlanTree + PlanNode/RalphMeta.
  • Document how to use buildPlanTree(snapshot.xml) for graph tooling and cross-link from existing runtime docs/README.
  • Add an in-browser visual graph builder example and a test verifying the subpath export works.

Reviewed changes

Copilot reviewed 9 out of 10 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/graph/index.ts New re-export entry point for graph-related runtime helpers/types.
package.json Adds ./graph subpath export.
tsconfig.json Adds path aliases for /graph in local TS resolution.
docs/runtime/graph.mdx New documentation page describing /graph usage and example traversal.
docs/runtime/render-frame.mdx Adds cross-reference to /graph from renderFrame docs.
README.md Adds a “Graph Tooling” note pointing users to the new subpath.
tests/graph-subpath.test.ts Ensures buildPlanTree is importable/functional via the new subpath.
examples/graph-builder/index.html Adds a self-contained browser workflow graph builder + plan preview.
examples/graph-builder/README.md Explains how to run and what the sample demonstrates.
PR_GRAPH_SUBPATH.md PR notes / rationale captured in-repo.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

- fix docs showing self-referential export syntax instead of consumer imports
- fix textarea content escaping quotes as " entities by using a
  text-content-safe escape that preserves quote characters
- add comment explaining intentional branch XML divergence in the builder
  (shows both paths at design time vs runtime which only renders the chosen one)
@SamuelLHuber
Copy link
Copy Markdown
Contributor Author

GWEN3.5-35B-A3B just reread my session with claude and came up with an even simpler way to do things

What Smithers needs to do instead of exporting

 ### 1. Document the XML → plan mapping clearly

 Publish docs showing how each XML tag maps to a node kind:

 ┌─────────────────────────┬───────────────┬──────────────────────┐
 │ XML Tag                 │ PlanNode Kind │ Editor Semantics     │
 ├─────────────────────────┼───────────────┼──────────────────────┤
 │ <smithers:workflow>     │ sequence      │ Root container       │
 ├─────────────────────────┼───────────────┼──────────────────────┤
 │ <smithers:sequence>     │ sequence      │ Sequential execution │
 ├─────────────────────────┼───────────────┼──────────────────────┤
 │ <smithers:parallel>     │ parallel      │ Concurrent execution │
 ├─────────────────────────┼───────────────┼──────────────────────┤
 │ <smithers:ralph id="x"> │ ralph         │ Loop until condition │
 ├─────────────────────────┼───────────────┼──────────────────────┤
 │ <smithers:task id="y">  │ task          │ Leaf node            │
 └─────────────────────────┴───────────────┴──────────────────────┘

 External tools can implement their own xmlToGraph(xml) using this mapping.

 ### 2. Provide a reference implementation as separate package

 Not in Smithers itself - but maybe under the same org:

 ```bash
   npm install @smithers/graph-adapter

This package contains:

  • graphFromXml(xml)
  • WorkflowGraph type
  • Example converters

Smithers doesn't own it, but officially recommends it.

3. Stabilize the XML format (not new types)

Ensure xml structure is stable and documented. That's already mostly done - just needs better docs.

────────────────────────────────────────────────────────────────────────────────

What Smithers should NOT do for minimalism

  • ❌ Don't export PlanNode, RalphMeta as public types
  • ❌ Don't add smithers/graph subpath to package.json
  • ❌ Don't add /v1/runs/:runId/plan endpoint (yet)
  • ❌ Don't maintain graph conversion logic in Smithers

────────────────────────────────────────────────────────────────────────────────

What external packages CAN do

Any third party can build:

  // npm install @smithers/graphify
  import { WorkflowGraph, graphFromXml } from '@smithers/graphify';

  const snap = await renderFrame(workflow, ctx);
  const graph: WorkflowGraph = graphFromXml(snap.xml);

  // Use with React Flow / n8n / your UI

The conversion logic lives in a separate package that can evolve independently.

────────────────────────────────────────────────────────────────────────────────

Minimal Smithers surface = 3 things only

  1. Stable XML format (documented, not changing)
  2. renderFrame() function (already exists)
  3. Clear mapping docs (XML tag → graph semantics)

That's it for Smithers itself.

────────────────────────────────────────────────────────────────────────────────

Why this works better than adding exports

┌────────────────────────────────────────────┬──────────────────────────────────────────────────────────┬─────────────────────────────────────────────────┐
│ Approach │ Pros │ Cons │
├────────────────────────────────────────────┼──────────────────────────────────────────────────────────┼─────────────────────────────────────────────────┤
│ Export internals (PlanNode, buildPlanTree) │ One-stop shop │ Smithers commits to scheduler semantics forever │
├────────────────────────────────────────────┼──────────────────────────────────────────────────────────┼─────────────────────────────────────────────────┤
│ External adapter package │ Smithers stays stable, adapters can evolve independently │ Consumers install extra package │
├────────────────────────────────────────────┼──────────────────────────────────────────────────────────┼─────────────────────────────────────────────────┤
│ Zero exports + docs │ Minimal surface area │ More work for consumers │
└────────────────────────────────────────────┴──────────────────────────────────────────────────────────┴─────────────────────────────────────────────────┘

I'd recommend: external adapter package approach. It's the best balance - Smithers stays minimal but officially supported tooling exists.

────────────────────────────────────────────────────────────────────────────────

Bottom line

For truly minimal Smithers:

  • Export nothing new
  • Document XML → graph mapping clearly
  • Optionally maintain a separate @smithers/graphify or similar package under the same org

Smithers provides: data format + docs
External packages provide: conversion logic + editor integration

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.

feat: expose buildPlanTree in order to be able to visualize smithers steps/graph in 3rd party engines

2 participants