{
"component": "orchestration",
"tier": "full",
"loop_stage": "control",
"summary": "The orchestration component runs multi-agent loops. Its WorkflowExecutor (src/orchestration/workflows.rs) drives a DAG of WorkflowSteps (Tool, Shell, Llm, Condition, Loop, SubWorkflow, Guardrail) with retries, timeouts, and cycle detection; its Swarm coordinator drives role-typed Agents through task assignment, weighted Votes, and Decisions resolved by a ConflictStrategy over SharedMemory; MultiAgentChat fans one completion out per Agent under a BudgetGuard and a tokio Semaphore; and the VisualFeedbackLoop iterates act-capture-evaluate against a VisualScore quality_threshold. On the loop it is the control driver that spawns, sequences, and reconciles parallel sub-loops.",
"loop_objects": ["Workflow", "WorkflowStep", "StepType", "WorkflowContext", "StepResult", "WorkflowStatus", "Swarm", "Agent", "AgentRole", "SwarmTask", "Vote", "Decision", "ConflictStrategy", "SharedMemory", "MultiAgentChat", "AgentResult", "MultiAgentEvent", "VisualFeedbackLoop", "VisualScore", "VisualLoopResult"],
"context_basis": "Recommendations were formed with src/orchestration/ read in the context of the full engine under a ~600k budget framing; fan-out and swarm coordination assume a shared budget that must be guarded across many concurrent sub-agents.",
"examples": [
{
"id": "orchestration-01",
"title": "Execute a Workflow as a dependency-ordered DAG",
"loop_stage": "control",
"pattern": "dag-execution",
"intent": "Run steps in dependency order so each starts only when its inputs are ready.",
"how_it_shapes_the_loop": "WorkflowExecutor::execute loads a Workflow, builds a WorkflowContext, and runs WorkflowSteps respecting depends_on, so the control layer sequences the sub-loop deterministically.",
"loop_objects_touched": ["Workflow", "WorkflowStep", "WorkflowContext", "StepResult"],
"wiring": {
"inputs_from": ["workflow definition (crate::orchestration::workflows::Workflow)", "WorkflowInputs"],
"outputs_to": ["StepResults", "WorkflowStatus"]
},
"touch_interaction": {
"gesture": "draw-connection",
"canvas_action": "Drawing edges between step nodes declares depends_on links; the executor runs them in the drawn order.",
"visual": "Step nodes wire up with directed arrows; each lights green as its StepStatus hits Completed, the next arming only when its parents are done."
},
"mini_scenario": "The user wires clone before test before review; WorkflowExecutor::execute runs each only after its predecessor's StepResult reads Completed.",
"pitfall": "depends_on must form a DAG; a dependency cycle stalls the workflow, which is why WorkflowContext tracks an executing_steps stack for cycle detection."
},
{
"id": "orchestration-02",
"title": "Retry a failing step with backoff and a timeout",
"loop_stage": "control",
"pattern": "retry-with-timeout",
"intent": "Give a flaky step bounded retries without letting it hang the loop.",
"how_it_shapes_the_loop": "WorkflowExecutor::execute_step_with_retry wraps a step in a tokio::select against its RetryConfig timeout and retries up to max_attempts with delay, so the control layer contains transient failures.",
"loop_objects_touched": ["WorkflowStep", "StepResult"],
"wiring": {
"inputs_from": ["WorkflowStep RetryConfig"],
"outputs_to": ["StepResult (Completed / Failed)"]
},
"touch_interaction": {
"gesture": "long-press",
"canvas_action": "Long-pressing a step opens its RetryConfig dial; each attempt animates a countdown against the timeout ring.",
"visual": "A retry counter ticks and a timeout ring drains; on timeout the ring snaps red and the step marks Failed."
},
"mini_scenario": "A network tool step fails once, waits the backoff, succeeds on retry two, and returns a Completed StepResult.",
"pitfall": "The timeout must actually cancel the step future; a retry loop without cancellation lets a hung step run past its timeout budget."
},
{
"id": "orchestration-03",
"title": "Branch the loop with a Condition step",
"loop_stage": "reason",
"pattern": "conditional-branch",
"intent": "Choose the next steps based on a runtime condition over the context.",
"how_it_shapes_the_loop": "A Condition StepType evaluates its 'if' via WorkflowContext::evaluate_condition (success(), failed(), equality) and runs the then or else branch inline, so the reason stage forks the workflow path at runtime.",
"loop_objects_touched": ["StepType", "WorkflowContext", "StepResult"],
"wiring": {
"inputs_from": ["prior StepResults", "WorkflowContext variables"],
"outputs_to": ["selected branch steps"]
},
"touch_interaction": {
"gesture": "tap",
"canvas_action": "Tapping a condition node reveals its two branch arms; the arm whose predicate holds lights up.",
"visual": "A diamond node shows then/else arms; the true arm glows green and the false arm dims when evaluate_condition resolves."
},
"mini_scenario": "If tests succeed the condition lights the review branch; if they failed it lights the notify branch instead.",
"pitfall": "Branch steps are marked control-flow-managed so the top-level pass does not run them twice; forgetting that double-executes both arms."
},
{
"id": "orchestration-04",
"title": "Iterate a Loop step over a list",
"loop_stage": "control",
"pattern": "step-iteration",
"intent": "Run a body of steps once per item, keyed per iteration.",
"how_it_shapes_the_loop": "A Loop StepType splits its 'in' list and runs 'do' steps per item, storing each result as step_id@idx in the WorkflowContext, so the control layer expands one node into an indexed sequence.",
"loop_objects_touched": ["StepType", "WorkflowContext", "StepResult"],
"wiring": {
"inputs_from": ["iteration list", "loop variable"],
"outputs_to": ["per-iteration StepResults", "aggregate result"]
},
"touch_interaction": {
"gesture": "spread",
"canvas_action": "Spreading a loop node unrolls it into one body-instance per item laid out in a row.",
"visual": "The loop node fans into indexed instances; each runs and greens in turn, the aggregate node summing their states."
},
"mini_scenario": "A loop over three services runs the deploy body for each; results store as deploy@0, deploy@1, deploy@2.",
"pitfall": "Dependency lookup is iteration-aware (dep@idx first, then plain dep); resolving a dep without the index reads the wrong iteration's result."
},
{
"id": "orchestration-05",
"title": "Substitute variables shell-safely into a Shell step",
"loop_stage": "foundation",
"pattern": "safe-substitution",
"intent": "Inject context values into a shell command without opening an injection hole.",
"how_it_shapes_the_loop": "WorkflowContext::substitute_shell_safe POSIX-quotes interpolated values before a Shell StepType runs, so the foundation layer prevents a malicious variable from escaping the command.",
"loop_objects_touched": ["StepType", "WorkflowContext"],
"wiring": {
"inputs_from": ["WorkflowContext variables"],
"outputs_to": ["safely-quoted shell command"]
},
"touch_interaction": {
"gesture": "long-press",
"canvas_action": "Long-pressing a shell node highlights each interpolated token wrapped in a quote shield.",
"visual": "Variable tokens in the command render inside a shield capsule; an unsafe raw value flashes a warning before quoting."
},
"mini_scenario": "A step runs 'git clone ${repo_url}'; the URL is POSIX-quoted so a value with a semicolon cannot append a second command.",
"pitfall": "Plain WorkflowContext::substitute must never feed a Shell step; using it instead of substitute_shell_safe reintroduces command injection."
},
{
"id": "orchestration-06",
"title": "Enforce a Guardrail step that fails closed",
"loop_stage": "verify",
"pattern": "guardrail-fail-closed",
"intent": "Block the workflow when a safety condition is violated.",
"how_it_shapes_the_loop": "A Guardrail StepType evaluates its condition via the GuardrailEngine over a GuardrailContext; a block or unknown EvaluationResult returns an error, so the verify stage halts the loop rather than proceeding unsafely.",
"loop_objects_touched": ["StepType", "WorkflowContext"],
"wiring": {
"inputs_from": ["GuardrailContext built from WorkflowContext variables"],
"outputs_to": ["block (Err) or allow (Ok)"]
},
"touch_interaction": {
"gesture": "long-press",
"canvas_action": "Long-pressing a guardrail node runs its check; a violation slams a barrier across the outgoing edge.",
"visual": "A gate node lowers a red barrier on block, an amber caution on warn, and stays open green on pass."
},
"mini_scenario": "A guardrail checks the diff stays in scope; a violation returns Err with block severity and the workflow stops.",
"pitfall": "Unknown must fail closed like block; treating an unresolved guardrail as allow lets an unevaluated safety check pass silently."
},
{
"id": "orchestration-07",
"title": "Nest a SubWorkflow with cycle detection",
"loop_stage": "control",
"pattern": "sub-loop-nesting",
"intent": "Compose a workflow out of reusable sub-workflows without infinite recursion.",
"how_it_shapes_the_loop": "A SubWorkflow StepType executes with an appended WorkflowContext::workflow_call_stack and a recursion_depth cap, so the control layer nests sub-loops while detecting call cycles across workflow boundaries.",
"loop_objects_touched": ["StepType", "WorkflowContext", "Workflow"],
"wiring": {
"inputs_from": ["sub-workflow name", "mapped inputs"],
"outputs_to": ["merged sub-workflow outputs"]
},
"touch_interaction": {
"gesture": "double-tap",
"canvas_action": "Double-tapping a sub-workflow node dives into its inner canvas; a breadcrumb tracks the workflow_call_stack.",
"visual": "The node opens into a nested canvas; the breadcrumb trail grows, flashing red if a name repeats in the stack."
},
"mini_scenario": "A release workflow calls the build sub-workflow, whose outputs merge back; a self-call would trip the cycle guard.",
"pitfall": "The call stack must track names across boundaries; depth counting alone misses a cycle between two mutually-calling workflows."
},
{
"id": "orchestration-08",
"title": "Assign a SwarmTask to the highest-trust idle Agent",
"loop_stage": "act",
"pattern": "role-matched-assignment",
"intent": "Route each task to an available specialist most likely to do it well.",
"how_it_shapes_the_loop": "Swarm::assign_task matches a SwarmTask's required_roles to idle Agents by highest trust_score and sets them Working, so the act stage dispatches work to the fittest agent per role.",
"loop_objects_touched": ["Swarm", "SwarmTask", "Agent", "AgentRole"],
"wiring": {
"inputs_from": ["queued SwarmTask (crate::orchestration::swarm::coordinator::queue_task)", "idle Agents"],
"outputs_to": ["assigned agents", "task InProgress"]
},
"touch_interaction": {
"gesture": "drag",
"canvas_action": "Dragging a task tile onto the swarm auto-snaps it to the idle agent of the required role with the best trust_score.",
"visual": "The task tile flies to the chosen agent node, which lights Working; passed-over agents dim briefly."
},
"mini_scenario": "An architecture task requiring Architect and Security roles snaps to the two idle specialists with the highest trust scores.",
"pitfall": "Only idle agents are eligible; assigning to a Working agent creates a zombie task, so completion must return agents to Idle."
},
{
"id": "orchestration-09",
"title": "Recycle Agents back to Idle on task completion",
"loop_stage": "control",
"pattern": "agent-recycling",
"intent": "Return finished agents to the pool so future tasks can use them.",
"how_it_shapes_the_loop": "Swarm::complete_task records the result, updates the Agent's trust_score and task counts, then sets it Idle, so the control layer keeps the agent pool circulating instead of freezing agents in Completed.",
"loop_objects_touched": ["Swarm", "Agent", "SwarmTask"],
"wiring": {
"inputs_from": ["task result", "success flag"],
"outputs_to": ["Agent Idle", "task Completed"]
},
"touch_interaction": {
"gesture": "flick",
"canvas_action": "Flicking a Working agent that just finished sends it back to the idle pool with an updated trust badge.",
"visual": "The agent node flips from Working amber to Idle green; its trust badge ticks up on success or down on failure."
},
"mini_scenario": "A Coder finishes its task; complete_task bumps its trust_score, marks the task Completed, and returns it Idle for the next assignment.",
"pitfall": "Forgetting to set the agent Idle after completion leaves it stuck Completed forever, starving the pool of that AgentRole."
},
{
"id": "orchestration-10",
"title": "Open a Decision and collect weighted Votes",
"loop_stage": "reason",
"pattern": "weighted-consensus",
"intent": "Let the swarm decide among options with each vote weighted by expertise and trust.",
"how_it_shapes_the_loop": "Swarm::create_decision opens a Decision; each Agent's Vote carries weighted_value = confidence x role_priority x trust_score, so the reason stage aggregates opinions by earned authority.",
"loop_objects_touched": ["Decision", "Vote", "Agent", "AgentRole"],
"wiring": {
"inputs_from": ["question + options", "Agent Votes (Swarm::vote)"],
"outputs_to": ["resolved Decision outcome"]
},
"touch_interaction": {
"gesture": "tap",
"canvas_action": "Tapping each agent casts its vote onto the decision node; vote bars grow by their weighted_value.",
"visual": "Option bars fill as votes land, each segment sized by the voter's weight; the leading option pulses ahead."
},
"mini_scenario": "On an architecture choice, the Security agent's high-priority high-trust vote outweighs two low-confidence votes for the alternative.",
"pitfall": "weighted_value is confidence times priority times trust; dropping any factor lets a confident but untrusted agent sway the decision unfairly."
},
{
"id": "orchestration-11",
"title": "Resolve a conflicted Decision by ConflictStrategy",
"loop_stage": "verify",
"pattern": "conflict-resolution",
"intent": "Break a tie or clash of votes with an explicit resolution rule.",
"how_it_shapes_the_loop": "Swarm::resolve_conflict applies a ConflictStrategy (PriorityWins, ConfidenceWins, MajorityWins, HumanIntervention, AcceptAll), so the verify stage turns a Conflict Decision into a determinate outcome or escalates it.",
"loop_objects_touched": ["Decision", "ConflictStrategy", "Vote"],
"wiring": {
"inputs_from": ["conflicted Votes"],
"outputs_to": ["Decision outcome", "human escalation"]
},
"touch_interaction": {
"gesture": "two-finger-rotate",
"canvas_action": "Rotating the strategy dial on a conflicted decision recomputes the winner under each ConflictStrategy rule.",
"visual": "The dial cycles strategy labels; the winning option re-highlights per rule, HumanIntervention greys all and raises a hand icon."
},
"mini_scenario": "A split vote is resolved by PriorityWins, letting the Security agent's option win; switching to HumanIntervention would escalate instead.",
"pitfall": "HumanIntervention returns no automatic outcome; treating its None as a default choice silently picks for the human it meant to consult."
},
{
"id": "orchestration-12",
"title": "Coordinate agents through bounded SharedMemory",
"loop_stage": "foundation",
"pattern": "shared-blackboard",
"intent": "Let agents exchange intermediate results without direct coupling.",
"how_it_shapes_the_loop": "SharedMemory offers write/read/peek/delete with an access log bounded to a ring buffer, so the foundation layer gives the swarm a common blackboard that cannot grow unbounded.",
"loop_objects_touched": ["SharedMemory", "Agent", "Swarm"],
"wiring": {
"inputs_from": ["agent writes (SharedMemory::write)"],
"outputs_to": ["other agents' reads (SharedMemory::read)"]
},
"touch_interaction": {
"gesture": "draw-connection",
"canvas_action": "Drawing from an agent to the memory slab writes a keyed MemoryEntry; drawing from the slab to another agent reads it.",
"visual": "A memory slab shows keyed cells; writes drop glowing cells, reads pull threads out, the access log scrolling beneath."
},
"mini_scenario": "The Architect writes a design key to SharedMemory; the Coder reads it and its access_count increments in the log.",
"pitfall": "The access log is a bounded ring buffer; relying on it for a full audit trail loses the oldest MemoryAccess records once it rolls over."
},
{
"id": "orchestration-13",
"title": "Fan one completion out per Agent under a semaphore",
"loop_stage": "act",
"pattern": "budget-scoped-fanout",
"intent": "Run many specialist perspectives in parallel with bounded concurrency.",
"how_it_shapes_the_loop": "MultiAgentChat::run_task spawns one chat_completion per Agent gated by a tokio Semaphore sized to MultiAgentConfig::max_concurrency, so the act stage parallelizes perspectives without unbounded fan-out.",
"loop_objects_touched": ["MultiAgentChat", "Agent", "AgentResult"],
"wiring": {
"inputs_from": ["task", "MultiAgentConfig roles"],
"outputs_to": ["AgentResults", "MultiAgentEvent::AllCompleted"]
},
"touch_interaction": {
"gesture": "spread",
"canvas_action": "Spreading the task node forks one lane per agent; only max_concurrency lanes run at once, the rest queue for a permit.",
"visual": "Agent lanes light up in parallel up to the concurrency cap; queued lanes wait greyed until a permit frees."
},
"mini_scenario": "One prompt fans out to Reviewer, Tester, and Security agents; with concurrency 2, Security waits until a semaphore permit frees.",
"pitfall": "The semaphore bounds concurrency, not total spend; without the BudgetGuard the parallel fan-out can still blow the token budget."
},
{
"id": "orchestration-14",
"title": "Guard the fan-out spend with a BudgetGuard",
"loop_stage": "control",
"pattern": "shared-budget-guard",
"intent": "Cap total tokens and cost across all parallel agents.",
"how_it_shapes_the_loop": "MultiAgentChat wraps the fan-out in a BudgetGuard enforcing max_budget_tokens and max_cost_usd, so the control layer skips agents once the shared budget is exhausted.",
"loop_objects_touched": ["MultiAgentChat", "Agent", "AgentResult"],
"wiring": {
"inputs_from": ["budget limits", "per-agent token estimate"],
"outputs_to": ["AgentResult or skip (MultiAgentEvent::AgentFailed)"]
},
"touch_interaction": {
"gesture": "long-press",
"canvas_action": "Long-pressing the budget bar shows it draining as each agent reserves its estimate; agents past the line grey out.",
"visual": "A shared budget bar depletes with each launch; when it hits zero, remaining agent lanes stamp 'skipped: budget'."
},
"mini_scenario": "Four agents fan out but the budget covers three; the fourth is pessimistically skipped and emits AgentFailed instead of overspending.",
"pitfall": "Estimate pessimistically before launch; a naive optimistic estimate can admit an agent that then overshoots the shared cap."
},
{
"id": "orchestration-15",
"title": "Cancel remaining agents on first failure (FailFast)",
"loop_stage": "control",
"pattern": "fail-fast-cancellation",
"intent": "Stop wasting budget on siblings once one agent has failed the task.",
"how_it_shapes_the_loop": "Under MultiAgentFailurePolicy::FailFast, the first agent error signals a shared tokio::sync::Notify that cancels the remaining tasks, so the control layer aborts the fan-out early.",
"loop_objects_touched": ["MultiAgentChat", "MultiAgentEvent", "AgentResult"],
"wiring": {
"inputs_from": ["first agent failure"],
"outputs_to": ["cancellation of remaining agents"]
},
"touch_interaction": {
"gesture": "flick",
"canvas_action": "A failing lane flicks a cancel wave across the sibling lanes, halting them mid-run.",
"visual": "The failed lane flashes red and a cancellation ripple sweeps the row; sibling lanes freeze and grey out."
},
"mini_scenario": "With FailFast set, the Tester agent errors; the Notify fires and the still-running Reviewer and Security lanes cancel.",
"pitfall": "FailFast trades completeness for cost; choosing it when partial results matter throws away work BestEffort would have kept."
},
{
"id": "orchestration-16",
"title": "Stream MultiAgentEvents as agents progress",
"loop_stage": "perceive",
"pattern": "event-streaming",
"intent": "Observe the fan-out live rather than waiting for the final batch.",
"how_it_shapes_the_loop": "MultiAgentChat emits MultiAgentEvents (AgentStarted, AgentCompleted, AgentFailed, AllCompleted) over an mpsc channel, so the perceive stage watches the parallel loop unfold in real time.",
"loop_objects_touched": ["MultiAgentEvent", "AgentResult", "MultiAgentChat"],
"wiring": {
"inputs_from": ["agent lifecycle transitions (MultiAgentChat::with_events)"],
"outputs_to": ["UI stream", "monitoring"]
},
"touch_interaction": {
"gesture": "tap",
"canvas_action": "Tapping the event stream node opens a live feed of agent lifecycle events scrolling as they fire.",
"visual": "An event ticker scrolls: green Started/Completed rows, red Failed rows, a final AllCompleted banner summing durations."
},
"mini_scenario": "The user taps the stream and watches AgentStarted then AgentCompleted rows arrive per agent, ending in AllCompleted.",
"pitfall": "Events use non-blocking try_send; if the channel fills, events drop, so the stream is a view, not a guaranteed-complete log."
},
{
"id": "orchestration-17",
"title": "Iterate a VisualFeedbackLoop to a quality threshold",
"loop_stage": "verify",
"pattern": "iterate-to-threshold",
"intent": "Refine a visual output until a critic scores it good enough.",
"how_it_shapes_the_loop": "VisualFeedbackLoop runs act-capture-evaluate until VisualScore.overall meets quality_threshold or max_iterations, so the verify stage closes a self-correcting visual sub-loop.",
"loop_objects_touched": ["VisualFeedbackLoop", "VisualScore", "VisualLoopResult"],
"wiring": {
"inputs_from": ["captured render (CaptureMethod)", "vision model critic"],
"outputs_to": ["VisualLoopResult", "next iteration or stop"]
},
"touch_interaction": {
"gesture": "spread",
"canvas_action": "Spreading the visual loop unrolls its iterations; each shows a capture thumbnail and its score climbing toward the line.",
"visual": "A row of iteration thumbnails rises in score; the loop stops and glows green when a thumbnail crosses the threshold line."
},
"mini_scenario": "A UI render scores 72 then 81; the second iteration crosses the 0.8 quality_threshold and the loop returns a VisualLoopResult with threshold_met true.",
"pitfall": "max_iterations must cap the loop; a strict threshold the critic never reaches spins forever without the iteration ceiling."
},
{
"id": "orchestration-18",
"title": "Parse a critic response into a dimensioned VisualScore",
"loop_stage": "reason",
"pattern": "structured-critique",
"intent": "Turn a vision model's prose into per-dimension scores plus suggestions.",
"how_it_shapes_the_loop": "parse_critic_response tolerates markdown fences and extracts a VisualScore (composition, hierarchy, readability, consistency, accessibility, weighted overall via compute_overall), so the reason stage gets actionable per-axis feedback.",
"loop_objects_touched": ["VisualScore", "VisualFeedbackLoop"],
"wiring": {
"inputs_from": ["vision model response (build_critic_prompt answer)"],
"outputs_to": ["VisualScore", "next-iteration prompt"]
},
"touch_interaction": {
"gesture": "double-tap",
"canvas_action": "Double-tapping a critique node breaks it into five dimension gauges plus a suggestions list.",
"visual": "Five radial gauges (composition, hierarchy, readability, consistency, accessibility) fill; a suggestions panel lists fixes below."
},
"mini_scenario": "The critic returns markdown-fenced JSON; parse_critic_response extracts accessibility 62 and the suggestion 'add focus indicators'.",
"pitfall": "Overall is a weighted average of the axes (compute_overall weights readability 0.25); recomputing it with equal weights misreports quality the loop then chases wrongly."
},
{
"id": "orchestration-19",
"title": "Shed load when the Swarm hits resource pressure",
"loop_stage": "control",
"pattern": "pressure-backpressure",
"intent": "Refuse new tasks when the swarm is already saturated.",
"how_it_shapes_the_loop": "Swarm::queue_task checks crate::resource::ResourcePressure and fails to enqueue when it is High or Critical, so the control layer applies backpressure instead of overcommitting agents.",
"loop_objects_touched": ["Swarm", "SwarmTask"],
"wiring": {
"inputs_from": ["resource pressure signal (set_resource_pressure)"],
"outputs_to": ["queued task or rejection"]
},
"touch_interaction": {
"gesture": "long-press",
"canvas_action": "Long-pressing the queue shows a pressure gauge; at Critical the intake slot slams shut and rejects the drop.",
"visual": "A pressure gauge rides green to red; at High/Critical the queue intake flashes closed and bounces the incoming task tile."
},
"mini_scenario": "Under Critical pressure the user tries to queue a task; queue_task rejects it so the overloaded swarm is not pushed further.",
"pitfall": "Backpressure must reject at intake, not mid-run; admitting a task under Critical pressure just moves the overload downstream."
},
{
"id": "orchestration-20",
"title": "Sweep timed-out Decisions to keep the swarm live",
"loop_stage": "control",
"pattern": "timeout-sweep",
"intent": "Prevent pending decisions from blocking the swarm indefinitely.",
"how_it_shapes_the_loop": "Swarm::sweep_timed_out_decisions marks Decisions older than decision_timeout_secs (default 300, set via with_decision_timeout) as TimedOut, so the control layer unblocks agents waiting on a vote that never resolved.",
"loop_objects_touched": ["Decision", "Swarm"],
"wiring": {
"inputs_from": ["pending Decisions", "decision_timeout_secs"],
"outputs_to": ["TimedOut Decisions", "unblocked agents"]
},
"touch_interaction": {
"gesture": "flick",
"canvas_action": "Flicking across the decision board sweeps stale pending decisions, stamping the overdue ones TimedOut.",
"visual": "Pending decision cards past their age flash and stamp a grey 'TimedOut' seal; agents waiting on them release."
},
"mini_scenario": "A decision sits unresolved past its 300s timeout; the sweep marks it TimedOut so the agents holding for its outcome move on.",
"pitfall": "A TimedOut decision has no consensus outcome; downstream logic must handle the timeout branch rather than assume a resolved choice."
}
]
}