Project Agent Todo Contract¶
Project agents should keep operator-facing work out of long chat replies,
review documents, and overloaded Next Action paragraphs. LoopX uses
separate fields so the dashboard and quota guard can show the right work to the
right actor.
Field Roles¶
Next Actionis one routing sentence for the next bounded step. It is not a reading queue, blocker dump, or checklist.User Todo / Owner Review Reading Queueis the human-facing checklist. Use it for concrete user, owner, or controller input that the agent cannot complete by itself.Agent Todois the project-agent checklist. Use it for safe follow-up work the agent can do after health, operator gates, evidence, and quota allow execution.- Production blockers, missing write approvals, and safety risks are gates or stop conditions. Do not count them as user todos unless a specific human action can clear them.
External boards and management surfaces, including Lark Kanban, are projections of this contract. They may show critical status, claims, gates, evidence, and worker handoff fields, but they should not become the place where agents invent new task identity. A long-running Codex session can claim visible board work and continue it; when it needs to fan out, split, supersede, or create successor work, it writes the new task through the LoopX todo lifecycle and lets the board sync catch up.
Write Contract¶
When read-only analysis, a review packet, a gate checklist, or P0/P1 steering
finds a concrete user or owner action, write it immediately with the todo CLI.
Use user_gate only when the item blocks an agent or the whole goal:
loopx todo add \
--goal-id <goal-id> \
--role user \
--task-class user_gate \
--blocks-agent <agent-id> \
--text "<public-safe blocking user or owner decision>"
Use user_action for owner-visible follow-up that should not stop unrelated
agents:
loopx todo add \
--goal-id <goal-id> \
--role user \
--task-class user_action \
--text "<public-safe non-blocking user or owner todo>"
Use --role agent for project-agent follow-up work:
Executable agent work should register its lane instead of relying on text
classification. Use advancement_task for a bounded implementation,
validation, benchmark, blocker-writeback, or repair segment:
loopx todo add \
--goal-id <goal-id> \
--role agent \
--text "<public-safe executable agent action>" \
--task-class advancement_task \
--action-kind run_eval
Use continuous_monitor only for watch-only surfaces where an unchanged poll
must stay quiet:
loopx todo add \
--goal-id <goal-id> \
--role agent \
--text "<public-safe monitor action>" \
--task-class continuous_monitor \
--action-kind monitor
--action-kind is a public-safe token. Known generic tokens such as
run_eval, validate, rebuild, writeback, monitor, and poll help the
CLI project the lane consistently, but explicit --task-class is the authority
when both are present. If an exact todo already exists, todo add updates or
inserts the metadata comment instead of creating a duplicate checkbox.
--task-class user_gate, --task-class user_action, and --task-class blocker
are non-executable control lanes; quota/executor code must not treat them as
advancement work. Open user todos must declare either user_gate or
user_action; a bare --role user todo is an authoring error.
For scheduled monitors, keep the contract minimal: --next-due-at is the first
eligible time, --cadence is the retry interval, --monitor-target-key is the
stable idempotency key, and optional --expires-at is the hard stop after
which the monitor must not catch up.
Terminology: a goal_id is the LoopX control-plane boundary: registry
entry, active-state file, quota lane, status projection, and run-history stream.
A todo_id is a structured work item inside that goal. LoopX does not
currently model issues as a separate runtime object.
Multiple agents may share the same project control plane. A todo can carry a
soft owner with claimed_by, but ordinary agent todos should not restate the
agent's broad prompt scope. Scope belongs in the automation prompt or sub-agent
handoff; the agent uses that scope to decide which open todo it may claim.
User-gate todos are different: when a user decision only unlocks one registered
agent or lane, record the blocked agent explicitly with blocks_agent so quota
does not stop unrelated agents. For convenience, todo add/update --role user
--task-class user_gate --agent-id <agent> defaults blocks_agent to that agent
when --blocks-agent is omitted. In multi-agent goals, open user_gate todos
must have exactly one explicit scope: either blocks_agent=<registered-agent>
for a lane-scoped decision or global_gate=true / --global-gate for a
genuine goal-wide owner gate. Unscoped multi-agent user gates are an authoring
error because every registered agent would otherwise see another lane's
question as its own stop condition.
When a user gate only blocks one concrete action, add the blocked todo id with
unblocks_todo_id=<todo_id>. When multiple todos share the same broad
action_kind, use the schema-backed decision-scope fields instead of relying
on title/body token overlap:
loopx todo add \
--goal-id <goal-id> \
--role user \
--task-class user_gate \
--agent-id codex-main-control \
--decision-scope direction:action:benchmark_target_choice \
--text "Choose the benchmark target before running that case."
loopx todo update \
--goal-id <goal-id> \
--todo-id <agent-todo-id> \
--required-decision-scope direction:action:benchmark_target_choice
Quota treats a gate as covering an agent todo when decision_scope matches or
dominates one of that todo's required_decision_scopes; otherwise the todo is
independent and can be selected as a safe fallback when the boundary permits it.
Each shared goal declares coordination.agent_model=peer_v1 and a
coordination.registered_agents set. Registration grants identity, not rank.
Work authority comes from claimed_by, task leases, the goal/write boundary,
and typed continuation policy. Functional profile roles and scope summaries are
advisory; they do not make one identity the default reviewer or leader.
Ordinary lifecycle mutations follow todo ownership. A goal may separately delegate narrow cross-owner actions to an orchestration agent:
coordination:
todo_lifecycle_authority:
- agent_id: codex-main-control
actions: [complete, reassign, supersede]
requires_reason: true
Configure the equivalent registry value without hand-editing state:
loopx configure-goal \
--goal-id <goal-id> \
--todo-lifecycle-authority-json \
'{"agent_id":"codex-main-control","actions":["complete","reassign","supersede"],"requires_reason":true}' \
--execute
The delegated agent must already be registered. Each override is action-scoped
and emits a typed receipt containing the actor, original owner, authority
source, and public-safe --authority-reason. Delegation never bypasses an
explicit excluded_agents boundary. coordination.supervisor remains a
proposal-only observation role and does not imply lifecycle authority.
loopx todo complete \
--goal-id <goal-id> \
--todo-id <todo-id> \
--agent-id codex-main-control \
--authority-reason "Verified the result and closed the stalled lane." \
--evidence "<public-safe evidence>"
An agent todo can name a different task repository without copying agent scope into todo metadata:
loopx todo update \
--goal-id <goal-id> \
--role agent \
--todo-id <todo-id> \
--task-repository git:github.com/owner/repo
task_repository is a first-class, credential-free Git identity. It routes
workspace isolation, not write authority; claim/lease, capabilities, the goal
boundary, and repository policy continue to apply.
quota should-run --agent-id <agent-id> is the preflight for every peer. When
the selected task writes repository state and the peer is in a non-git,
unrelated, or non-isolated workspace, it returns workspace_guard and blocks
normal delivery until that peer moves to an independent worktree and reruns the
guard. Read-only and monitor-only work does not require isolation merely because
of agent identity. When task_repository is absent, the registered goal repo is
still the expected repository, so an unrelated worktree cannot bypass the goal
repository rule.
Contributor-facing example:
If the response includes
effective_action=agent_workspace_repair, the peer should not edit files yet.
Create or switch to a separate worktree and rerun the same guard:
git worktree add /tmp/<goal-id>-peer-b -b codex/<peer-branch>
cd /tmp/<goal-id>-peer-b
loopx --format json quota should-run \
--goal-id <goal-id> \
--agent-id codex-peer-b
Only after that rerun returns normal delivery should the peer claim an in-scope
todo and edit repository files. A todo claimed by another peer remains owned by
that peer until explicit transfer. For agent-specific quota payloads, current
agent claims are preferred, unclaimed todos remain selectable, and other-peer
claims are diagnostic context rather than executable work. This reduces
collisions without writing broad prompt scope into todo metadata or pretending
that a soft claim is already a hard lease. When a runnable current-agent or unclaimed
advancement todo exists, quota may also expose
agent_lane_next_action.schema_version=agent_lane_next_action_v0. That field is
the peer's current slice for this turn; it does not overwrite the durable
goal-level Next Action. loopx status --agent-id <agent-id> may attach the same derived field to matching status
queue items for observation, while leaving the project-level route unchanged.
When a candidate has target_capabilities and missing target bridge
capabilities, quota may mark it capability_repair_mode=true; scoped
next-action selection should prefer that repair-mode candidate over ordinary
runnable work in the same claim/priority bucket so capability-building todos do
not require fragile active-state reordering.
Deferred todos may carry a machine-readable resume condition with
resume_when=<token>. Supported conditions are:
resume_when=todo_done:<todo_id>: the deferred todo becomes a successor replan candidate after the referenced todo reachesstatus=done.resume_when=pr_merged:#532orresume_when=pr_merged:owner/repo#532: the deferred todo becomes a successor candidate after a structured rollout event records that PR merge. An unqualified#532is bound only to the todo's GitHubtask_repository; use the qualified form for cross-repository dependencies. If LoopX cannot derive that binding, it keeps the todo deferred and exposes a machine-readable repository ambiguity instead of matching the same PR number in another repository.
Open todos may also carry resume_when when they are visible but not yet
executable. Until the parsed resume_condition.satisfied value is true, status
and quota keep the todo out of first_executable_items,
executable_backlog_items, capability_gate.runnable_candidates, and
agent_lane_next_action. When resume_ready=true, the open todo may enter the
normal executable lane for its claimed agent.
Status and quota expose deferred todos as a visibility lane after sorted open todo lanes. This is a deferred gate-resume lane: it is not runnable work, and it is not evidence that the current agent has no todo, until an agent reopens, supersedes, or records a no-follow-up rationale for the deferred item.
loopx register-agent \
--goal-id <goal-id> \
--agent-id codex-main-control \
--agent-id codex-side-bypass \
--execute
Then claim through the dedicated command. --claimed-by records the durable
owner, while --agent-id attributes the peer performing this mutation. For a
self-claim they are both required and must name the same registered agent:
loopx todo claim \
--goal-id <goal-id> \
--todo-id <todo_id> \
--claimed-by codex-main-control \
--agent-id codex-main-control
Old projects that do not yet have coordination.registered_agents are
intentionally blocked when an agent tries to claim work. The CLI error includes
the register-agent --agent-id <agent-id> --execute command so the agent or
controller can register its identity before writing ownership metadata. Agent
registration writes the source registry named by the global projection; if the
shared global registry is not writable, it fails before changing that source so
the control plane does not drift into a half-registered state.
Use --clear-claim when an owning peer reassigns a todo or releases work.
claimed_by is visibility, not a runtime lease: it does not bypass quota, user
gates, write-scope checks, validation, or actor authorization. On registered
multi-agent goals, todo claim/update/complete/supersede require --agent-id;
the actor must not be excluded and must match the current claimed_by owner
when one exists. An exact linked user_gate decision scope is the narrow
exception: its typed approve/reject/cancel completion is attributed to the
owner/controller decision instead of an agent actor. Old projects without
coordination.registered_agents still fail closed before ownership metadata
can be written.
todo claim is non-destructive: if another registered agent already owns the
todo, it fails closed instead of silently replacing claimed_by. Transfer
ownership with an explicit todo update --clear-claim or
todo update --claimed-by <agent-id> decision. The CLI writes claim changes and
completion handoffs under the same active-state file lock as todo
add/update/complete, so concurrent CLI writers re-read the latest state before
editing instead of overwriting a stale snapshot.
The command resolves the active state from the project registry, creates the
canonical section when needed, updates updated_at, and avoids duplicate exact
todo text. If a dashboard or controller needs the new checklist immediately,
refresh the status projection after the write:
For multi-agent goals, refresh-state requires an explicit --agent-id.
The default scoped refresh is an agent-lane run: it is useful for keeping the
same turn's writeback/accounting identity intact, but it does not replace the
goal-level status route.
Lifecycle Contract¶
Agents should not patch active-state checkboxes directly to move work forward.
Use the lifecycle commands so LoopX can preserve todo_id, status,
classification metadata, timestamps, and idempotency in one write.
Todo lifecycle should stay simple. Do not add a separate feature state machine
such as slice_done, rolled_out, or proven_in_product_path unless the
product has a concrete UI/runtime need for it. For ordinary project work, use
todo succession instead: complete the implementation slice, then create the
next concrete todo for rollout, product-path audit, benchmark proof, docs,
telemetry, or operator decision.
A non-trivial feature todo is done only as a slice. Before or during completion, the agent should do one of two things:
- create the next public-safe agent or user todo with
--next-agent-todo,--next-user-todo, or a follow-uptodo add; - record a compact no-follow-up rationale with
--no-follow-upplus--note/--reason/--evidence, explaining why the feature is truly finished and does not need rollout, audit, docs, or product-path proof.
This keeps the active checklist honest without making LoopX a heavyweight project-management state machine.
Complete the current todo and atomically register the next executable todo:
loopx todo complete \
--goal-id <goal-id> \
--todo-id <todo_id> \
--agent-id <registered-agent> \
--evidence "<public-safe artifact or result>" \
--next-agent-todo "<public-safe next executable action>" \
--next-task-class advancement_task \
--next-action-kind run_eval
Human review is not automatically an execution gate. When a validated feature PR can wait for review while independent work continues, atomically derive a bound reminder and a runnable successor:
loopx todo complete \
--goal-id <goal-id> \
--todo-id <todo_id> \
--claimed-by <registered-agent> \
--agent-id <registered-agent> \
--evidence "<validated PR URL and checks>" \
--next-user-todo "Review the validated feature PR." \
--next-user-task-class user_action \
--next-agent-todo "Continue the next independent feature slice." \
--next-claimed-by <registered-agent>
--next-user-todo requires an explicit
--next-user-task-class user_gate|user_action. Use user_action for a reminder
that stays visible without setting blocks_agent; reserve user_gate for an
exact owner/controller authority boundary such as merging an aggregate branch
into main, release, benchmark launch, credentials, or protected production
action. Omitting the task class fails before writeback so LoopX never guesses
an authorization boundary. Add a separate continuous_monitor todo when the
PR lifecycle needs periodic readback.
For an experimental feature stack, a stable integration branch may collect
small feature PRs while review reminders remain open. Each feature still uses a
dedicated worktree and branch; its PR targets the integration branch. The
aggregate integration-branch PR to main is the review/merge boundary. This
keeps review latency from suspending unrelated work without weakening the final
delivery gate.
Terminal PR state does not silently complete a review reminder: merged PRs may
still need post-merge review. When the owner explicitly acknowledges that an
exact review action is complete, persist a typed acknowledgement receipt with
the exact bound user_action and GitHub PR:
loopx issue-fix pr-review-ack \
--url https://github.com/owner/repo/pull/123 \
--goal-id <goal-id> \
--todo-id <review-todo-id> \
--agent-id <bound-agent> \
--owner-acknowledged
The receipt is fail-closed, idempotent per todo revision, and read back after append. It binds the goal, todo revision, agent, provider, repository, PR number, and canonical permalink without parsing reminder prose. Reopening or materially editing the todo invalidates the prior acknowledgement.
Use pr-review-reconcile as the single reconciliation path. It may be invoked
explicitly or by a due continuous_monitor through any scheduler or host
adapter. Supplying --owner-acknowledged first records the same typed receipt.
Reconciliation validates the current todo revision before provider access and
again before completion, then closes the reminder only for the exact terminal
PR. A missing receipt, stale revision, unavailable provider, or unsupported
forge leaves the reminder open. Quota projection has no provider side effects.
Heartbeat hosts with external_evidence_poll may run the bounded batch form
before quota:
The batch reads only persisted exact acknowledgement bindings, skips stale or
already reconciled todos before provider access, and performs no quota spend.
Provider failures are reported as degraded results and do not block the
subsequent quota guard. Binding, acknowledgement, and reconciliation are kernel
contracts; loopx-project may document the workflow but is not a runtime
dependency.
If an agent takes ownership at completion time, include the claim in the same locked lifecycle write:
loopx todo complete \
--goal-id <goal-id> \
--todo-id <todo_id> \
--claimed-by codex-peer-a \
--agent-id codex-peer-a \
--evidence "<public-safe artifact or result>" \
--next-agent-todo "Continue the next bounded task." \
--next-claimed-by codex-peer-b \
--next-continuation-policy independent_handoff
LoopX does not infer continuation authority from agent identity. action_kind
remains an open domain token describing the work. The closed
continuation_policy enum describes only the relationship between the completed
task and its successor:
independent_handoffis the default. The successor stays unclaimed unless--next-claimed-byselects a registered peer;same_agent_non_deliverykeeps an evidence-backed non-delivery continuation with the completing peer.
Review, verification, merge, and publication remain ordinary action_kind
values. They do not alter scheduler ordering. Use independent_handoff for a
successor that another peer may claim, and add one or more excluded_agents
only when executor separation is required. claimed_by may not name an
excluded peer.
same_agent_non_delivery is intentionally structural rather than
review-specific. It covers readiness checks, audits, triage, and other
non-delivery continuations when explicitly selected. LoopX does not infer it
from action_kind, and it does not authorize repository delivery or bypass
successor quota/capability checks.
A handoff becomes a blocking dependency only when it carries both
unblocks_todo_id and executor exclusions. Ordinary independent successors do
not inherit an owner implicitly. Use --next-claimed-by when the next owner is
already known; leave the successor unclaimed when a later claim should select
it.
For small changes that satisfy the repository's self-merge rules, a peer may self-merge and complete without a successor review todo by making the exception explicit:
loopx todo complete \
--goal-id <goal-id> \
--todo-id <todo_id> \
--claimed-by codex-peer-a \
--agent-id codex-peer-a \
--self-merged \
--evidence "<public-safe commit, validation, and self-merge summary>"
--self-merged requires --evidence. Do not use it for runtime,
benchmark, permission, production, destructive git, publication, public
evidence-policy, or broad coordination changes that need an independent
handoff.
After a validated self-merge, write back the real delivery outcome at the
project level when the slice advanced the public product or case path. An
agent-lane refresh with --agent-id records peer-local notes; a goal-scope
refresh records the durable goal route. Both require a registered peer. Do
not append a follow-up goal-level surface_only sync after a validated
outcome_progress slice; either skip the duplicate sync or mirror the product
progress with:
loopx refresh-state \
--goal-id <goal-id> \
--classification <public-safe-progress-classification> \
--delivery-batch-scale multi_surface \
--delivery-outcome outcome_progress \
--agent-id <registered-agent> \
--progress-scope goal
This keeps validated peer product work from being misread as another surface-only heartbeat turn. When a self-merged slice has an obvious same-scope continuation, it may also atomically add that successor todo and claim it back to the same peer:
loopx todo complete \
--goal-id <goal-id> \
--todo-id <todo_id> \
--claimed-by codex-peer-a \
--agent-id codex-peer-a \
--self-merged \
--evidence "<public-safe commit, validation, and self-merge summary>" \
--next-agent-todo "Continue the next small docs/productization slice." \
--next-claimed-by codex-peer-a
Without --self-merged, no implicit review route is created. For independent
review, use --next-action-kind review --next-continuation-policy
independent_handoff; add --next-excluded-agent <author> only when the review
must remain open to multiple peers while excluding the author. When that
successor belongs to a specific repository or needs execution capabilities,
set --next-task-repository <git:host/path> and repeat
--next-required-capability <capability> in the same completion command. The
successor is then fully routable before its executor exclusions take effect.
Use todo update for lower-level, non-terminal status changes:
loopx todo update \
--goal-id <goal-id> \
--todo-id <todo_id> \
--agent-id <registered-agent> \
--status blocked \
--reason "<public-safe blocker>" \
--task-class blocker
Agent Todo completion always goes through todo complete; todo update
--status done is rejected so review, successor, and no-follow-up policy cannot
be bypassed. An evidence-backed peer
continuous_monitor with no required write scope may close with
todo complete --no-follow-up when its bounded watch ends without a material
transition. This closeout records self_merged=false and does not
create a successor review todo for observation-only work.
Use --resume-when when deferring a successor that should wake up after a
machine-readable condition instead of living only in prose:
loopx todo update \
--goal-id <goal-id> \
--todo-id <todo_id> \
--agent-id <registered-agent> \
--status deferred \
--resume-when todo_done:<blocking_todo_id> \
--reason "<public-safe deferred rationale>"
Use todo supersede when the current open todo should be retired and replaced:
loopx todo supersede \
--goal-id <goal-id> \
--todo-id <todo_id> \
--agent-id <registered-agent> \
--reason "<public-safe reason>" \
--next-agent-todo "<replacement executable action>"
If the superseded todo was claimed, the replacement inherits that claimed_by.
If it carried blocks_agent / unblocks_todo_id, the replacement inherits
those unblock fields too. Use --next-claimed-by <agent-id> to make a handoff
explicit.
todo complete and todo supersede are intentionally idempotent for repeated
heartbeats: if the old todo is already complete and the next todo already
exists with the same metadata, the command returns changed=false and does not
duplicate the next checkbox. todo archive-completed is only a hygiene command:
it moves already-completed active todos into Completed Work Archive; it does
not mark open todos as done.
Parsed Schema¶
Projects may keep writing ordinary Markdown checkboxes, but readers should use
the structured projection emitted by status/quota when available. Todo summaries
carry schema_version=todo_summary_v0; individual items carry
schema_version=todo_item_v0, todo_id, role, status, priority,
title, archive_state, source_section, index, text, task_class, and
optional action_kind, claimed_by, required_capabilities, and
target_capabilities. A capability-admitted agent Todo may also carry an opaque
capability_binding_ref; it identifies the capability-owned authority row and
is preserved across generated agent successors. Once set, the binding is
immutable and is distinct from executor required_capabilities.
action_kind is extensible; optional
continuation_policy is limited to independent_handoff,
or same_agent_non_delivery. Agent todos may also carry excluded_agents,
unblocks_todo_id, and no_followup=true to express executor separation,
dependency lineage, and intentional closeout. blocks_agent is reserved for
scoping user gates.
After a hard-cut upgrade, loopx check reports agent todos that still carry
removed gate-routing fields. New readers also preserve a read-only
removed_continuation_policy diagnostic for legacy review_handoff and
primary_review records and exclude those records from claim/quota execution.
This fail-closed compatibility marker is not a supported continuation type and
is never written back. Repair legacy review records explicitly:
loopx todo update \
--goal-id <goal-id> \
--todo-id <todo-id> \
--agent-id <registered-agent> \
--role agent \
--continuation-policy independent_handoff \
--excluded-agent <author>
For removed blocks_agent routing, use loopx todo update --todo-id <todo_id>
--role agent --clear-blocks-agent. LoopX does not infer the excluded author or
rewrite either form automatically.
Deferred successors may carry resume_when, resume_condition, and
resume_ready; resume_ready=true means the deferred item should be considered
for a successor replan before any agent-scoped no-candidate wait, not that
normal delivery may skip the todo lifecycle. A ready deferred successor also
preempts a strictly lower-priority open advancement todo for this lifecycle
replan. It does not preempt an equal-priority open todo, and it never enters the
normal executable backlog until a lifecycle command reopens it.
The todo_id is first-class when written by the CLI.
claimed_by values are normalized public-safe agent ids and should correspond to
coordination.registered_agents. Legacy Markdown without metadata still gets a
parser-derived compatibility id from local section/index/text, and the first
lifecycle command will materialize that id back into metadata. Future lease
timestamps, dependency, capability detail, and evidence-link fields should
extend this item shape instead of adding another todo format.
In Markdown, lane metadata is stored as an indented HTML comment directly under
the checkbox, for example:
- [ ] Run one validated benchmark case and write back result or blocker.
<!-- loopx:todo todo_id=todo_8e280be49441 status=open task_class=advancement_task action_kind=run_eval required_capabilities=shell%2Cbenchmark_runner claimed_by=codex-main-control -->
Plain checkbox text remains a compatibility fallback. New automation-facing work should prefer the CLI metadata path so quota and dashboard consumers do not need project-specific word lists.
Executable agent todos may declare per-todo environment needs with
--required-capability. Keep this field near the todo, not in a global agent
profile: the same agent may have shell/filesystem capability for docs work, but
lack benchmark_runner, external_evidence_poll, network, or another bridge
for a specific step.
loopx todo add \
--goal-id <goal-id> \
--role agent \
--text "<public-safe executable agent action>" \
--task-class advancement_task \
--action-kind run_eval \
--required-capability shell \
--required-capability benchmark_runner
Use --target-capability when the todo is meant to build, repair,
materialize, or parity-check a capability rather than use that capability as a
prerequisite. For example, a benchmark product-path parity todo can hard-require
only shell while targeting the benchmark runner bridge:
loopx todo add \
--goal-id <goal-id> \
--role agent \
--text "<public-safe benchmark parity repair action>" \
--task-class advancement_task \
--action-kind benchmark_treatment_product_path_parity \
--required-capability shell \
--target-capability benchmark_runner
status projects required_capabilities on every visible todo. quota
should-run then derives a read-only capability_gate from the visible
executable queue, not from a single preselected todo. With multiple P0 or P1
items, it scans the projected queue in order and exposes the candidate set:
capability-satisfied todos appear in capability_gate.runnable_candidates,
while blocked higher-priority candidates remain visible in
capability_gate.blocked_candidates. The gate does not choose the final todo;
the agent keeps decision authority and must pick from the runnable set during
its steering audit. If no visible executable candidate can run, the gate returns
repair_bridge, ask_owner, or skip according to the missing capability
class.
target_capabilities stay visible in runnable candidate payloads. If a target
bridge is absent, the candidate is annotated with capability_repair_mode=true
and missing_target_capabilities, but that target is not treated as a hard
execution blocker.
Execution Order¶
- Run the quota guard against the shared global registry before spending automatic delivery compute.
- If the guard or review packet exposes open user todos, surface them to the user instead of reporting "no new user action".
- If the guard sets
notify_user_on_open_todo=true, treat the open todos as a blocker-push notification: ask at most three items, skip delivery work, and skip quota spend unless the same blocker was already surfaced recently. - Do not execute
agent_command, adapter work, write-control, or production actions while the relevant gate is still unresolved. - After the user todo is completed or explicitly deferred, the project agent may continue only through the safe path allowed by the current guard or review packet.
Public Smokes¶
Two dependency-free public fixtures cover this contract:
python3 examples/control_plane/todo-cli-smoke.py
python3 examples/control_plane/todo-lifecycle-cli-smoke.py
python3 examples/project/project-agent-adoption-smoke.py
python3 examples/control_plane/todo-concurrent-write-lock-smoke.py
python3 examples/capability-gate-smoke.py
The first verifies the todo CLI writes canonical active-state sections. The
second verifies lifecycle transitions by todo_id, including claimed
completion, supersede, idempotent next-todo insertion, and non-executable
blocker lanes.
The third verifies an executor-facing path from quota guard hint, to user todo
write, to status projection, to approved project-agent handoff.
The fourth verifies concurrent todo writers wait on the active-state lock and
preserve both claim metadata and unrelated updates.
The fifth verifies per-todo required_capabilities, including multiple P0/P1
candidate selection, bridge repair, and owner-gated capability misses.