ADR-005 accepted: resolve all open consequences, update cascading docs
Resolve the three open consequences from ADR-005 (Event Log as Single Source of Truth) and transition from Proposed to Accepted: 1. Event log IS the call protocol event stream — not a separate type, but an EventLogProjection interface (append/getStatus/getResult/ getEvents) over CallEventMapValue[] with an append-only contract. 2. Event log persists across template re-renders — projections recompute against the new DAG; orphaned events stay in log for audit but don't affect active projections. 3. Edges get dataFlow: boolean attribute on TemplateEdgeAttrs — inferred (not manual) by GraphologyHostConfig from template expressions. typeCompat() only runs on dataFlow: true edges. Inference rules are precisely specified for Conditional.test, Map.over, and Operation.input. Also resolve OQ-05 (structural containers stay transparent; aggregate status is a projection from children) and OQ-10 (running node failure is a FailurePolicy configuration, default continues-running). Cascading updates to: - reactive-execution.md: add hybrid status model (event-log-driven vs projection-driven vs signal-mutation), EventLogProjection interface, result projection respecting retries, FailurePolicy type - host-configs.md: ReactiveContext now includes resultProjection and computed results; resolved Q1/Q3/Q4 - schema.md: dataFlow attribute on TemplateEdgeAttrs with inference rules and type checking implications - workflow-templates.md: edge creation rules with dataFlow, result projection in Conditional/Map, resolved Q1/Q4 - open-questions.md: all ADR-005 questions marked resolved, updated summary table and cross-cutting themes, removed duplicate OQ-07 7 files changed, 464 insertions, 139 deletions
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
---
|
||||
status: draft
|
||||
last_updated: 2026-05-20
|
||||
last_updated: 2026-05-21
|
||||
---
|
||||
|
||||
# Host Configs
|
||||
@@ -234,16 +234,19 @@ interface ReactiveContext {
|
||||
statusSignals: Map<string, Signal<NodeStatus>>; // Status signals by key (owned by WorkflowReactiveRoot)
|
||||
preconditions: Map<string, Computed<boolean>>; // Precondition computeds by key (owned by WorkflowReactiveRoot)
|
||||
blockedByFailure: Map<string, Computed<boolean>>; // blockedByFailure computeds by key (owned by WorkflowReactiveRoot)
|
||||
resultProjection: EventLogProjection; // Result projection from ADR-005 event log
|
||||
parentMap: Map<string, string>; // Child → parent key mapping (for precondition computation)
|
||||
siblingMap: Map<string, string[]>; // Parent → children keys (for structural context)
|
||||
results: Map<string, Signal<unknown>>; // Operation output signals by key
|
||||
results: Map<string, Computed<CallResult | undefined>>; // Result computeds by key (derived from event log)
|
||||
}
|
||||
```
|
||||
|
||||
The `ReactiveContext` is constructed during `ReactiveHostConfig` initialization. It receives the `operationRegistry` and empty maps. During `createInstance`, nodes and signals are registered in the context maps. After rendering completes, the context holds a complete index of the reactive workflow tree.
|
||||
The `ReactiveContext` is constructed during `ReactiveHostConfig` initialization. It receives the `operationRegistry`, empty maps, and the `EventLogProjection` from the `WorkflowReactiveRoot`. During `createInstance`, nodes and signals are registered in the context maps. After rendering completes, the context holds a complete index of the reactive workflow tree.
|
||||
|
||||
**Important**: `statusSignals`, `preconditions`, and `blockedByFailure` are references to the `WorkflowReactiveRoot`'s maps. The `ReactiveHostConfig` does not own these signals — it looks them up during `createInstance` to wire `WorkflowNode` references. Disposal is the `WorkflowReactiveRoot`'s responsibility.
|
||||
|
||||
**Result projection (ADR-005)**: The `resultProjection` provides `getResult(nodeId)` which returns `CallResult | undefined`. This is derived from the event log, not from direct signal reads. `Conditional.test` and `Map.over` functions access predecessor results through this projection, ensuring they always see the most recent data — including retry results.
|
||||
|
||||
### createInstance
|
||||
|
||||
```typescript
|
||||
@@ -435,7 +438,7 @@ Alternative: Create "virtual" nodes for structural containers that hold `signal<
|
||||
|
||||
### Conditional Test Evaluation
|
||||
|
||||
The `Conditional.test` prop can be a function or a string. At the template level, it's stored as a prop. At runtime, the reactive engine evaluates it as a `computed` that depends on referenced nodes' outputs. This evaluation needs access to the `WorkflowContext` (which holds the results of previous steps), which means the reactive engine must have a reference to the call graph or a results map.
|
||||
The `Conditional.test` prop can be a function or a string. At the template level, it's stored as a prop. At runtime, the reactive engine evaluates it as a `computed` that depends on referenced nodes' outputs from the **result projection** (per ADR-005). This evaluation reads from `getResult(nodeId)` which derives from the event log, ensuring that `Conditional.test` always sees the most recent state — including retry results.
|
||||
|
||||
## Constraints
|
||||
|
||||
@@ -448,13 +451,13 @@ The `Conditional.test` prop can be a function or a string. At the template level
|
||||
|
||||
## Open Questions
|
||||
|
||||
1. **Should structural containers create "virtual" nodes in the reactive engine?** This would simplify precondition computation (every node has a status) but adds nodes that don't correspond to calls or operations.
|
||||
1. ~~**Should structural containers create "virtual" nodes in the reactive engine?**~~ **Resolved (OQ-05)**: Containers stay transparent. Aggregate status for structural containers is computed as a projection from children's statuses, without requiring nodes in the event log or DAG. The `parentMap` and `siblingMap` in `ReactiveContext` provide the structural context for precondition computation.
|
||||
|
||||
2. **Should the GraphologyHostConfig produce a separate graph for edge types?** Currently all edge types (`sequential`, `conditional`, `typed`) share the same graph. An alternative is a separate graph per edge type, enabling type-specific queries without filtering.
|
||||
|
||||
3. **How does the ReactiveHostConfig interact with the call protocol?** When a node transitions to `ready`, the reactive engine needs to call `registry.execute()` or `PendingRequestMap.call()`. This bridges the reactive layer to the operation execution layer. The HostConfig's `createInstance` callback is one option; a separate `ExecutionEngine` class is another.
|
||||
3. ~~**How does the ReactiveHostConfig interact with the call protocol?**~~ **Resolved (ADR-005)**: The reactive layer bridges to the call protocol through the event log. The hub coordinator appends call protocol events; the reactive layer projects them into status and results. The `ReactiveHostConfig` reads from the `EventLogProjection` interface (via `getStatus()` and `getResult()`), not from direct signal mutations by the coordinator.
|
||||
|
||||
4. **Should the reactive engine own the call graph?** Currently the call graph (from call-graph.md) and the reactive engine (from this doc) are separate concepts. But at runtime, every `<Operation>` in a template becomes a call graph node. Should the reactive engine populate the call graph as a side effect?
|
||||
4. ~~**Should the reactive engine own the call graph?**~~ **Resolved (ADR-005)**: Neither owns the other. Both the call graph and the reactive engine are projections of the same event log. The call graph projects the structural view (who triggered whom). The reactive engine projects the behavioral view (what's running, what's blocked).
|
||||
|
||||
## References
|
||||
|
||||
|
||||
Reference in New Issue
Block a user