Following the Turn Loop Controller plan in CONTRIBUTOR_TASKS.md (P0 slices), I would like to implement the first slice: the pure transition contract.
Scope (first PR, deliberately narrow)
A pure decision-table module, e.g. loopx/control_plane/turn_driver/loop_controller.py, exposing one function:
decide_next_disposition(turn_receipt, quota_decision) -> typed disposition
Input: one Turn receipt (loopx_turn_receipt_v0 result kind + validation status + terminal policy) plus a fresh quota/scheduler decision (loopx_turn_envelope_v0).
Output: exactly one typed disposition — run_now / wait / user_action_required / repair / replan / terminal — each with a reason code and a spends_quota: false marker.
The pure transition does not invoke a model, sleep, mutate a host scheduler, write state, or spend quota. Scheduler process management, host-specific wake APIs, and operator presentation stay out (later P1/P2 adapters).
Draft decision table (for review before implementation)
| receipt |
fresh decision |
disposition |
| validated_completion (terminal postcondition met) |
any |
terminal |
| validated_progress, bounded budget remaining |
should_run + delivery allowed |
run_now |
| validated_progress, bounded budget exhausted |
any |
terminal (bounded stop) |
| repair_required result |
any |
repair |
| replan_required result |
any |
replan, and payload requires a bounded todo/vision delta before any successor Turn (never rerun the same stale todo just because a host session is resumable) |
| user_action_required (receipt or decision) |
— |
user_action_required with the projected action, no host run, no spend |
| quiet_noop_allowed / cadence-only decision |
— |
wait, no spend |
| stale receipt (lineage mismatch with fresh decision) |
— |
fail closed: wait with stale_receipt reason (or contract error; happy to align on which) |
| malformed/unknown receipt or decision |
— |
fail closed with contract error |
Open question I would like maintainer guidance on: for the stale/malformed cases, should the disposition be wait with a reason code or a distinct contract_error disposition? The task board lists six dispositions, so I lean toward wait + explicit reason codes unless you prefer a seventh.
Validation plan
- focused decision-table pytest covering each row plus boundary rows (progress at budget limit, replan without delta, stale lineage)
python -m pytest -q tests/test_loopx_turn_driver.py tests/test_loopx_turn_executor.py tests/test_loopx_turn_transaction.py
python examples/autonomous-replan-obligation-smoke.py
loopx check --scan-path CONTRIBUTOR_TASKS.md --scan-path CONTRIBUTING.md
git diff --check
Refs: #2366 (design), #2365 (host-mode selector), CONTRIBUTOR_TASKS GH-C72.
Following the Turn Loop Controller plan in
CONTRIBUTOR_TASKS.md(P0 slices), I would like to implement the first slice: the pure transition contract.Scope (first PR, deliberately narrow)
A pure decision-table module, e.g.
loopx/control_plane/turn_driver/loop_controller.py, exposing one function:Input: one Turn receipt (
loopx_turn_receipt_v0result kind + validation status + terminal policy) plus a fresh quota/scheduler decision (loopx_turn_envelope_v0).Output: exactly one typed disposition —
run_now/wait/user_action_required/repair/replan/terminal— each with a reason code and aspends_quota: falsemarker.The pure transition does not invoke a model, sleep, mutate a host scheduler, write state, or spend quota. Scheduler process management, host-specific wake APIs, and operator presentation stay out (later P1/P2 adapters).
Draft decision table (for review before implementation)
terminalrun_nowterminal(bounded stop)repairreplan, and payload requires a bounded todo/vision delta before any successor Turn (never rerun the same stale todo just because a host session is resumable)user_action_requiredwith the projected action, no host run, no spendwait, no spendwaitwithstale_receiptreason (or contract error; happy to align on which)Open question I would like maintainer guidance on: for the stale/malformed cases, should the disposition be
waitwith a reason code or a distinctcontract_errordisposition? The task board lists six dispositions, so I lean towardwait+ explicit reason codes unless you prefer a seventh.Validation plan
python -m pytest -q tests/test_loopx_turn_driver.py tests/test_loopx_turn_executor.py tests/test_loopx_turn_transaction.pypython examples/autonomous-replan-obligation-smoke.pyloopx check --scan-path CONTRIBUTOR_TASKS.md --scan-path CONTRIBUTING.mdgit diff --checkRefs: #2366 (design), #2365 (host-mode selector), CONTRIBUTOR_TASKS GH-C72.