Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 30 additions & 18 deletions docs/reference/protocols/quota-monitor-observation-receipt-v0.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,26 +14,33 @@ advancement work remains active.

- The heartbeat receipt's `todo_id` remains the only settlement Todo. Only
that Todo may be used by `refresh-state` and `quota spend-slot`.
- `quota monitor-poll --todo-id <monitor>` may record one auxiliary, no-spend
observation in the same Turn only when the requested Todo is a due
`continuous_monitor` visible to the same Agent.
- `quota monitor-poll --todo-id <monitor>` may record auxiliary, no-spend
observations in the same Turn only when each requested Todo is a due
`continuous_monitor` visible to the same Agent. Admission checks the Todo
authority as well as the bounded decision projection, so a due monitor is
not rejected merely because it falls outside the compact list.
- The monitor receipt records both `settlement_todo_id` and the observed
monitor `todo_id`. They may differ; this never grants a second delivery or
quota-spend identity.
- A receipt-bound monitor remains strict: another monitor cannot be substituted.
Reusing the same Turn for a different observation conflicts with the existing
monitor-poll effect digest.
- Each auxiliary observation has an operation identity scoped by the monitor
Todo, or by a digest of `target_key` when there is no Todo id. Exact retries
replay that observation; changed content conflicts only with the same monitor
identity; another due monitor receives an independent no-spend receipt.
Shipped turn-only receipts remain replayable for their original monitor.
- A receipt-bound monitor remains strict: another monitor cannot be substituted
for the Turn's settlement Todo. Multiple auxiliary receipts never change the
already-bound settlement identity.
- After an unchanged auxiliary observation, the original advancement Todo
remains selected. A material observation may create its independently routed
successor through the existing monitor contract, but it still does not
replace the Turn's settlement identity.

### Acceptance

The CLI path must prove that a due monitor can update its cadence and replay
idempotently without spending quota, while a guard replay continues to select
the original advancement Todo. Existing wrong-Todo tests for receipt-bound
monitor Turns must remain passing.
The CLI path must prove that multiple due monitors can each update their cadence
and replay idempotently in one settlement Turn without spending quota, while a
guard replay continues to select the original advancement Todo. Existing
wrong-Todo tests for receipt-bound monitor Turns must remain passing.

## 中文

Expand All @@ -47,18 +54,23 @@ monitor Turns must remain passing.

- heartbeat 回执中的 `todo_id` 始终是唯一结算 Todo;只有它可用于
`refresh-state` 与 `quota spend-slot`。
- 仅当请求对象是同一 Agent 可见且已到期的 `continuous_monitor` 时,
`quota monitor-poll --todo-id <monitor>` 才可在同一 Turn 写入一次辅助、
不计费的观察回执。
- 仅当每个请求对象都是同一 Agent 可见且已到期的 `continuous_monitor` 时,
`quota monitor-poll --todo-id <monitor>` 才可在同一 Turn 写入辅助、不计费
的观察回执。准入同时检查 Todo 权威源与有界决策投影,不能仅因到期 monitor
位于精简列表之外就拒绝它。
- 监控回执同时记录 `settlement_todo_id` 与被观察的 monitor `todo_id`。
二者允许不同,但不会因此产生第二个交付或配额结算身份。
- 若 heartbeat 本身绑定的是 monitor,仍保持严格身份,不能换成另一个 monitor;
同一 Turn 也不能用不同观察内容覆盖既有 monitor-poll effect。
- 每个辅助观察按 monitor Todo 建立操作身份;没有 Todo id 时,按
`target_key` 摘要建立身份。精确重试只重放该观察;同一 monitor 下内容变化
只与该 monitor 冲突;另一个到期 monitor 获得独立的不计费回执。已发布的
Turn-only 旧回执仍可对原 monitor 重放。
- 若 heartbeat 本身绑定的是 monitor,仍保持严格身份,不能把另一个 monitor
替换为本 Turn 的结算 Todo;多个辅助回执也绝不改变既有结算身份。
- 辅助观察无变化后,原 advancement Todo 继续保持选中;若观察发生重大变化,
可按既有 monitor 契约创建独立路由的 successor,但仍不替换本 Turn 的结算身份。

### 验收

CLI 端到端测试必须证明:到期 monitor 能更新周期并幂等重放、全程不消耗配额;
随后重放 guard 仍选择原 advancement Todo。同时,receipt-bound monitor Turn 的
错误 Todo 替换测试必须继续通过。
CLI 端到端测试必须证明:多个到期 monitor 能在同一结算 Turn 中分别更新周期并
幂等重放、全程不消耗配额;随后重放 guard 仍选择原 advancement Todo。同时,
receipt-bound monitor Turn 的错误 Todo 替换测试必须继续通过。
23 changes: 23 additions & 0 deletions docs/reference/protocols/turn-envelope-v0.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,29 @@ only a qualified request upgrades the identity-less receipt. A newly due hard
lane leaves the receipt unbound, and only the resulting receipt-bound envelope
is a delivery contract.

