- C-01: fix broken README link (call-graph-runtime.md → call-graph.md)
- C-02: add CallEdgeAttrs union type alias in schema.md
- C-03/W-12: rename TypedEdgeAttrs → OperationEdgeAttrs for consistent
{GraphType}EdgeAttrs naming pattern, update all references
- W-01: standardize terminology — prerequisites=structural/graph,
preconditions=reactive/computed, rename WorkflowNode.prerequisites
to preconditions, rename computePrerequisites to computePreconditions
- W-09: update ADR-001/002/003 status from Proposed to Accepted
- W-10: clarify call graph mutation API — addCall creates triggered
edges automatically, addDependency creates depends_on edges
- update review checklist with resolved items
3.8 KiB
ADR-001: ujsx Trees as Workflow Template IR
Status
Accepted
Context
Flowgraph needs a way to define workflow templates — reusable sequences of operations with conditional branching and parallel execution. The templates must be:
- Declarative — defining what should happen, not how
- Composable — nesting sequential, parallel, and conditional flows
- Serializable — store in JSON, transmit over APIs, version in git
- Validatable — check against an operation graph before execution
- Renderable to multiple targets — structural validation (DAG) and runtime execution (reactive)
The obvious approach is a custom template format: an array of step objects with type discriminators:
const template = [
{ type: "operation", name: "architect" },
{ type: "sequential", steps: [...] },
];
This works but has limitations:
- Custom format requires a custom parser, serializer, and validator
- No composition primitives —
sequentialandparallelare just types in an array - No host switching — a separate compiler is needed for each target (DAG, execution engine)
- No incremental updates — changing a step requires rebuilding the entire structure
Decision
Use ujsx UNode trees as the workflow template intermediate representation. Workflow components (Operation, Sequential, Parallel, Conditional) are UComponent functions that produce UElement nodes. The template is rendered to different targets through ujsx HostConfig implementations.
const template = h(Sequential, {},
h(Operation, { name: "architect" }),
h(Operation, { name: "reviewer" }),
);
Rationale
-
No new format — ujsx already defines
UNode,UElement,URoot, type guards, and serialization. We don't need to design, implement, and maintain a template format. -
Composition is structural —
<Sequential>and<Parallel>compose naturally as parent-child structure in a tree. Array-of-objects requires custom merging logic. -
Host target switching — the same
UNodetree renders to a graphology DAG (for validation) or a reactive engine (for execution) by swapping theHostConfig. No template-specific compiler needed. -
Incremental updates — when the ujsx reconciler is implemented, template changes (add/remove/reorder steps) can be applied incrementally without rebuilding the entire DAG. Array-of-objects requires full diffing and rebuilding.
-
Reactive props —
@preact/signals-coreenables signal-driven prop updates. AnOperationnode'snamecould be asignal<string>, enabling dynamic workflow modification at runtime. -
Serialization for free —
UNodetrees are plain JSON.JSON.stringify(template)works. No custom serializer needed.
Consequences
- Direct dependency on
@alkdev/ujsx— flowgraph importsh,createRoot,HostConfig,ReactiveRoot, and type definitions from ujsx. This is a direct dependency, not a peer dependency. - Function props don't serialize —
Conditional.testcan be a function(results) => boolean, which doesn't survive JSON round-trips. Templates with conditional branches need to providetestat render time or use expression strings. - Template components must follow ujsx component contract —
(props) => UNode. This is a minimal contract but it means components are synchronous functions that return a tree. - The template IS the tree — there is no separate compilation step between the ujsx tree and the render target. The
HostConfig.render()call IS the compilation.
References
- ujsx architecture:
@alkdev/ujsx/docs/architecture/README.md - ujsx HostConfig:
@alkdev/ujsx/docs/architecture/host-config.md - Workflow templates: workflow-templates.md
- Host configs: host-configs.md