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:
2026-05-21 19:40:45 +00:00
parent f3e084d02f
commit 907c33650f
14 changed files with 189 additions and 85 deletions

View File

@@ -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`);