Skip to content
Open
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
8 changes: 2 additions & 6 deletions loopx/cli_commands/project_lifecycle_refresh_state.py
Original file line number Diff line number Diff line change
Expand Up @@ -844,14 +844,10 @@ def handle_refresh_state_command(
if (material_refresh_ready and isinstance(candidate, Mapping)
and candidate.get("validation_bound") is True):
# A late reflection may recover after spend already committed.
# Read the same settlement; never repeat its accounting effect.
# Reuse the settlement read after recording the refresh event.
attach_reward_memory_ingest_after_spend(
payload, execute=True, registry_path=registry_path,
runtime_root=runtime_root, goal_id=args.goal_id,
agent_id=args.agent_id, todo_id=args.todo_id,
turn_instance_id=args.turn_instance_id,
replan_obligation_id=getattr(args, "replan_obligation_id", None),
goal_ref=goal_ref,
runtime_root=runtime_root, readback=settlement_readback,
)
if material_refresh_ready and post_writeback_hooks:
settlement_identity = (
Expand Down
10 changes: 3 additions & 7 deletions loopx/cli_commands/quota.py
Original file line number Diff line number Diff line change
Expand Up @@ -506,9 +506,10 @@ def _record_non_guard_quota_event(
)
if args.quota_command == "spend-slot" and spend_turn_instance_id:
assert context is not None # A settlement requires a prepared authority context.
_export("attach_spend_settlement_result")(
readback = _export("attach_spend_settlement_result")(
payload,
runtime_root=context.runtime_root,
registry_path=registry_path,
goal_id=args.goal_id,
agent_id=args.agent_id,
todo_id=rollout_todo_id,
Expand All @@ -521,12 +522,7 @@ def _record_non_guard_quota_event(
execute=bool(args.execute),
runtime_root=context.runtime_root,
registry_path=registry_path,
goal_id=args.goal_id,
agent_id=args.agent_id,
todo_id=rollout_todo_id,
turn_instance_id=spend_turn_instance_id,
replan_obligation_id=rollout_replan_obligation_id,
goal_ref=goal_ref,
readback=readback,
)


Expand Down
25 changes: 5 additions & 20 deletions loopx/cli_commands/quota_reward_memory.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@

from collections.abc import Mapping
from pathlib import Path
from typing import Any
from typing import TYPE_CHECKING, Any

if TYPE_CHECKING:
from ..control_plane.quota.settlement import QuotaSettlementReadback


def stage_reward_memory_outcome_candidate(
Expand Down Expand Up @@ -65,30 +68,12 @@ def attach_reward_memory_ingest_after_spend(
execute: bool,
runtime_root: Path,
registry_path: Path,
goal_id: str,
agent_id: str,
todo_id: str | None,
turn_instance_id: str,
replan_obligation_id: str | None,
goal_ref: Mapping[str, Any] | None = None,
readback: QuotaSettlementReadback,
) -> None:
if not execute or payload.get("ok") is not True:
return
from ..capabilities.reward_memory.turn_hooks import run_staged_memory_settlement
from ..control_plane.quota.settlement import read_heartbeat_settlement

readback = read_heartbeat_settlement(
runtime_root,
goal_id=goal_id,
agent_id=agent_id,
todo_id=todo_id,
turn_instance_id=turn_instance_id,
replan_obligation_id=replan_obligation_id,
registry_path=registry_path,
goal_ref=goal_ref,
)
if readback is None:
return
identity = readback.identity.value
writeback_event = readback.writeback_event
details_value = (
Expand Down
9 changes: 5 additions & 4 deletions loopx/control_plane/quota/settlement_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
requalify_bound_heartbeat_receipt,
)
from .settlement import (
QuotaSettlementReadback,
attach_settlement_progress,
read_heartbeat_settlement,
settlement_result_payload,
Expand Down Expand Up @@ -460,16 +461,15 @@ def attach_spend_settlement_result(
payload: dict[str, object],
*,
runtime_root: Path,
registry_path: Path,
goal_id: str,
agent_id: str | None,
todo_id: str | None,
turn_instance_id: str,
replan_obligation_id: str | None = None,
goal_ref: Mapping[str, object] | None = None,
) -> None:
registry_path = (
Path(str(payload["registry"])) if payload.get("registry") else None
)
) -> QuotaSettlementReadback:
"""Attach receipts and return the same post-event observation to the caller."""
readback = read_heartbeat_settlement(
runtime_root,
goal_id=goal_id,
Expand Down Expand Up @@ -500,3 +500,4 @@ def attach_spend_settlement_result(
elif payload.get("receipt_repair_required"):
payload["receipt_repair_required"] = False
payload["receipt_repaired"] = True
return readback
Loading