An unbound selection that no longer qualifies is a preflight outcome, not a
settlement-identity conflict. The full quota response preserves the TypeScript
`action_selection_qualification_v0` result and returns
`quota_action_selection_deferred` or `quota_action_selection_rejected`, including
the exact current preemption or eligibility reason. An existing identity-less
receipt is replayed without mutation; a first-call rejection reports
`heartbeat_receipt.status=not_committed` and writes no receipt event. The agent
can therefore refresh the current portfolio with the same Turn id and re-enter
deterministically. A receipt already bound to a different Todo or autonomous
replan obligation remains a hard `heartbeat_receipt_identity_conflict`.
When a due monitor is visible only as auxiliary context for an advancement lane,
the typed reason is
`auxiliary_monitor_not_selectable_in_advancement_lane`. The agent selects a
current advancement Todo or retries after the monitor becomes the hard lane;
this state is never reported as a receipt write failure.

A selection may also qualify while repository delivery is temporarily blocked
by the peer workspace guard. In that case the response and bound receipt keep
the selected Todo, `effective_action=agent_workspace_repair`, and the typed
worktree recovery instruction. Moving to an independent worktree and rerunning
the guard with the same Turn id resumes the selected Todo; the wrapper must not
rewrite this recoverable state as a settlement-identity conflict.

