fix: architecture review - address 5 critical issues, 6 warnings, 3 suggestions
Critical fixes: - C1: Create standalone ADR-006 file (edge type consistency), extract from open-questions.md inline content - C2: Convert CallResult from plain interface to TypeBox schema, aligning with 'TypeBox as single source of truth' constraint - C3: Add fromJSON() cycle detection specification - enforce ADR-002 DAG invariant even on deserialized input - C4: Rewrite consumer-integration.md Phase 4 to use ADR-005 event-append pattern instead of direct signal mutation - C5: Fix operator precedence bug in consumer-integration.md (missing parentheses around OR condition) Warnings addressed: - W1: Fix immutability claim - operation graph is 'conventionally immutable', not prevented by API - W2: Add EventLogProjection to reactive exports map - W3: Add CallResult/CallResultSchema to schema exports map - W4: Fix reactive-execution.md Level 1 error handling to use event-append pattern instead of direct signal mutation - W5: Remove duplicate dataFlow inference description in schema.md - W6: Clarify ADR-006 project context (flowgraph vs taskgraph) Suggestions implemented: - S1: Add 'reviewed' document lifecycle status between draft/stable, update all docs to reviewed status - S2: Add carve-out note for analysis result types in schema.md constraints (they are ephemeral, not serialized) - S3: Add isComplete() and getAggregateStatus() convenience methods to WorkflowReactiveRoot specification
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
---
|
||||
status: draft
|
||||
status: reviewed
|
||||
last_updated: 2026-05-19
|
||||
---
|
||||
|
||||
@@ -152,22 +152,50 @@ const reactiveHost = new ReactiveHostConfig(registry, workflowRoot);
|
||||
const reactiveRoot = createRoot(reactiveHost, {});
|
||||
reactiveRoot.render(template);
|
||||
|
||||
// 4. Subscribe to status changes and effect-driven execution
|
||||
// 4. Drive execution via the event log (ADR-005 pattern)
|
||||
// The hub coordinator appends call protocol events; the projections derive state.
|
||||
for (const [nodeId, node] of workflowRoot.nodes) {
|
||||
// Start the call when preconditions are met
|
||||
effect(() => {
|
||||
if (node.preconditions.value && node.status.value === "idle" || node.status.value === "waiting") {
|
||||
node.status.value = "running";
|
||||
// getInput resolves the node's input from predecessor outputs and static config
|
||||
// For Operation nodes, input comes from the template props or aggregated predecessor results
|
||||
const input = resolveInput(nodeId, workflowRoot);
|
||||
registry.execute(node.operationId, input, { parentRequestId: parentCallId })
|
||||
.then(result => { node.status.value = "completed"; node.output.value = result; })
|
||||
.catch(error => { node.status.value = "failed"; });
|
||||
if (node.preconditions.value && (node.status.value === "idle" || node.status.value === "waiting")) {
|
||||
// All preconditions satisfied — start the call by appending to the event log
|
||||
const operationId = dag.getNodeAttributes(nodeId).name;
|
||||
const requestId = crypto.randomUUID();
|
||||
workflowRoot.nodeKeyToRequestId.set(nodeId, requestId);
|
||||
|
||||
// Append call.requested event — the status projection derives "running" from this
|
||||
workflowRoot.append({
|
||||
type: "call.requested",
|
||||
requestId,
|
||||
operationId,
|
||||
input: getInput(nodeId, workflowRoot),
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
|
||||
// Start the actual call — when it completes, append the result event
|
||||
registry.execute(operationId, getInput(nodeId, workflowRoot), { parentRequestId })
|
||||
.then(result => {
|
||||
// Append call.responded event — the status projection derives "completed" from this
|
||||
workflowRoot.append({
|
||||
type: "call.responded",
|
||||
requestId,
|
||||
output: result,
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
})
|
||||
.catch(error => {
|
||||
// Append call.error event — the status projection derives "failed" from this
|
||||
workflowRoot.append({
|
||||
type: "call.error",
|
||||
requestId,
|
||||
error: { code: error.code, message: error.message },
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Track failures
|
||||
// Track failures for logging
|
||||
effect(() => {
|
||||
if (node.status.value === "failed") {
|
||||
console.error(`Node ${nodeId} failed`);
|
||||
|
||||
Reference in New Issue
Block a user