You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
PR #221 (closed-loop step 9, apex-driver outer loop) introduced a new conductor pattern that is not currently documented in the conductor-mechanics skill:
Pattern: iterate-until-stable loop
Conductor's only built-in loop primitive is for_each over a fixed collection. PR #221's apex-driver outer loop needed an unbounded "keep dispatching waves until either (a) the apex is satisfied, (b) we've made no progress, or (c) we've hit a configured cap" loop. Implemented as:
Loop counter as a temp-file side-channel. Each script step is a fresh PowerShell process, so per-iteration state has to live on disk. apex-driver.yaml writes apex-driver-iter-{apex_id} under [System.IO.Path]::GetTempPath() from an outer_loop_init step (resets to 0 once after declare_root), and outer_loop_evaluator increments it.
Loop body as a graph cycle.outer_loop_evaluator.routes includes - to: build_worklist with when: \"{{ outer_loop_evaluator.output.decision == 'continue' }}\". This is an explicit cycle in the agent graph — conductor's strict-undefined / M3 rules do not forbid cycles, but lint-strict-undefined and reachability checks must continue to pass.
4-way decision route. Strict priority order via successive when: clauses on the same step (complete > cap > blocked > continue), with M4 catch-all defaulting to a non-loop terminal (terminal_apex_blocked) so a malformed envelope can't infinite-loop.
Cap configurable via env var.POLYPHONY_APEX_MAX_DISPATCH_ITERATIONS overrides the default 10. The cap is a safety net, not a performance ceiling — state is on disk + ADO so resume after cap-hit is honest.
Why it belongs in conductor-mechanics
This is a runtime/plumbing pattern (not a design principle): a future workflow author who needs the same "iterate until stable" shape needs to know about the temp-file counter idiom, the cycle-with-defensive-catch-all rule, and the priority-ordered when: decision routing. Without docs they'll either reinvent it incompatibly or assume conductor has a primitive it doesn't.
Suggested home: a new reference like references/m10-graph-cycles.md (or a section under references/m04-routing-rules.md if cycles are considered a routing concern). Cross-link from polyphony-workflow-author / polyphony-actionable.
.conductor/registry/workflows/apex-wave-dispatch.yaml — aggregate_renegotiation extension producing the per-iteration progress counters the evaluator consumes.
.conductor/registry/tests/e2e-apex-driver.Tests.ps1 Section 2b — the structural assertions that pin the pattern (M4 catch-all defaults to a terminal, four decisions are present, every cycle node is reachable from entry).
Out of scope for this issue
Whether conductor itself should grow a first-class while / until-stable primitive. That's a conductor change, not a skill update. Capture the question separately if it's worth pursuing.
Concurrency safety of the temp-file counter (two parallel apex-driver invocations on the same apex_id would race). Documented as a known characteristic in the PR Closed-loop step 9: apex-driver outer loop (Option α) #221 body; revisit if/when apex-driver becomes multi-tenant per apex.
PR #221 (closed-loop step 9, apex-driver outer loop) introduced a new conductor pattern that is not currently documented in the
conductor-mechanicsskill:Pattern: iterate-until-stable loop
Conductor's only built-in loop primitive is
for_eachover a fixed collection. PR #221's apex-driver outer loop needed an unbounded "keep dispatching waves until either (a) the apex is satisfied, (b) we've made no progress, or (c) we've hit a configured cap" loop. Implemented as:apex-driver.yamlwritesapex-driver-iter-{apex_id}under[System.IO.Path]::GetTempPath()from anouter_loop_initstep (resets to 0 once afterdeclare_root), andouter_loop_evaluatorincrements it.outer_loop_evaluator.routesincludes- to: build_worklistwithwhen: \"{{ outer_loop_evaluator.output.decision == 'continue' }}\". This is an explicit cycle in the agent graph — conductor's strict-undefined / M3 rules do not forbid cycles, but lint-strict-undefined and reachability checks must continue to pass.when:clauses on the same step (complete>cap>blocked>continue), with M4 catch-all defaulting to a non-loop terminal (terminal_apex_blocked) so a malformed envelope can't infinite-loop.POLYPHONY_APEX_MAX_DISPATCH_ITERATIONSoverrides the default10. The cap is a safety net, not a performance ceiling — state is on disk + ADO so resume after cap-hit is honest.Why it belongs in conductor-mechanics
This is a runtime/plumbing pattern (not a design principle): a future workflow author who needs the same "iterate until stable" shape needs to know about the temp-file counter idiom, the cycle-with-defensive-catch-all rule, and the priority-ordered
when:decision routing. Without docs they'll either reinvent it incompatibly or assume conductor has a primitive it doesn't.Suggested home: a new reference like
references/m10-graph-cycles.md(or a section underreferences/m04-routing-rules.mdif cycles are considered a routing concern). Cross-link frompolyphony-workflow-author/polyphony-actionable.Concrete examples to cite
.conductor/registry/workflows/apex-driver.yaml—outer_loop_init(counter reset),outer_loop_evaluator(counter increment + decision + cycle route),terminal_apex_iteration_cap/terminal_apex_blockedterminals..conductor/registry/workflows/apex-wave-dispatch.yaml—aggregate_renegotiationextension producing the per-iteration progress counters the evaluator consumes..conductor/registry/tests/e2e-apex-driver.Tests.ps1Section 2b — the structural assertions that pin the pattern (M4 catch-all defaults to a terminal, four decisions are present, every cycle node is reachable from entry).Out of scope for this issue
while/until-stableprimitive. That's a conductor change, not a skill update. Capture the question separately if it's worth pursuing.apex-driverinvocations on the sameapex_idwould race). Documented as a known characteristic in the PR Closed-loop step 9: apex-driver outer loop (Option α) #221 body; revisit if/when apex-driver becomes multi-tenant per apex.cc closed-loop-state-plan §3.4 + §4 PR #9.