resolve architecture review round 2: criticals, warnings, suggestions

- C-05: Add flowgraph-api.md with complete public API surface
- C-06: Document <Map> component in workflow-templates.md
- C-07: Specify Conditional else-branch behavior
- C-08: Add lifecycle/ownership section to reactive-execution.md
- C-09: Add consumer-integration.md end-to-end walkthrough
- W-02: Add reactive error boundary semantics (3 levels)
- W-03: Complete ReactiveContext interface definition
- W-04: Add template composition rules (8 rules)
- W-05: Document removeChild for both HostConfigs
- W-06: Document signal/effect disposal lifecycle
- W-07: Add ADR-004 (no schema version field)
- W-08: Add type compatibility depth/contract to analysis.md
- W-11: Add performance characteristics section
- S-01: Getting Started merged into consumer-integration.md
- S-02: Add flow diagrams for template rendering pipeline
- S-03: Add node status state machine diagram
- S-04: Add testing strategy section
- S-06: Validate source structure cross-references

Review round 2 fixes:
- Define TemplateNodeAttrs as alias for OperationNodeAttrs
- Document CallEventMapValue and CallResult types in schema.md
- Standardize CycleError naming (replace CircularDependencyError)
- Add function form to Map.over type definition
- Define Map aggregate completion/failure semantics
- Fix immutability claim for fromCallEvents
- Clarify edgeType storage alongside OperationEdgeAttrs
- Clarify WorkflowNode.status === statusMap (same Signal)
- Add component-to-tag mapping for WorkflowTag
This commit is contained in:
2026-05-19 13:05:35 +00:00
parent 1dbaccbde3
commit eaeba38e71
13 changed files with 1489 additions and 57 deletions

View File

@@ -1,6 +1,6 @@
---
status: draft
last_updated: 2026-05-19
last_updated: 2026-05-20
---
# Build & Distribution
@@ -17,6 +17,7 @@ Package structure, exports map, dependencies, and platform targets.
│ │ ├── sequential.ts # <Sequential> component
│ │ ├── parallel.ts # <Parallel> component
│ │ ├── conditional.ts # <Conditional> component
│ │ ├── map.ts # <Map> component
│ │ └── index.ts
│ ├── host/
│ │ ├── graphology.ts # GraphologyHostConfig
@@ -133,7 +134,7 @@ Following the taskgraph pattern, each module has a sub-path export:
| Sub-path | Content | Use case |
|----------|---------|----------|
| `@alkdev/flowgraph` | Barrel export (everything) | Full import |
| `@alkdev/flowgraph/component` | `<Operation>`, `<Sequential>`, `<Parallel>`, `<Conditional>` | Template authoring |
| `@alkdev/flowgraph/component` | `<Operation>`, `<Sequential>`, `<Parallel>`, `<Conditional>`, `<Map>` | Template authoring |
| `@alkdev/flowgraph/host` | `GraphologyHostConfig`, `ReactiveHostConfig` | ujsx HostConfig implementations |
| `@alkdev/flowgraph/schema` | TypeBox schemas, enums, types | Schema-only import (no graph dependency) |
| `@alkdev/flowgraph/graph` | `FlowGraph` class, construction, mutation, queries | Core graph operations |
@@ -272,6 +273,81 @@ The sub-path export structure enables effective tree-shaking:
The barrel export (`@alkdev/flowgraph`) re-exports everything for convenience, but consumers concerned about bundle size should use sub-path imports.
## Testing Strategy
### Unit Test Categories
| Category | What to test | How |
|----------|-------------|-----|
| Schema validation | TypeBox schemas validate/correct shapes | `Value.Check()` / `Value.Errors()` |
| Graph construction | `fromSpecs`, `fromCallEvents`, `fromJSON` | Build graphs, assert node/edge counts |
| Graph mutations | `addNode`, `addEdge`, `updateStatus` | Assert success, assert throws on violations |
| Graph queries | `topologicalOrder`, `ancestors`, `descendants` | Known graphs, expected results |
| Type compatibility | `typeCompat` for known schema pairs | Compatible/incompatible/unknown |
| Template validation | `validateTemplate` against known graphs | Known valid/invalid templates |
| Error hierarchy | `CycleError`, `InvalidTransitionError`, etc. | Assert throw types, assert message format |
| Reactive execution | Signal propagation, preconditions, abort cascade | Set up mini reactive graph, assert state transitions |
### Testing Reactive Graphs
Testing signal-based state propagation requires specific patterns:
1. **Setup**: Create a `WorkflowReactiveRoot` with a known DAG. Assert initial state (all nodes `idle`).
2. **Transition**: Set a node's status signal to a known value. Assert that dependents' `preconditions` and `blockedByFailure` computeds update correctly.
3. **Assertion**: Check `node.status.value`, `node.preconditions.value`, `node.blockedByFailure.value` at each step.
```typescript
// Example test pattern
const root = new WorkflowReactiveRoot(dag);
const nodeA = root.statusMap.get("A")!;
const nodeB = root.statusMap.get("B")!;
// Initially: both idle
expect(nodeA.value).toBe("idle");
expect(nodeB.preconditions.value).toBe(false); // A not completed yet
// Complete A → B's preconditions met
nodeA.value = "completed";
expect(nodeB.preconditions.value).toBe(true);
```
4. **Cleanup**: Call `root.dispose()` after each test to prevent signal leaks.
### Testing Template Rendering
Template rendering tests follow the same pattern for both HostConfigs:
1. Define a template
2. Render to the target (graphology or reactive)
3. Assert the output (graph structure or signal state)
```typescript
// GraphologyHostConfig test
const host = new GraphologyHostConfig();
const root = createRoot(host, new DirectedGraph());
root.render(template);
const graph = root.ctx.graph;
expect(graph.nodes()).toEqual(["A", "B", "C"]);
expect(graph.edges()).toEqual(["A->B", "B->C"]);
// ReactiveHostConfig test
const reactiveHost = new ReactiveHostConfig(registry, workflowRoot);
const reactiveRoot = createRoot(reactiveHost, {});
reactiveRoot.render(template);
expect(workflowRoot.statusMap.size).toBe(3);
```
### Testing Error Paths
All error paths should be tested:
- Cycle detection: adding a cycle-creating edge throws `CycleError`
- Duplicate node/edge: adding duplicates throws `ConstructionError`
- Invalid status transition: `updateStatus(completed → running)` throws `InvalidTransitionError`
- Validation errors: `validateGraph()` returns arrays, never throws
## Constraints
- **No filesystem access** — flowgraph is a pure computation library. Persistence is the hub's concern.