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
---
# Schema
@@ -99,6 +99,53 @@ type NodeStatus = Static<typeof NodeStatusEnum>;
**Precondition semantics**: A predecessor in `completed` or `skipped` status satisfies a dependent's preconditions. A predecessor in `failed` or `aborted` status does NOT satisfy preconditions — it blocks the dependent and triggers failure propagation (the dependent transitions to `aborted`). This enables partial success: independent parallel branches continue running even when one branch fails.
### CallResult
The result of a completed call, used by `Conditional.test` and `Map.over` to access predecessor outputs:
```typescript
interface CallResult {
status: NodeStatus; // Status of the call (completed, failed, aborted, skipped)
output: unknown; // Call output (if completed)
error?: { // Call error (if failed)
code: string;
message: string;
details?: unknown;
};
}
```
`CallResult` is the value in the `results` map passed to `Conditional.test` and `Map.over` functions. It's derived from `CallNodeAttrs` but simplified for template use — it omits `requestId`, `operationId`, `identity`, and timestamps, preserving only what template logic needs.
### OperationTypeEnum
The type of an operation, determining its call semantics:
```typescript
const OperationTypeEnum = Type.Union([
Type.Literal("query"), // Read-only, idempotent
Type.Literal("mutation"), // Side effects, not idempotent
Type.Literal("subscription"), // Streaming, produces multiple results
]);
type OperationType = Static<typeof OperationTypeEnum>;
```
This enum is used in `OperationNodeAttrs.type` to classify operations by their call behavior.
### CallEventMapValue
`CallEventMapValue` is imported from `@alkdev/operations` (peer dependency). It represents a single call protocol event — the union type of all event types (`CallRequestedEvent | CallRespondedEvent | CallErrorEvent | CallAbortedEvent | CallCompletedEvent`). The full definition lives in `@alkdev/operations/src/call.ts`.
Flowgraph's `fromCallEvents()` and `updateFromEvent()` accept this type directly. The mapping from `CallEventMapValue` to `CallNodeAttrs` is:
| Event type | Action |
|------------|--------|
| `call.requested` | Add node with `status: "pending"`, add `triggered` edge if `parentRequestId` present |
| `call.responded` | Update node status to `completed`, set `output` and `completedAt` |
| `call.error` | Update node status to `failed`, set `error` and `completedAt` |
| `call.aborted` | Update node status to `aborted`, set `completedAt` |
| `call.completed` | Update node status to `completed`, set `completedAt` (if not already set) |
### EdgeType
The type of edge in a flowgraph. Matches the call graph storage schema's `edgeType` column:
@@ -196,6 +243,17 @@ type OperationEdgeAttrs = Static<typeof OperationEdgeAttrs>;
Type-compatibility edges carry a boolean `compatible` flag and optional detail. This allows the operation graph to include both compatible edges (green paths) and incompatible edges (red paths) for diagnostics.
**Edge type storage**: Operation graph edges always have `edgeType: "typed"` stored on the edge as a separate attribute alongside `OperationEdgeAttrs`. Graphology edges carry both the `OperationEdgeAttrs` (compatible, compatibilityDetail) and the `edgeType` field. The `edgeType` is not inside `OperationEdgeAttrs` because it's a universal edge classification that applies to all edge types across all graph modes (operation, call, template). The `OperationEdgeAttrs` schema only defines the mode-specific attributes.
```typescript
// How operation graph edges are stored in graphology:
{
edgeType: "typed", // Universal classification (stored alongside attrs)
compatible: true, // OperationEdgeAttrs field
compatibilityDetail: "..." // OperationEdgeAttrs field
}
```
**Naming note**: Previously named `TypedEdgeAttrs`. Renamed to follow the `{GraphType}EdgeAttrs` pattern used by `CallEdgeAttrs` and `TemplateEdgeAttrs`.
### TriggeredEdgeAttrs (Call Graph)
@@ -236,6 +294,18 @@ type TemplateEdgeAttrs = Static<typeof TemplateEdgeAttrs>;
Template edges carry an `edgeType` to distinguish sequential flow from conditional branching. Conditional edges optionally store a `condition` that determines whether the target node executes.
### TemplateNodeAttrs (Workflow Templates)
Template DAGs use `OperationNodeAttrs` for their operation nodes — the template doesn't need a separate node type because every node in a template DAG corresponds to an operation invocation. The template's structural information (`Sequential`, `Parallel`, `Conditional`, `Map`) is expressed through edges, not through special node types.
```typescript
// Template DAGs use OperationNodeAttrs for operation nodes
type TemplateNodeAttrs = OperationNodeAttrs;
// This alias makes the intent explicit: a template node represents an operation invocation
```
The separation between `OperationNodeAttrs` and `TemplateNodeAttrs` is a type alias for clarity. In the template context, each node carries the same attributes as an operation node (name, namespace, type, input/output schemas), but with template-specific edges (sequential, conditional) rather than type-compatibility edges (typed).
## SerializedGraph Factory
Following the taskgraph pattern, a generic factory for graphology native JSON format: