Compute Quota¶
LoopX should own compute allocation across projects. The first version should stay deliberately simple: each goal gets one compute quota number, and automations or controller ticks use that number to decide how often the goal may consume agent time.
This replaces the current ad hoc pattern where priority is encoded only by changing automation periods. A timer can wake the executor, but the product policy should live in LoopX.
Product Scope¶
In v0.1, quota means compute quota only.
It does not decide human reward, write approval, production permission, or operator gate outcomes. Those remain separate LoopX states.
Compute quota answers one question:
Out of the available automatic agent time, how much should this goal be allowed to consume?
Examples:
1.0: full duty cycle. If the controller checks hourly, this goal is eligible on every healthy check for the full 24-hour window. With the default minute-granularity accounting,1.0means24 * 60 = 1440automatic compute minute-slots.0.5: half duty cycle, roughly 12 hours per day or half of scheduling minute-slots.0.3: 30% duty cycle, roughly 7.2 hours per day or 30% of scheduling minute-slots.0: compute-paused. This is a Goal-level hard pause: the goal stays visible but receives no automatic Codex turns, andquota should-runreturns a single authoritative paused contract. Because the pause is a typed terminal decision evaluated before any selector lane is built, no capability-bridge, workspace, replan, monitor, or inbox lane can leave a contradicting execution signal underneath it (should_run=false, all automatic permissions false,DONT_NOTIFY, scheduler neverrun_now, no quota spend). Set it withloopx configure-goal --quota-compute 0; negative values are rejected.
This is different from a single agent's monitor_only work mode.
quota.compute=0 pauses the whole Goal for every agent and every lane;
monitor_only is a per-agent lane state where one agent still runs bounded
read-only monitor polls while the Goal itself remains active for other work.
The number can be interpreted as either a duty cycle or a relative weight, depending on the executor:
- per-goal automations can turn
0.5into "run on about half the ticks"; - a shared controller loop can use the same number as a weighted selection ratio between eligible goals.
Minimal Contract¶
The compact status shape can start with a small object:
{
"quota": {
"compute": 0.5,
"window_hours": 24,
"slot_minutes": 1,
"allowed_slots": 720,
"spent_slots": 240,
"state": "eligible",
"next_eligible_at": "2026-06-02T12:00:00+08:00",
"reason": "0.5 compute quota, 240/720 minute-slots spent in the current window"
}
}
Registry entries may declare the same policy directly:
{
"quota": {
"compute": 0.5,
"window_hours": 24
},
"control_plane": {
"self_repair": {
"enabled": false,
"allow_health_blocker_repair": false,
"allow_waiting_projection_repair": false
}
}
}
If quota.compute is missing, status treats the goal as 1.0 by default so a
newly connected project remains eligible unless a harder gate blocks it.
slot_minutes is optional and defaults to 1. The default allowed_slots is
computed as window_hours * 60 / slot_minutes * compute, so compute=1.0 is
already the full available 24-hour duty cycle. Operators should only set
allowed_slots explicitly for exceptional overrides such as a temporary burst,
a deliberately stricter experimental cap, or a non-minute scheduler. It should
not be required to express normal full quota.
For the first implementation, spent_slots counts compact automatic compute
budget units. Under the default minute granularity, one slot means one minute of
automatic compute budget. Minute-based heartbeat continuations can spend
--slots 1; coarser controllers should spend the number of scheduler minutes
they actually reserve. It does not need to count exact tokens yet.
Status derives the current spent_slots from compact quota_slot_spent
runtime events in the current window_hours window. The registry remains the
policy source for compute, window_hours, and optional allowed_slots; it is
not the spend ledger.
Later versions can replace slots with real runtime, token spend, or cost, but the operator-facing model should remain the same: a project has a simple compute share.
State Order¶
Quota is applied after hard gates:
- Health and safety gates: broken registry, contract failures, or unsafe boundary issues block the goal.
- Operator gates: read-only opt-in, write-control, reward judgment, and production actions still require explicit human decisions.
- Evidence waits: a goal waiting on external metrics or target-controller response should not consume delivery compute just because quota remains.
- Focus waits: a goal can still be Codex-owned while the current delivery lane is saturated or waiting for novelty, owner evidence, external eval, or a clean baseline. It should stay visible but should not spend delivery compute only because compute quota remains.
- Compute quota: among goals that are otherwise eligible, quota decides whether this one should receive the next automatic turn.
This keeps the model small. Quota does not become a second permission system.
Control Plane Settings¶
Registry entries can carry per-goal control_plane policy alongside quota.
These settings are the control surface for behavior that would otherwise be
tempting to encode in a heartbeat prompt.
Current self-repair settings:
control_plane.self_repair.enabled: defaultfalse.control_plane.self_repair.allow_health_blocker_repair: when enabled, a goal may spend one bounded turn repairing LoopX health or contract blockers that prevent normal delivery.control_plane.self_repair.allow_waiting_projection_repair: when enabled, a goal instate=waitingwith no concrete waiting owner but with a current action or agent backlog may spend one bounded turn repairing the projection or writing back the concrete blocker.
Self-repair is deliberately opt-in per goal. A short heartbeat can read the
machine contract from quota should-run, but ordinary goals without this
registry policy stay in their existing skip, waiting, or health-blocked lanes.
Independent of opt-in health/projection repair, status may project an
autonomous_replan_obligation when active state or public run history shows 2
consecutive stalled monitor/no-progress turns. In that case
execution_obligation.kind=autonomous_replan_required and
must_attempt_work=true; launchers should run a bounded replan slice before
another quiet no-op, then spend only after validated writeback.
Allocation Contract¶
quota plan reports an advisory next automatic turn. It does not grant
permission, clear an operator gate, record human reward, or authorize a project
agent to run work that would otherwise be blocked.
The allocation rule is intentionally small:
- derive quota groups from the same status payload used by
loopx status; - keep
blocked_health,operator_gate,focus_wait,waiting,throttled, andpausedgoals in their own lanes, even when they have a highquota.compute; - only goals with
state=eligibleenter the eligible lane; - sort eligible goals by effective
quota.compute, highest first; - set
summary.next_automatic_turnto the first eligible goal, ornonewhen the eligible lane is empty.
Automations and controllers should treat next_automatic_turn as a scheduling
hint, then ask quota should-run --goal-id <goal-id> immediately before
spending compute. If the guard returns should_run=false, the executor should
skip the blocked delivery work and follow the reported health, operator,
evidence, focus-wait, pause, or throttle reason. When state=operator_gate also
returns safe_bypass_allowed=true, the target heartbeat may do one bounded
read-only steering or analysis step that does not depend on that gate, but it
must not execute agent_command, adapter work, write-control, production
actions, or the gated path.
When state=focus_wait is caused by a connected-delivery outcome floor and the
payload returns safe_bypass_kind=outcome_floor_recovery, the goal is not free
to resume ordinary delivery. It may spend one bounded recovery turn only to
produce the required ranker/cross-domain evidence artifact named by
quota.must_advance, or to write back the concrete blocker that prevents that
artifact. Surface-only summary/queue/contract propagation and synthetic-only
test chains remain blocked.
When control_plane.self_repair.enabled=true and the selected goal is stalled
by a repairable control-plane condition, quota should-run may instead return
decision=self_repair, self_repair_allowed=true, stall_self_repair, and an
effective_action such as control_plane_health_repair or
control_plane_projection_repair. This makes "repair the control plane" a
machine-readable bounded action for the selected goal, not a prompt-specific
branch. Spend is allowed only after validation and durable writeback.
Delivery outcome is a structured enum consumed by quota, not a suffix inferred
from classification. New writes should use one of:
| Value | Quota meaning |
|---|---|
surface_only |
Useful surface work, but not primary result evidence; may trigger follow-through when an advancement todo remains. |
outcome_gap |
The run exposed a concrete blocker or missing outcome; follow-up should advance the result or write a precise blocker. |
outcome_progress |
Accountable delivery progress that may be eligible for spend after validation/writeback. |
primary_goal_outcome |
The selected stage's primary outcome is complete; it satisfies outcome-floor recovery. |
classification remains a human/history label. Quota may use old
execution-profile hints only as a compatibility fallback for historical runs;
new control-plane decisions should be driven by the enum above.
quota should-run also separates long-running observation from work that should
advance the selected goal. When the selected goal's current projection is a
dependency-only observation, the payload includes work_lane_contract with
lane=continuous_monitor. If open agent todos remain, schema
work_lane_contract_v1 now classifies todo items as
task_class=advancement_task or task_class=continuous_monitor. Agents should
register executable work with loopx todo add --task-class
advancement_task --action-kind <token> instead of depending on project-specific
phrases in the automation prompt. The guard treats explicit task_class as
authoritative, uses recognized generic action_kind tokens as the next best
signal, and falls back to conservative text classification only for older
checkboxes. It sets
next_lane=advancement_task,
obligation=advance_unless_material_monitor_transition,
must_attempt_work=true, and reason codes such as dependency_observation and
open_agent_todo only when at least one open todo is advancement-class work.
If all visible open todos are monitor-class and no open todo is hidden, the
same contract uses obligation=quiet_until_material_monitor_transition and
must_attempt_work=false. Hidden open todos are treated as advancement work to
avoid false quiet no-ops from a truncated top-N todo projection.
Open agent todos that explicitly say not to run/launch until owner evidence,
credentials, substrate proof, or another prerequisite is present are
monitor-class blockers rather than executable advancement work; with an open
user todo they should keep that todo visible in user_todo_summary without
turning an unchanged eligible monitor poll into a blocker-push notification.
If the selected goal's next_action/recommended_action explicitly points to
an executable chain, such as collecting or aggregating repeats and then
rebuilding labels, rerunning a scorer, or validating an eval gate,
monitor-only todos are not allowed to make the goal quiet. The contract instead uses
obligation=materialize_advancement_todo_or_blocker and
must_attempt_work=true, so the worker must either materialize the concrete
advancement todo or write the blocker that prevents it.
heartbeat_recommendation should then say follow_work_lane_contract or
monitor_quiet_until_material_transition instead of encoding another
project-specific branch. An executor may still record one material
dependency-state transition when it changes the selected goal decision, but
unchanged monitor polls are quiet no-spend checks with should_run=false and
effective_action=monitor_quiet_skip. This keeps monitoring useful without
letting it consume every eligible turn, and it keeps the hard routing rule in
one small machine contract.
When the current agent lane has only monitor work and no current, unclaimed, or
other-agent advancement frontier, a future next_due_at is not enough to make
the lane quiet. Quota must first project autonomous_replan_required unless a
recent same-agent autonomous replan ACK explicitly carries
watch_lane_continuation. Generic frontier deltas such as a vision patch,
no-follow-up rationale, or active next action may settle their own replan
obligations, but cannot silently convert an empty active-goal frontier into a
monitor wait. Projected ACKs from a different agent lane are diagnostic only
for this decision and cannot clear the current lane's empty-frontier obligation.
Executable todos can also declare explicit write-scope requirements through
todo metadata, for example required_write_scopes=runner%2F%2A%2A or the CLI
flag loopx todo add --required-write-scope runner/**. Before normal
delivery, quota should-run compares the first executable advancement todo's
required_write_scopes with goal_boundary.write_scope. If the current
boundary does not cover the selected scope, the guard keeps should_run=true
for a bounded repair turn but sets normal_delivery_allowed=false,
effective_action=boundary_projection_repair, and
blocked_action_scope=boundary_projection. The worker must repair the
checkpointed boundary projection, rewrite the todo inside the current boundary,
or write a concrete user/controller gate before attempting the write.
Executable todos can also declare environment capability requirements through
todo metadata, for example required_capabilities=shell%2Cbenchmark_runner or
the CLI flag loopx todo add --required-capability benchmark_runner.
This is not a global agent profile and not a permission system. It is a
per-todo execution preflight so the guard can distinguish "quota is available"
from "this step can actually run in the current environment."
Use required_capabilities for prerequisites, not for the capability the todo
is intended to create. A todo that repairs, develops, materializes, or
parity-checks a bridge capability should declare that output side with
target_capabilities. Target capabilities are projected for visibility and
repair-mode routing, but they are not hard execution gates.
quota should-run compares the visible executable advancement queue with the
current launcher capabilities. Basic local capabilities such as shell,
filesystem_read, and filesystem_write are assumed by default; launchers can
add temporary capabilities with --available-capability, for example:
loopx --format json quota should-run \
--goal-id <goal-id> \
--available-capability benchmark_runner
Use the same --available-capability flags for quota spend-slot after a
validated turn, because spend preview recomputes the same should-run guard
before writing quota accounting.
The resulting capability_gate is a read-only projection:
{
"schema_version": "capability_gate_v0",
"action": "run",
"required": ["shell", "filesystem_write"],
"missing": [],
"decision_owner": "agent",
"selection_policy": "agent_steering_audit_over_runnable_candidates",
"runnable_candidates": [
{"todo_id": "todo_docs"}
],
"blocked_candidates": [
{
"todo_id": "todo_eval",
"required_capabilities": ["shell", "benchmark_runner"],
"missing_capabilities": ["benchmark_runner"]
}
]
}
Multiple P0/P1 items are handled as an ordered queue, not as a single selected
todo. The guard scans visible executable candidates in projection order and
projects which candidates are actually runnable in runnable_candidates; the
agent then chooses which runnable item to advance during its steering audit. A
runnable P0 remains visible before any P1 fallback, but LoopX does not
turn that ordering into an automatic final choice. Blocked higher-priority
candidates stay visible in blocked_candidates. If every visible executable
candidate is missing a capability, the gate returns
action=repair_bridge for repairable local bridges such as
benchmark_runner/external_evidence_poll and runtime provisioning such as
network, action=ask_owner for owner-held capabilities such as
credentials/production_access, or
action=skip for unsupported capability classes.
Blocked candidates retain typed resolution_bindings even when another todo is
runnable. Each binding names the capability, its resolution owner, and the
exact blocked_todo_ids. The interaction contract turns an owner-held binding
into an idempotent scoped user_gate write linked by unblocks_todo_id, while
an agent-repairable binding becomes an idempotent advancement todo with the
capability in target_capabilities. The user gate does not block unrelated
runnable todos. quota should-run remains read-only; the agent or host executes
the projected todo write before normal writeback.
Completing a repair or owner todo is not proof that the runtime capability is
available. The current host must still verify the real callsite and pass the
capability through --available-capability on both preflight and spend. This
keeps capability truth session-scoped and prevents stale persistent grants.
Runtime capability absence is not permission authority. A missing network
declaration therefore remains in the agent repair lane: the agent should
observe or repair the launcher/runtime bridge, truthfully pass
--available-capability network once verified, or write a concrete runtime
blocker. LoopX must not turn that condition into a user approval request.
When a missing repairable bridge is itself the target of the todo, for example
required_capabilities=shell plus target_capabilities=benchmark_runner, the
candidate remains in runnable_candidates with capability_repair_mode=true,
capability_action=repair_bridge, and candidate-local
missing_target_capabilities. This avoids a circular gate where a todo cannot
develop benchmark_runner because it does not already have benchmark_runner.
Multi-agent handoffs use the same ordering as all other agent todos. Review is
an action_kind, not a scheduling rank. excluded_agents can prevent named
peers from claiming or executing an otherwise ordinary handoff, while
unblocks_todo_id records dependency lineage. Active-next alignment, claims,
priority, capability gates, write scopes, user gates, and validation continue
to determine the executable action.
Deferred todo visibility is a separate gate-resume lane, not executable
backlog and not part of the no-candidate quiet-wait semantics. Status/quota may
expose up to eight sorted deferred_items and up to eight ready
deferred_resume_candidates after the sorted open todo lanes. In agent-scoped
quota should-run --agent-id <peer>, all deferred items may remain
visible for diagnosis, but only ready candidates claimed by the current agent
or left unclaimed can wake that peer. If such a candidate exists and no
open current-agent/unclaimed advancement todo exists, quota returns
effective_action=successor_replan_required, normal_delivery_allowed=false,
and execution_obligation.contract = deferred_resume_projection. The worker
must reopen, supersede, or record a public-safe no-follow-up rationale before
ordinary delivery work. Only when no ready current-agent/unclaimed deferred
resume exists should agent-scoped quota fall through to agent_scope_wait,
reassignment_required, or scope_exhausted.
Priority remains authoritative across the resume boundary. When a ready
current-agent or unclaimed deferred successor has strictly higher priority than
the selected open advancement todo, quota also returns
successor_replan_required before lower-priority delivery. The deferred item
does not become executable in place: selected_todo points to the deferred
successor so the worker can reopen, supersede, or close it explicitly, while
goal_frontier_projection.deferred_successors.top_ready_todo_id reports the
same lifecycle target. An equal-priority open todo remains executable, avoiding
unnecessary lifecycle churn within a priority bucket.
Open todos with resume_when use the same readiness signal before they enter
ordinary execution lanes. Until resume_ready=true, quota must not include the
todo in capability_gate.runnable_candidates or agent_lane_next_action; it
may continue with a lower-priority executable fallback when the interaction
contract allows safe scoped fallback work.
If an active per-agent vision has no other selectable advancement and its
existing current-agent or unclaimed successor is blocked by an exact supported
resume_when, quota projects vision_wait_state.state=waiting and
agent_scope_wait instead of deriving another autonomous vision replan. The
wait payload preserves the todo id, condition, and automatic-resume contract.
Once resume_ready=true, the wait disappears and ordinary open-todo or
deferred-successor selection runs again. Missing vision checkpoints, closed
stage succession, unsupported conditions, and continuous-monitor-as-gate
repairs remain fail-closed and are not hidden by this wait path.
For completed handoff gates, record that rationale structurally as
no_followup=true; status projects the gate as cleared_no_followup instead
of waking the blocked agent with successor_replan_required.
External-evidence waits have an additional CLI-level observation contract. When
the selected goal is state=waiting, waiting_on=external_evidence, and its
current lane is a continuous monitor, or when the active state says a
long-running external worker was launched and the current action is to poll a
compact result/marker, quota should-run returns
external_evidence_observation.schema_version =
external_evidence_observation_obligation_v0. The guard keeps
should_run=false so ordinary delivery remains blocked, but sets
effective_action=external_evidence_observe and
execution_obligation.kind=external_evidence_observation_required with
must_attempt_work=true. The executor must verify a read-only observable
handle such as a thread id, automation id, job id, lock/result marker, or
compact writeback path before treating the poll as unchanged evidence. If that
handle is missing, stale, or never launched, write back a compact blocker or
launch-readiness fault instead of returning a quiet no-op.
For autonomous heartbeats, unchanged monitor polls can be recorded as no-spend stall evidence with:
loopx --registry "$HOME/.codex/loopx/registry.global.json" quota monitor-poll --goal-id <GOAL_ID> --source heartbeat --execute
quota monitor-poll is valid when the current guard is a quiet monitor skip,
an external-evidence observation, or a due continuous_monitor todo selected
by work_lane_contract.obligation=attempt_due_monitor. For due monitor todos,
pass --todo-id or --target-key plus a public --result-hash; unchanged
polls update last_checked_at, next_due_at, and
consecutive_no_change without appending quota_slot_spent:
When a poll sees a material transition, add --material-change and optionally
--next-agent-todo or --next-user-todo so the monitor produces a concrete
follow-up instead of staying as an opaque watch. A user follow-up must declare
--next-user-task-class user_gate|user_action explicitly: use user_gate for
a blocking owner decision and user_action for a visible reminder that must
not block the bound agent lane. Omitting the task class fails before writeback:
loopx quota monitor-poll --goal-id <GOAL_ID> \
--target-key <TARGET_KEY> --result-hash <HASH> --material-change \
--next-user-todo "<PUBLIC_SAFE_REVIEW_REMINDER>" \
--next-user-task-class user_action \
--next-agent-todo "<PUBLIC_SAFE_FOLLOW_UP>" --execute
The command appends a quota_monitor_poll run record, does not mutate the
registry, and does not append quota_slot_spent. The run includes
quota_monitor_target_v0, a compact hash of the public monitor identity. Six
consecutive public stalled monitor records with the same target feed
autonomous_replan_obligation as
dead_monitor_repeat, so the next independent quota should-run may flip to
autonomous_replan_required /
execution_obligation.must_attempt_work=true; the executor should then record a
watch-lane expiry, concrete blocker, todo supersede, or successor runnable todo
instead of another quiet skip.
After that bounded replan slice is acknowledged by a compact state run carrying
autonomous_replan_ack_v0 and repair_delta_contract_v0 with
delta_present=true, later empty monitor polls for the same acknowledged wait
do not repeatedly retrigger replan. A plain classification such as
monitor_poll_autonomous_replan_recorded_v0 is not enough. Accounting-only
runs such as delivery_completion_spend_accounted_v0 remain neutral liveness
records; they do not close a replan obligation by themselves. Empty polls stay
no-spend liveness checks until a material monitor transition, regression, or
concrete blocker appears.
The same guard exposes automation_liveness. For
monitor_quiet_skip, it must say automation_action=keep_active_quiet,
keep_active=true, and pause_allowed=false: unchanged monitor-only polls are
not a reason to cancel recurring automation. Pausing/deleting is reserved for
either a read-only terminal state derived by LoopX from complete, valid todo
sources, no-follow-up closure evidence, and an empty normalized frontier, or a
bounded self-repair/replan path that is itself stuck for two more eligible
turns. The terminal case projects
automation_action=stop_terminal_no_followup, keep_active=false, and
pause_allowed=true; raw status/attention fields are not terminal authority.
Missing or malformed sources and any open todo, monitor, successor, acceptance
gap, autonomy blocker, or replan obligation fail closed. This keeps recurring
controllers alive during ordinary waits while honoring an explicit completed
goal shutdown without another quota-spending turn.
An individual registered peer can instead be put in monitor_only work mode:
This suppresses that peer's advancement, autonomous replan, repair, fallback,
and new-topic lanes while preserving due continuous_monitor todos and verified
direct operator replies. A future or unchanged monitor stays quiet and no-spend;
a due monitor may spend only after a validated material transition. Other peers
remain active. Use --clear-agent-work-mode <agent-id> (or set =active) to
resume ordinary advancement.
The read model exposes that derivation as
goal_frontier_projection.terminal_state={kind:no_followup, derived:true,
source:validated_goal_closure} plus
source_completeness.user_todos=valid and
source_completeness.agent_todos=valid. Producers derive the source proof and
closure intent from structured todo items; callers cannot authorize shutdown by
writing attention_queue.status or registry attention text.
automation_liveness deliberately does not set the polling cadence. The guard
also exposes scheduler_hint, which is the host-runtime scheduling contract:
Codex App automations should progressively back off toward the recommended
interval/max during long waits, while Codex CLI TUI and Claude Code loops should
run one final quota should-run replan check after their unchanged-poll limit
and exit or stop only if the guard is still unchanged. Cadence changes, final
checks, and self-stop turns never spend quota; only validated delivery or
allowed writeback does.
Scheduler ownership is explicit. A bare quota should-run can still expose the
quota decision, but its scheduler_hint fails closed with
repair_scheduler_execution_context; it never assumes Codex App. Generated
Codex App heartbeats pass the explicit codex_app_heartbeat profile; generated
commands use its compact alias --codex-app. Other hosts
pass the typed --host-surface, --scheduler-owner, and --execution-mode
triple that describes the runtime which will consume the hint.
Compute States¶
Recommended compact states:
eligible: the goal can consume the next automatic agent turn.focus_wait: the goal is Codex-addressable in principle, but the current delivery focus is intentionally paused by a continuation boundary or missing novelty, owner evidence, external eval, or clean baseline.throttled: the goal is healthy but has spent its current compute quota.waiting: the goal is waiting on external evidence or a target controller, so compute should not be spent yet.operator_gate: the goal needs a human decision before more compute is useful.paused: compute quota is0or the operator paused the goal.blocked_health: the goal must fix registry, contract, or boundary issues before more work runs.
These are product states for the dashboard. Adapter classifications remain drill-down details.
Dashboard Implication¶
The dashboard should show compute quota as a compact control surface:
- quota chips:
1.0,0.5,0.3,0; - spent/allowed minute-slots for the current window;
- a visible explicit-override marker only when
allowed_slotsis manually set away from the window-derived default; - next eligible time when throttled;
- simple operator actions: set quota, pause, resume, or grant a temporary burst;
- next-turn view sorted by "should receive the next automatic turn."
The first screen should make it obvious why a project is quiet:
- it is waiting on evidence;
- it is gated by the operator;
- it is in focus wait because the current lane needs novelty, evidence, or a clean baseline before another delivery turn;
- it is throttled by compute quota;
- it is paused;
- or it is eligible and should run next.
CLI Surface¶
The first read-only or preview commands are:
loopx quota status
loopx quota plan
loopx --format json --registry "$HOME/.codex/loopx/registry.global.json" quota should-run --goal-id <goal-id> --runtime-profile codex_app_heartbeat
loopx --registry "$HOME/.codex/loopx/registry.global.json" quota spend-slot --goal-id <goal-id> --slots 1
loopx --registry "$HOME/.codex/loopx/registry.global.json" quota spend-slot --goal-id <goal-id> --slots 1 --execute
These commands reuse the status contract, including contract health, global
registry health, attention queue, run history, and derived quota state. They do
not mutate registry, runtime history, reward overlays, or operator gates.
Project heartbeat prompts should use the shared global registry for
should-run and spend-slot so they see the same operator gates, user todos,
and quota state as the dashboard. Project-local state writes such as
refresh-state and todo add still happen in the source project and sync their
public-safe projection back to the global registry.
quota status is the broad inventory for agents: it shows every registered
goal under blocked_health, operator_gate, focus_wait, eligible,
waiting, throttled, or paused.
quota plan is the next-turn view for automations: it hides empty groups in
markdown output and highlights next_automatic_turn when a goal is eligible.
If that value is none, the automation should skip delivery compute and follow
the displayed gate, evidence, or health reason.
quota should-run is the per-goal guard for heartbeat jobs. It returns a small
JSON or Markdown decision:
Its health gate is also per-goal: global contract errors and errors owned by the selected goal fail closed, while errors owned by other goals remain visible in broad status inventory without blocking this decision. Error ownership comes from structured contract diagnostics, never from parsing a goal-id prefix out of an error string.
{
"goal_id": "project-main-control",
"decision": "skip",
"should_run": false,
"state": "operator_gate",
"reason": "operator gate blocks gated delivery; safe non-gated steering may continue",
"blocked_action_scope": "gated_delivery",
"safe_bypass_allowed": true,
"heartbeat_recommendation": {
"source": "quota.should-run",
"recommended_mode": "ask_operator_gate",
"notify": "NOTIFY",
"spend_policy": "do not append quota spend while asking the operator gate"
},
"scheduler_hint": {
"schema_version": "scheduler_hint_v0",
"action": "backoff_waiting_for_user",
"codex_app": {
"recommended_interval_minutes": 30,
"example_progression_minutes": [30, 60]
},
"unchanged_poll": {
"limits": {
"local_scheduler": 3,
"codex_cli_tui": 3,
"codex_app_ssh_goal": 3,
"claude_code_loop": 3
},
"after_limits": {
"local_scheduler": "stop_tick_loop",
"codex_cli_tui": "update_goal_blocked_keep_loopx_active",
"codex_app_ssh_goal": "update_goal_blocked_keep_loopx_active",
"claude_code_loop": "stop_loop"
},
"final_quota_replan_check_enabled": true,
"final_quota_replan_check_action": "rerun_quota_should_run_once",
"spend_policy": "no quota spend for final replan check or loop stop"
},
"detail_ref": {
"schema_version": "scheduler_hint_detail_v0",
"omitted_by_default": true,
"execution_required": false,
"request": "loopx quota should-run --include-detail scheduler",
"hot_path_runtime_fields": [
"codex_app",
"unchanged_poll",
"reset_policy"
],
"contains": [
"local_scheduler",
"codex_cli_tui",
"codex_app_ssh_goal",
"claude_code_loop",
"final_quota_replan_check",
"reset_policy_detail",
"stateful_backoff_detail"
]
},
"reset_policy": {
"reset_token": "0123456789abcdef",
"host_state_key": "scheduler_hint.reset_policy.reset_token",
"codex_app_initial_interval_minutes": 30,
"codex_app_initial_rrule": "FREQ=MINUTELY;INTERVAL=30",
"identity_signature": "123456789abc"
}
},
"operator_question": "是否同意 project-main-control 先做 read-only map dry-run?",
"gate_prompt": "请用户/控制器确认当前 gate:..."
}
Only state=eligible, outcome-floor recovery safe-bypass, or registry-enabled
control-plane self-repair returns should_run=true. Known goals that are
gated, in focus wait, waiting, throttled, paused, or health-blocked return
ok=true only when the status export itself is healthy, but otherwise return
should_run=false.
safe_bypass_allowed=true is not permission to clear the gate; it only says the
agent can spend a bounded turn on independent read-only steering/analysis.
For state=operator_gate, quota should-run should also surface
gate_prompt, operator_question, next_handoff_condition, missing_gates,
user_todo_summary, or agent_todo_summary when those fields are available.
A heartbeat should use that prompt to ask the user or target controller the
concrete gate question instead of silently skipping, unless the same unresolved
gate was already asked in the recent visible thread. If
user_todo_summary.open_count > 0, existing open user todos are themselves
user-visible action; do not report "no new user action" while those todos
remain open. This also applies after a bounded safe-bypass step: the compact
report must still list the existing open user todos instead of saying that
there is no user action. If agent_todo_summary.open_count > 0, the agent
should use that summary as its next safe follow-up checklist instead of mining
chat history or an overlong Next Action.
For an agent-scoped quota request, user_todo_summary.open_count counts only
user todos bound to that agent plus explicit goal_bound=true items. Todos
bound to another agent remain in
other_agent_bound_user_action_items for diagnosis, but they must not preserve
an inherited operator_gate, enter interaction_contract.user_channel, or
trigger a heartbeat notification. In multi-agent goals, todo authoring must use
--bound-agent <registered-agent> (or --agent-id when the author and bound
lane are the same) or --goal-bound; claimed_by is not a user-todo routing
field.
For state=focus_wait, state=waiting, or
waiting_on=external_evidence, an open user todo can be the smallest unlock
for a quiet project. In that case, quota should-run should set
notify_user_on_open_todo=true and include open_todo_notify_reason. The
target heartbeat should return a compact NOTIFY listing at most three open
user todos and the expected reply (done, defer/not now, or new evidence
link/date/conclusion), while skipping delivery work and quota spend for that
blocker-push turn. If quota also sets
open_todo_notification_policy=repeat_until_resolved, repeat that notification
until the todo is done, deferred, or replaced. If a failed host cadence update
leaves a tighter poll, user_gate_notification_cooldown_v0 keeps the gate open
but suppresses duplicate notices outside a bounded reminder window. Otherwise,
blocker-push cases may still be de-duplicated when the same blocker was already
surfaced recently. Eligible monitor-only polls with no material transition keep
the open user todo visible in user_todo_summary, but do not force a repeated
notification, make the turn a user-action gate, or leave the top-level
should_run set for an otherwise quiet no-op.
Monitor catch-up is also bounded across runnable lanes. After two consecutive
unchanged monitor-only work turns, monitor_debt_arbitration_v0 prefers a
same- or higher-priority advancement todo over another overdue monitor,
including inside scoped user-gate fallback selection. Scheduler/accounting and
state-refresh rows do not break the streak; real advancement or a material
monitor transition does. Direct Lark inbox reply_due work remains a stronger
preemption and is never delayed by this fairness backoff.
For every registered goal, quota should-run also includes a todo_write_hint
so agent executors know to write newly discovered user/owner work with
loopx todo add --role user --task-class user_gate|user_action instead of
hiding it in Next Action, review docs, or chat. Multi-agent writes must also
name --bound-agent <registered-agent> or --goal-bound; agent-scoped
user_gate writes bind to the same agent named by --blocks-agent.
When available, quota should-run also keeps next-action signals separate:
active_state_next_action is the durable ## Next Action,
latest_run_recommended_action is the latest non-agent-lane run's
recommendation, and agent_lane_next_action is the current --agent-id
slice. Agent-scoped payloads may also include
goal_route_hint.schema_version=goal_route_hint_v0, a compact read-only
synthesis that says whether the current lane should run, claim, wait, or
reassign while preserving ## Next Action as durable goal-level guidance. It
is an advisory routing hint, not a writeback instruction and not a replacement
for agent_todo_summary.
When projected, goal_frontier_projection.schema_version =
goal_frontier_projection_v0 is the per-goal progress/frontier view used before
lane-local quiet or wait decisions. Its autonomous_replan_decision says that a
required replan must be selected independently of monitor_quiet_skip or
agent_scope_wait; the policy lives in loopx.control_plane.goals.goal_frontier, while
quota only wires the selected mode into interaction_contract.
If the active-state and latest-run actions differ,
next_action_projection_warning asks the executor to explicitly write back the
intended durable route with a primary goal-scope refresh-state --next-action
or keep treating the signals as distinct.
refresh-state records recommended_action_source so hosts can tell whether a
run recommendation came from an explicit argument, durable ## Next Action, an
Agent Todo compatibility fallback, or the generic default. Dispatch still comes
from agent_lane_next_action / todo projection, not from shared ## Next Action
alone.
When agent_lane_next_action.selected_by=unclaimed_todo, the payload marks
claim_required_before_work=true; executors must claim the todo before editing
or launching delivery work.
For goals with coordination.registered_agents, quota should-run accepts an
optional --agent-id <registered-agent>. Identity-aware heartbeat prompts pass
that flag through their quota guard. If a registered goal is checked without an
agent id, the payload includes automation_prompt_upgrade.required=true and a
recommended heartbeat-prompt --agent-id ... --agent-scope ... command. This
does not flip should_run; it is a lightweight migration signal for stale
installed automations.
When the status payload has same-goal checkpointed decisions that are stale or
have newer sampled events, quota should-run includes
decision_freshness_warning. This warning does not make an eligible goal skip;
it prevents a worker from treating an old reward, steering note, or operator
gate as current authority. Before reusing that decision, the worker must rebase
the decision point against the latest registry, ACTIVE_GOAL_STATE, quota,
policy, and run status. This is not a repo rollback and does not carry the whole
old chat state forward.
When the status payload has missing, stale, or unknown canary promotion
readiness evidence, quota should-run also includes
promotion_readiness_warning. This warning is additive: it does not change
should_run, but it lets heartbeat workers and dashboards report release
readiness blockers from the shared runtime release ledger without parsing
doctor, dashboard copy, or chat reports. New evidence is stored there rather
than in a project Goal. Before promoting the local release
snapshot, run python3 examples/canary/canary-promotion-readiness-smoke.py and
confirm fresh evidence in status or doctor output. The
--no-write-evidence form remains the non-mutating validation path and therefore
does not clear this warning.
Connected delivery goals also include goal_boundary when the registry has
boundary data. That field carries the adapter status, allowed write scope,
parent-approval scopes, registry guards, next probe, and stop condition. It is
the preferred place for project-specific delivery boundaries; automation
prompts should say to obey goal_boundary instead of repeating long protected
file or action lists.
It also includes heartbeat_recommendation, which keeps generic heartbeat
lifecycle decisions in LoopX instead of one-off automation prompts. The
common modes are:
run_first_read_only_map: a connected read-only goal has no saved compact run yet; run one realloopx read-only-map --goal-id <goal-id>, validate and save theread_only_project_map, then append exactly one heartbeat spend.mapped_noop_if_unchanged: the latest compact read-only map already exists; if there is no new user instruction, owner evidence, agent todo, stale source, or safe handoff, return a quiet no-op without another dry-run or quota spend.steering_audit_then_one_step: the goal is eligible but needs the normal steering audit before selecting one bounded progress segment. A coherent implementation/test/state batch is valid when scope and validation are clear; the contract is bounded, not tiny.
The same response includes interaction_contract.schema_version =
loopx_interaction_contract_v0, the top-level user/agent/CLI protocol
for the selected turn. It tells a worker whether this is a user gate, blocker
push, bounded delivery, external-evidence observation, monitor quiet skip,
autonomous replan, outcome-floor recovery, mapped no-op, or quota throttle. It
also names whether the user must be interrupted, whether Codex must attempt
work, whether delivery is allowed, whether quiet no-op is allowed, and whether
quota spend is allowed only after validation. Executors should read this object
first.
The response also includes scheduler_hint.schema_version=scheduler_hint_v0.
That hint is not a delivery permission. It is the cross-runtime wait policy:
run_now keeps the active cadence for required work; backoff_waiting_for_user
slows Codex App and stops CLI/Claude loops after repeated unchanged polls;
backoff_until_reassigned handles peer reassignment waits without dropping
agent-to-agent handoff cadence too quickly;
backoff_until_material_transition handles monitor-only quiet polls; and
backoff_until_fresh_evidence handles mapped or post-handoff no-op waits.
For Codex App and local schedulers, recommended_interval_minutes is the next
target interval. For Codex App heartbeats, recommended_rrule is emitted only
when codex_app.stateful_backoff.apply_needed=true; if the desired RRULE is
already applied, it is omitted so the agent does not call a host tool again.
If that match still needs a reset-token/identity binding,
stateful_backoff.ack_needed=true and the bound ack runs without a host update.
After a successful host RRULE update, the agent records that fact with
loopx plus codex_app.ack_hint.cli_args; current payloads use
quota scheduler-ack-current to re-read the latest scheduler hint before LoopX
advances the per goal/agent scheduler state without spending quota. Human gates
can move Codex App heartbeats through [30, 60] after the concrete user todo
has been surfaced. LoopX caps the Codex App integration at 60 minutes; coarser
waits remain available to the local scheduler instead of being emitted as App
heartbeat RRULEs.
CLI-produced ACK hints bind that argument vector to the exact registry and
effective runtime root that produced quota should-run. Hosts must execute the
complete vector; stripping its leading global options can route a
project-launched ACK into project-local scheduler state instead of the shared
control plane.
Monitor-only quiet waits move through [15, 30, 60] while preserving the
same no-spend monitor-poll contract, unless a monitor cadence or due time caps
the progression earlier.
Agent-scope waits use a more conservative adjustment curve such as
[10, 20, 30, 60], so a 600-second local tick stays close to the existing
agent-to-agent interaction cadence before cooling further.
The compact hot path carries only the reset fields hosts need to act:
reset_policy.reset_token, host_state_key,
codex_app_initial_interval_minutes, codex_app_initial_rrule, and the short
identity_signature. Hosts should cache and compare reset_token across
unchanged polls and reset the unchanged streak whenever the token changes. The
token is derived from scheduler action plus the current identity/profile inputs;
the explanatory reset profile, profile signature, reset condition summary, and
stateful-backoff policy live in scheduler_hint.cold_path_detail when callers
request loopx quota should-run --include-detail scheduler. Hosts should also
reset when an external event makes the goal actionable again, such as user
feedback in the thread, a new or reassigned todo, a resolved gate, or material
evidence transition. A reset applies codex_app_initial_interval_minutes (and
the matching local scheduler initial interval) before starting unchanged
backoff again; it never spends quota.
For Codex App heartbeats, hosts and agents should use automation_update only
when codex_app.stateful_backoff.apply_needed=true and
codex_app.recommended_rrule is present. After automation_update succeeds,
the agent must run codex_app.ack_hint.cli_args. Current payloads use
quota scheduler-ack-current, so LoopX then persists reset_token,
identity_signature, progression_index, and
last_applied_rrule under the runtime root. Repeated unchanged identity
advances through progression_minutes only after the applied RRULE has had one
real interval to run. An immediate post-ACK reconciliation therefore verifies
the settled target instead of manufacturing the next backoff target in the
same turn. A changed reset_policy.reset_token
returns to the current profile's initial interval. This gives hosts a compact
post-update ack protocol instead of requiring them to own or diff the whole
quota state. If apply_needed=false and ack_needed=true, the same command
records an exact matching host readback without calling automation_update.
If automation_update fails or times out, the agent must not ACK. LoopX keeps
the observed host RRULE authoritative. The agent runs
codex_app.failure_hint.cli_args once to persist the failed target/observed-host
pair without quota spend. LoopX retains up to four distinct pairs for 24 hours,
so active-work and monitor-wait targets cannot overwrite one another while the
host RRULE remains unchanged. Later heartbeats expose apply_needed=false and
state_status=host_update_failure_suppressed for every retained exact pair.
A changed host observation invalidates failures recorded against the old host,
and a successful scheduler ACK clears only failures whose target is the
acknowledged RRULE. A fallback ACK therefore preserves failures for other
targets observed against the same unchanged host, preventing the next turn
from retrying a known-bad target/host pair. The legacy
host_update_failure scalar remains as the latest compatibility projection;
new hosts should consume host_update_failures. LoopX never treats an intended
cadence as an applied cadence.
For a human gate whose observed host interval is tighter than the failed target,
the same packet also projects user_gate_notification_cooldown_v0. The first
notice is preserved; short host polls are quiet, one host-sized window opens at
each target cadence, and a changed gate identity or host RRULE bypasses the old
cooldown. This changes notification delivery only, not the underlying user todo.
scheduler-ack is not a second should-run: it confirms the host update or
matching readback and does not emit or make immediately due a successor RRULE
in the same turn. User
feedback, newly runnable work, reassignment, or material evidence therefore
restores the automation to the current profile's initial interval before
backoff resumes.
quota should-run also observes the RRULE of a uniquely matched active Codex
App heartbeat (goal + agent + current thread). The observed host RRULE takes
precedence over last_applied_rrule when computing apply_needed, and the
compact result is exposed as stateful_backoff.host_observation. A mismatch is
drift_detected, so an ACK written before the host update—or a later host-side
cadence regression—cannot permanently suppress the repair. This observation
contains only cadence metadata. If a reset RRULE already matches but its new
reset token/identity is not persisted, apply_needed=false, ack_needed=true,
and the bound ack_hint.cli_args records that exact readback without a no-op
host write. Missing or mismatched readback still requires automation_update;
LoopX never edits the App manifest directly.
For Codex App SSH Goal, Codex CLI TUI, and Claude Code loops, the default hot path reads
scheduler_hint.unchanged_poll.limits.<runtime>. A value of 3 means the third
unchanged poll triggers the compact final quota/replan check named by
scheduler_hint.unchanged_poll.final_quota_replan_check_action; if the rerun is
still unchanged, the loop applies
scheduler_hint.unchanged_poll.after_limits.<runtime>. Hosts that need the
older per-runtime detail objects must opt in with
quota should-run --include-detail scheduler and read
scheduler_hint.cold_path_detail.local_scheduler,
scheduler_hint.cold_path_detail.codex_cli_tui,
scheduler_hint.cold_path_detail.codex_app_ssh_goal, or
scheduler_hint.cold_path_detail.claude_code_loop. That opt-in is diagnostic
and migration support only: a host or agent that forgets
--include-detail scheduler must still retain the core scheduling abilities by
reading the default hot-path fields named in
scheduler_hint.detail_ref.hot_path_runtime_fields.
For native Codex /goal runtimes (codex_cli_tui and
codex_app_ssh_goal), the after-limit action calls update_goal with
status=blocked only after the same blocked condition has repeated for three
consecutive Goal turns. The registered LoopX goal stays active, the user resumes
the native Goal with /goal resume, and neither the final check nor the blocked
transition spends LoopX quota.
The response also includes execution_obligation, which is the compatibility
field that separates worker execution from user-facing notification.
heartbeat_recommendation.notify answers "should this heartbeat interrupt the
user?", not "may the worker skip work?" When
execution_obligation.kind=work_lane_contract, the hard routing rule is
work_lane_contract.obligation; execution_obligation should merely point the
worker to that object. When
execution_obligation.kind=external_evidence_observation_required, delivery is
still blocked but a read-only observation or compact blocker writeback is
mandatory before quiet no-op. When
execution_obligation.must_attempt_work=true, a short heartbeat must attempt
one bounded segment, validate it, write durable state/events, and spend once
after successful delivery. A quiet no-op is only allowed when the machine
contract explicitly says must_attempt_work=false and no blocker-push
notification is required. If notify_user_on_open_todo=true, the turn should
notify the user and skip spend instead of disappearing silently; verified
mapped_noop_if_unchanged after confirming the mapped source is unchanged
remains a quiet no-op case.
Unknown goals or status collection failures return non-zero so automations fail closed.
quota spend-slot is an accounting helper. By default it is dry-run only: it
shows the before and after should-run decision for consuming slots and writes
nothing. With --execute, it appends one compact quota_slot_spent runtime
event. It never mutates registry, reward overlays, operator gates, write
control, private evidence, or production state. The event is status-neutral:
it remains in run history for audit and quota counting, but it should not
become the current work classification in status, quota should-run, or the
dashboard attention queue.
The displayed before -> after transition is a same-status-payload projection
for the slot being written. Later quota status and quota should-run
commands recompute spent_slots from quota_slot_spent events still inside
the rolling window_hours; if an older spend expires between the preview and
the next check, the visible total can stay flat or drop even though the new
event was appended. Treat the appended event path as the write receipt, and
treat spent_slots as the current rolling-window total rather than a monotonic
counter.
Post-turn accounting protocol:
- call
quota should-runbefore spending delivery compute; - do the bounded automatic turn, validation, and state writeback;
- append an accountable
refresh-statewithdelivery_outcome=outcome_progressorprimary_goal_outcomebefore spend. This run is the causal delivery record consumed byspend-slot; a plainstate_refreshedrun without delivery outcome is quota-neutral and cannot replace it; - append exactly one
quota spend-slot --executeevent for that completed turn after validated writeback. When the writeback is a state refresh that moves the guard from eligible/replan to waiting,spend-slotmay still account the latest unspentoutcome_progressdelivery run once; a later duplicate spend is rejected because the latest run is then the spend event, not the delivery run. - a quota-neutral
refresh-staterecord may use a custom classification. Its refresh provenance, rather than the literalstate_refreshedclassification, keeps it from hiding the prior delivery. Explicit non-accountable delivery outcomes and unrelated events remain fail-closed. - after spend, an optional plain state-only refresh may update dashboard or controller state. Do not append another accountable progress refresh after spend, because it would become a new unspent delivery record;
- keep accountable delivery attribution on the worktree that produced it. If
refresh-statemust run from a separate registry checkout, pass--delivery-workspace-path <delivery-worktree>; the path is validated locally and omitted from persisted history. Do not point this option at the canonical checkout for peer work. - autonomous replans follow the same accountable-outcome rule: spend after a
concrete successor, blocker, or
outcome_progress/primary_goal_outcomewriteback, but do not spend for asurface_onlywatch-lane continuation or no-follow-up rationale that closes intomonitor_quiet_skip. A replan that only changes a user gate, monitor target, watch continuation, or durable Next Action is normalized tooutcome_gapeven if the caller requests an accountable outcome; it must also change the executable, blocked, or terminal frontier before it can count as delivery progress. - give each heartbeat a stable turn id and pass it to
quota should-run; the guard commits one idempotent receipt, and for unchangedmonitor_quiet_skipit idempotently appends the no-spend stall observation before returning quiet no-op versus autonomous replan; - do not append spend for quiet
should_run=falseskips, preflight failures, pure dry-run previews, or duplicate accounting attempts; - if
should_run=falsebutsafe_bypass_allowed=trueand the agent actually completes bounded safe-bypass work, append one spend event for that work. Forsafe_bypass_kind=outcome_floor_recovery, spend only after validated ranker/cross-domain evidence or concrete blocker writeback, not for another surface-only report. Every safe-bypass spend must have a latest unspent accountable delivery writeback; the bypass decision alone never authorizes accounting. - if
should_run=truewitheffective_action=control_plane_health_repairorcontrol_plane_projection_repair, append one spend event only after the control-plane projection or blocker writeback is validated.
Slot Spend Event Contract¶
A real spend write path appends one compact runtime event after an automatic
Codex turn actually consumes quota. The smallest public-safe event is
classification=quota_slot_spent with a nested quota_event object:
{
"goal_id": "project-main-control",
"classification": "quota_slot_spent",
"quota_event": {
"event_type": "quota_slot_spent",
"source": "heartbeat",
"slots": 1,
"reason_summary": "one automatic Codex turn completed under an eligible quota guard",
"delivery_run_generated_at": null,
"delivery_run_classification": null,
"before": {
"should_run": true,
"state": "eligible",
"compute": 0.5,
"window_hours": 24,
"slot_minutes": 1,
"spent_slots": 719,
"allowed_slots": 720
},
"after": {
"should_run": false,
"state": "throttled",
"compute": 0.5,
"window_hours": 24,
"slot_minutes": 1,
"spent_slots": 720,
"allowed_slots": 720
}
}
}
The public fixture is
examples/quota-slot-spend-event.example.json.
Validation rule:
- write only after a fresh
quota should-runreturnedshould_run=true, or after it returnedsafe_bypass_allowed=trueand the agent completed one bounded safe-bypass step; - for
effective_action=control_plane_health_repairorcontrol_plane_projection_repair, treat the spend as control-plane self-repair accounting, not ordinary delivery; slotsmust be positive, andafter.spent_slotsmust equalbefore.spent_slots + slots;- if
after.spent_slots >= after.allowed_slots,after.stateshould bethrottledandafter.should_runshould befalse; - do not include human reward, operator-gate approval, write-control, private evidence, internal links, raw logs, or production identifiers in the quota event.
This event is accounting, not permission. It records that compute was spent after the normal health/evidence/quota checks allowed the turn, or after an operator gate explicitly scoped its block to the gated delivery path and allowed one independent safe-bypass step.
Other write commands can stay behind explicit operator approval:
loopx quota set --goal-id <goal-id> --compute 0.5
loopx quota pause --goal-id <goal-id>
loopx quota burst --goal-id <goal-id> --slots 2 --dry-run
The important behavior is that automations ask LoopX whether a goal is eligible before spending compute. They should not rely only on their own cron period as the priority model.
Acceptance Criteria¶
- Each active goal can expose a compute quota such as
1.0,0.5,0.3, or0. loopx statuscan explain whether the goal is eligible, throttled, waiting, paused, or blocked before an automation spends another turn.- A shared controller can rank eligible goals by compute quota without opening every project-agent thread.
- Per-goal automations can use the same quota to skip some ticks with a compact reason.
- Changing automation cadence is no longer the primary way to express project priority.