Files
flowgraph/docs/architecture/decisions/001-ujsx-as-template-ir.md
glm-5.1 d2253099ee add flowgraph architecture docs (Phase 1 SDD)
Draft architecture specification for @alkdev/flowgraph — a workflow graph library providing DAG-based orchestration over operations. Covers two graph types (operation graph, call graph), ujsx workflow templates, GraphologyHost and ReactiveHost configs, signal-driven execution, type-compatibility analysis, error hierarchy, and build/distribution. Includes 3 ADRs: ujsx as template IR, DAG-only enforcement, decoupled storage.
2026-05-19 09:36:22 +00:00

3.8 KiB

ADR-001: ujsx Trees as Workflow Template IR

Status

Proposed

Context

Flowgraph needs a way to define workflow templates — reusable sequences of operations with conditional branching and parallel execution. The templates must be:

  1. Declarative — defining what should happen, not how
  2. Composable — nesting sequential, parallel, and conditional flows
  3. Serializable — store in JSON, transmit over APIs, version in git
  4. Validatable — check against an operation graph before execution
  5. 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 — sequential and parallel are 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

  1. 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.

  2. Composition is structural<Sequential> and <Parallel> compose naturally as parent-child structure in a tree. Array-of-objects requires custom merging logic.

  3. Host target switching — the same UNode tree renders to a graphology DAG (for validation) or a reactive engine (for execution) by swapping the HostConfig. No template-specific compiler needed.

  4. 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.

  5. Reactive props@preact/signals-core enables signal-driven prop updates. An Operation node's name could be a signal<string>, enabling dynamic workflow modification at runtime.

  6. Serialization for freeUNode trees are plain JSON. JSON.stringify(template) works. No custom serializer needed.

Consequences

  • Direct dependency on @alkdev/ujsx — flowgraph imports h, createRoot, HostConfig, ReactiveRoot, and type definitions from ujsx. This is a direct dependency, not a peer dependency.
  • Function props don't serializeConditional.test can be a function (results) => boolean, which doesn't survive JSON round-trips. Templates with conditional branches need to provide test at 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