Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
118 changes: 118 additions & 0 deletions PR_GRAPH_SUBPATH.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
# PR Title

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

# PR Body

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:

```ts
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

```bash
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 |
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,17 @@ smithers list workflow.tsx
smithers approve workflow.tsx --run-id abc123 --node-id review
```

## Graph Tooling

Smithers does not ship a visual workflow editor, but it exposes the XML-to-plan conversion the runtime uses internally:

```ts
import { renderFrame } from "smithers-orchestrator";
import { buildPlanTree } from "smithers-orchestrator/graph";
```

That is enough to build external graph inspectors, React Flow canvases, and n8n-style workflow builders on top of Smithers without re-implementing its execution semantics.

## Hot Module Replacement

Edit your workflow files while a run is executing. Smithers watches your source tree and hot-reloads changes on save — prompts, config, agent settings, and component structure — without restarting the process or losing run state.
Expand Down
128 changes: 128 additions & 0 deletions docs/runtime/graph.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
---
title: graph
description: Build workflow graph tooling on top of Smithers by converting rendered XML into the scheduler plan tree.
---

Smithers does not ship a visual workflow builder, but it does expose the same XML-to-plan conversion the runtime uses internally. That is enough to build external graph visualizers, inspectors, and node-based editors.

## Import

```ts
import { buildPlanTree } from "smithers-orchestrator/graph";
import { renderFrame } from "smithers-orchestrator";
```

## What `/graph` exports

```ts
import { buildPlanTree } from "smithers-orchestrator/graph";
import type { PlanNode, RalphMeta } from "smithers-orchestrator/graph";
```

`buildPlanTree(xml)` converts the rendered `XmlNode` tree into the plan tree the engine schedules.

## Basic usage

```ts
import { renderFrame } from "smithers-orchestrator";
import { buildPlanTree } from "smithers-orchestrator/graph";
import workflow from "./workflow";

const snapshot = await renderFrame(workflow, {
runId: "preview",
iteration: 0,
input: {},
outputs: {},
});

const { plan, ralphs } = buildPlanTree(snapshot.xml);
console.log(plan, ralphs);
```

## PlanNode shape

```ts
type PlanNode =
| { kind: "task"; nodeId: string }
| { kind: "sequence"; children: PlanNode[] }
| { kind: "parallel"; children: PlanNode[] }
| {
kind: "ralph";
id: string;
children: PlanNode[];
until: boolean;
maxIterations: number;
onMaxReached: "fail" | "return-last";
}
| { kind: "group"; children: PlanNode[] };
```

## Building a node graph UI

A minimal graph builder can:

1. call `renderFrame()` to get `snapshot.xml`
2. call `buildPlanTree(snapshot.xml)`
3. walk the returned `PlanNode` tree
4. emit your own UI-friendly `nodes[]` and `edges[]`

For example:

```ts
type UiNode = { id: string; label: string; kind: string };
type UiEdge = { from: string; to: string; kind: string };

function planToUiGraph(plan: PlanNode | null) {
const nodes: UiNode[] = [];
const edges: UiEdge[] = [];
let nextId = 0;

function visit(node: PlanNode, parentId?: string): string {
const id = node.kind === "task" ? node.nodeId : `${node.kind}:${nextId++}`;
nodes.push({ id, label: node.kind === "task" ? node.nodeId : node.kind, kind: node.kind });
if (parentId) edges.push({ from: parentId, to: id, kind: "contains" });

if ("children" in node) {
let previousChildId: string | undefined;
for (const child of node.children) {
const childId = visit(child, id);
if (node.kind === "sequence" && previousChildId) {
edges.push({ from: previousChildId, to: childId, kind: "next" });
}
previousChildId = childId;
}
if (node.kind === "ralph" && previousChildId) {
edges.push({ from: previousChildId, to: id, kind: "loop" });
}
}

return id;
}

if (plan) visit(plan);
return { nodes, edges };
}
```

This is enough to power React Flow, Dagre, Mermaid, n8n-style inspectors, or a custom workflow editor.

## Why use `/graph` instead of re-parsing XML yourself?

`buildPlanTree()` already captures Smithers scheduling semantics, including:

- `smithers:sequence` → ordered children
- `smithers:parallel` / `smithers:merge-queue` → concurrent children
- `smithers:ralph` → loop node with iteration metadata
- `smithers:worktree` → grouping boundary

Using `/graph` keeps third-party tooling aligned with the runtime instead of duplicating that mapping.

## Scope

The `/graph` subpath is intentionally minimal. It exposes the existing XML-to-plan conversion so external tools can build their own graph model and UI on top.

## Related

- [renderFrame](/runtime/render-frame)
- [Events](/runtime/events)
- [CLI overview](/cli/overview)
7 changes: 7 additions & 0 deletions docs/runtime/render-frame.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,12 @@ const snap2 = await renderFrame(workflow, {
});
```

### Building a Workflow Visualizer

The XML tree and task list can be used to build visual representations of the workflow DAG. The `xml` field mirrors the JSX structure, while `tasks` provides the flattened execution order.

If you want the same XML-to-plan conversion Smithers uses internally, import `buildPlanTree` from `smithers-orchestrator/graph` and pass `snapshot.xml` to it.

### CLI

```bash
Expand All @@ -217,6 +223,7 @@ Prints the `GraphSnapshot` as JSON to stdout.

## Related

- [graph](/runtime/graph) -- Convert rendered XML into the plan tree used for graph tooling.
- [runWorkflow](/runtime/run-workflow) -- Execute the workflow.
- [Events](/runtime/events) -- Monitor execution progress.
- [Execution Model](/concepts/execution-model) -- The render-execute-persist loop.
63 changes: 63 additions & 0 deletions examples/graph-builder/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# Smithers Graph Builder

A fully in-browser visual workflow editor for Smithers. No server required.

One self-contained HTML file. Open it directly or host it anywhere.

## What it does

- lets you add and edit a richer set of node types
- agent prompt
- shell command
- approval gate
- parallel block
- bounded review loop
- branch
- visualizes the workflow as a node graph with
- labeled edges
- branch fan-out
- loop-back edges
- zoom controls
- horizontal / vertical reorientation
- a minimap
- edits prompts, output keys, schemas, commands, loop settings, and branch conditions inline
- exports both
- `workflow.graph.json`
- `workflow.tsx`
- supports loading local workflows from
- `workflow.graph.json`
- `workflow.tsx`
- a directory containing those files
- supports uploading graph JSON or a self-contained workflow TSX file manually
- validates generated code through Smithers before local save
- uses `buildPlanTree()` from `smithers-orchestrator/graph` to show the runtime plan tree

## Stability model

The builder is intentionally **graph-first**.

- If a `workflow.graph.json` sidecar exists, it is treated as the source of truth.
- Generated `workflow.tsx` is derived from that graph model.
- Loading an existing standalone `workflow.tsx` works as a **best-effort import** using Smithers rendering. This is useful for drafting and visualization, but it is not guaranteed to round-trip perfectly.

That split keeps editing stable and explicit while still letting the builder leverage Smithers to import existing workflows.

## Run

Open `index.html` in any browser. That's it.

```bash
open examples/graph-builder/index.html
```

Or host it on any static file server, CDN, or paste it into a gist.

## How it works

Everything runs in the browser:

- `buildPlanTree` from `smithers-orchestrator/graph` is inlined as a pure function
- TSX import uses a client-side text parser that extracts Smithers component structure
- graph editing, code generation, plan preview — all client-side JS
- no fetch calls, no API, no server, no dependencies

Loading
Loading