CompositeLoop Regions¶
A composite_loops[] entry is an explicit owner for one immediate cyclic strongly connected component (SCC). It is not a decorator: validation rejects partial loop declarations, decorative loop declarations, duplicate ownership, and immediate cycles without an exact loop owner.
Region model¶
The graph compiler condenses an accepted loop SCC into one CompositeLoop region. The runtime executes components inside that region in compiled order for a bounded number of loop-local iterations.
Current loop policies are intentionally small:
max_iterations: hard bound; omitted or non-positive means one iteration at runtime.convergence:single_pass,after_first_iteration, oralwaysstops after one iteration and records convergence;stable_stateremains an accepted advisory value for existing examples.solver_iteration: a bounded policy for iterative components. Components can callGraphContext::report_loop_convergence({converged, residual, reason})during execution. The runtime exposes the loop id, zero-based iteration index, one-based iteration number, policy type, and max iteration count throughGraphContext::loop_iteration.residual_threshold: optional non-negative threshold. A reported residual at or below this threshold marks asolver_iterationloop converged even when the report did not setconverged.partial_success:commit_outputs,discard_outputs, orfail_run.solver_iterationdefaults todiscard_outputswhen it stops without convergence; existing fixed-point/transaction-style policies default tocommit_outputsfor compatibility.budget_ms: stops the loop when the cumulative loop duration exceeds the budget.- cooperative cancellation: a requested cancellation is observed between loop iterations, never by interrupting the component currently executing.
No dynamic solver plugins or external optimization libraries are involved; the typed report API is in-process component code.
Transaction and visibility¶
Publications inside the loop still go through GraphContext::publish() and the runtime publication router. Outputs from loop-owned components to components outside the loop are staged as CompositeLoop external outputs and committed only after the loop finishes successfully, or after an explicit partial_success: commit_outputs policy allows a non-converged solver stop to publish them. If a staged external output is an async completion and the loop later discards it, async in-flight accounting is released and the discard is observable through CompositeLoop/publication metrics.
If an internal component fails, the loop stops with SchedulerStopReason::kError, records runtime.loop.error, emits a loop_error trace event, and does not commit staged external loop outputs. This prevents downstream components from observing half-updated loop state.
Metrics and trace¶
CompositeLoop metrics use the loop id as component_id in RuntimeMetricSample:
runtime.loop.iterationsruntime.loop.convergedruntime.loop.budget_overrunruntime.loop.max_iterations_hitruntime.loop.errorruntime.loop.cancellation_requestedruntime.loop.cancellation_observedruntime.loop.output_discardedruntime.loop.residual
Trace events:
loop_iteration_beginloop_iterationloop_iteration_endloop_convergedloop_budget_overrunloop_max_iterations_hitloop_output_discardedloop_errorloop_cancellation_requestedloop_cancellation_observed
loop_iteration* and loop_converged events may include policy, residual,
residual_threshold, and reason attributes. These events are sufficient to
explain bounded iteration, convergence, partial-output discard, budget stop,
cooperative cancellation, and internal failure paths without source-level
debugging.