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

69 lines
3.8 KiB
Markdown

# 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:
```typescript
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.
```typescript
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 free**`UNode` 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 serialize** — `Conditional.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](../workflow-templates.md)
- Host configs: [host-configs.md](../host-configs.md)