Portfolio v2 preserves v1's selection policy, candidate ordering, and
settlement rules, and adds an optional `continuation_hint` to each suggested
action. The default quota producer and Turn controller now require v2. The
Expand Down
144 changes: 125 additions & 19 deletions loopx/cli_commands/quota.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
quota_error_code,
)
from ..control_plane.quota.heartbeat_receipt import (
HEARTBEAT_RECEIPT_SCHEMA_VERSION,
fail_heartbeat_receipt,
find_heartbeat_receipt,
heartbeat_receipt_settlement_replan_obligation_id,
Expand Down Expand Up @@ -379,17 +380,17 @@
return existing, todo_id, replan_obligation_id


def _require_requested_quota_action_selection(
payload: Mapping[str, object],
def _apply_requested_quota_action_selection_preflight(

Check failure on line 383 in loopx/cli_commands/quota.py

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Refactor this function to reduce its Cognitive Complexity from 23 to the 15 allowed.

See more on https://sonarcloud.io/project/issues?id=huangruiteng_loopx&issues=AaCabplSyyKf7W-ftX4P&open=AaCabplSyyKf7W-ftX4P&pullRequest=4335
payload: dict[str, object],
*,
requested_todo_id: str | None,
receipt_bound_todo_id: str | None,
receipt_bound_replan_obligation_id: str | None,
) -> None:
) -> bool:
if not requested_todo_id or (
receipt_bound_todo_id or receipt_bound_replan_obligation_id
):
return
return False
selected_todo = payload.get("selected_todo")
selected_todo_id = (
normalize_todo_id(selected_todo.get("todo_id"))
Expand All @@ -415,25 +416,109 @@
agent_channel: Mapping[str, object] = (
agent_channel_value if isinstance(agent_channel_value, Mapping) else {}
)
pending_selection_qualified = (
pending_selection_delivery_qualified = (
selection_binding == "pending_action_selection"
and payload.get("normal_delivery_allowed") is True
)
pending_selection_workspace_repair_qualified = (
selection_binding == "pending_action_selection"
and payload.get("workspace_repair_allowed") is True
and payload.get("effective_action") == "agent_workspace_repair"
and execution_obligation.get("kind") == "agent_workspace_repair"
and execution_obligation.get("must_attempt_work") is True
and agent_channel.get("must_attempt") is True
and agent_channel.get("delivery_allowed") is False
)
exact_current_obligation_qualified = (
selection_binding != "pending_action_selection"
and execution_obligation.get("must_attempt_work") is True
and agent_channel.get("must_attempt") is True
)
if (
selected_todo_id != requested_todo_id
or payload.get("ok") is not True
or payload.get("should_run") is not True
or not (pending_selection_qualified or exact_current_obligation_qualified)
selected_todo_id == requested_todo_id
and payload.get("ok") is True
and payload.get("should_run") is True
and (
pending_selection_delivery_qualified
or pending_selection_workspace_repair_qualified
or exact_current_obligation_qualified
)
):
raise HeartbeatReceiptIdentityConflictError(
"explicit action selection must name one currently projected "
"agent-scoped, capability-ready Todo"
return False

qualification_value = payload.get("action_selection_qualification")
if not isinstance(qualification_value, Mapping):
raise RuntimeError("requested action selection lacks typed qualification")
qualification = qualification_value
qualification_state = str(qualification.get("state") or "")
if qualification_state not in {"deferred", "rejected"}:
raise RuntimeError(
"requested action selection qualification conflicts with its projection"
)
qualification_reason = str(
qualification.get("reason") or "candidate_not_currently_eligible"
)
deferred = qualification_state == "deferred"
auxiliary_monitor = (
qualification_reason
== "auxiliary_monitor_not_selectable_in_advancement_lane"
)
error_code = (
"quota_action_selection_deferred"
if deferred
else "quota_action_selection_rejected"
)
payload.update(
{
"ok": False,
"decision": "skip",
"should_run": False,
"effective_action": error_code,
"state": error_code,
"waiting_on": "codex",
"status": error_code,
"error_code": error_code,
"reason": (
"explicit action selection was deferred by the current "
f"delivery frontier: {qualification_reason}"
if deferred
else "explicit action selection is not currently eligible: "
f"{qualification_reason}"
),
"recommended_action": (
"handle the current delivery preemption, then rerun quota "
"should-run with the same --turn-instance-id; omit --todo-id "
"first when a refreshed action portfolio is needed"
if deferred
else "the due monitor is visible as auxiliary context, not an "
"independently selectable action in the current advancement lane; "
"choose a current advancement Todo, or rerun after the monitor "
"becomes the hard lane"
if auxiliary_monitor
else "rerun quota should-run with the same --turn-instance-id "
"without --todo-id, then choose a currently eligible Todo"

Check warning on line 499 in loopx/cli_commands/quota.py

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Extract this nested conditional expression into an independent statement.

See more on https://sonarcloud.io/project/issues?id=huangruiteng_loopx&issues=AaCa1Vq3He7829VWf6or&open=AaCa1Vq3He7829VWf6or&pullRequest=4335
),
}
)
return True


def _attach_uncommitted_action_selection_receipt(
payload: dict[str, object],
*,
turn_instance_id: str,
) -> None:
"""Expose an accurate non-durable receipt for a rejected preflight."""

payload["heartbeat_receipt"] = {
"schema_version": HEARTBEAT_RECEIPT_SCHEMA_VERSION,
"turn_instance_id": turn_instance_id,
"status": "not_committed",
"stall_observation": "not_evaluated",
"reason_code": str(
payload.get("error_code") or "quota_action_selection_rejected"
),
}


def _commit_requested_action_selection(
Expand Down Expand Up @@ -544,6 +629,7 @@
heartbeat_receipt_existing_status = "replayed"
heartbeat_receipt_existing_appended = False
heartbeat_receipt_ready = False
action_selection_preflight_failed = False
heartbeat_stall_observation = "not_evaluated"
detail_sections: frozenset[str] = frozenset()
context: QuotaCommandContext | None = None
Expand Down Expand Up @@ -636,14 +722,20 @@
turn_start_hook_dispatch=turn_start_hook_dispatch,
)
_attach_turn_start_hook_dispatch(payload, turn_start_hook_dispatch)
_require_requested_quota_action_selection(
payload,
requested_todo_id=_requested_quota_action_todo_id(args),
receipt_bound_todo_id=receipt_bound_todo_id,
receipt_bound_replan_obligation_id=(receipt_bound_replan_obligation_id),
action_selection_preflight_failed = (
_apply_requested_quota_action_selection_preflight(
payload,
requested_todo_id=_requested_quota_action_todo_id(args),
receipt_bound_todo_id=receipt_bound_todo_id,
receipt_bound_replan_obligation_id=(
receipt_bound_replan_obligation_id
),
)
)
if heartbeat_turn_id:
if heartbeat_receipt_existing:
if action_selection_preflight_failed:
heartbeat_receipt_ready = True
elif heartbeat_receipt_existing:
(
heartbeat_receipt_existing,
heartbeat_receipt_existing_status,
Expand Down Expand Up @@ -826,7 +918,21 @@
replan_obligation_id=rollout_replan_obligation_id,
)
if heartbeat_turn_id and args.quota_command == "should-run":
if not heartbeat_receipt_ready:
if action_selection_preflight_failed:
if heartbeat_receipt_existing:
render_existing_heartbeat_receipt_payload(
payload,
receipt=heartbeat_receipt_existing,
turn_instance_id=heartbeat_turn_id,
status="replayed",
appended=False,
)
else:
_attach_uncommitted_action_selection_receipt(
payload,
turn_instance_id=heartbeat_turn_id,
)
elif not heartbeat_receipt_ready:
prior_reason = str(payload.get("reason") or "").strip()
fail_heartbeat_receipt(
payload,
Expand Down
Loading