feat(runtime): consume governed Activity Core closes
Assistant: codex Assistant-Model: gpt-5.6-sol Assistant-Session: 01a06ba0-10aa-7ea0-b20a-4f3fac39efe9
This commit is contained in:
parent
0e6d795aa4
commit
d00ffcb402
22 changed files with 1218 additions and 148 deletions
|
|
@ -1,8 +1,7 @@
|
|||
"""Durable, idempotent close-evidence outbox core.
|
||||
"""Durable, idempotent Activity Core close-evidence outbox.
|
||||
|
||||
The outbox is intentionally not wired into the live claim loop yet. It stores
|
||||
bounded Activity Core completion/failure intents outside target checkouts so a
|
||||
future reconciler can retry close delivery without re-running repository work.
|
||||
It stores bounded completion/failure intents outside target checkouts so the
|
||||
claim worker can retry close delivery without re-running repository work.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
|
@ -32,6 +31,7 @@ _RECORD_KEYS = frozenset(
|
|||
"entry_id",
|
||||
"run_id",
|
||||
"transaction_id",
|
||||
"worker_id",
|
||||
"action",
|
||||
"result",
|
||||
"error",
|
||||
|
|
@ -61,12 +61,17 @@ class OutboxCorruptError(CloseOutboxError):
|
|||
"""A durable record cannot be safely decoded or validated."""
|
||||
|
||||
|
||||
class PermanentCloseDeliveryError(CloseOutboxError):
|
||||
"""Activity Core conclusively refused a close intent; do not retry it."""
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class CloseRequest:
|
||||
"""One bounded Activity Core completion or failure intent."""
|
||||
|
||||
run_id: str
|
||||
transaction_id: str
|
||||
worker_id: str
|
||||
action: str
|
||||
result: dict[str, Any]
|
||||
error: str = ""
|
||||
|
|
@ -80,6 +85,7 @@ class CloseRequest:
|
|||
)
|
||||
_validate_identifier(self.run_id, "run_id")
|
||||
_validate_identifier(self.transaction_id, "transaction_id")
|
||||
_validate_identifier(self.worker_id, "worker_id")
|
||||
if self.action not in {"complete", "fail"}:
|
||||
raise InvalidCloseRequestError("action must be 'complete' or 'fail'")
|
||||
if not isinstance(self.result, dict):
|
||||
|
|
@ -118,6 +124,7 @@ class CloseRequest:
|
|||
"entry_id": self.entry_id,
|
||||
"run_id": self.run_id,
|
||||
"transaction_id": self.transaction_id,
|
||||
"worker_id": self.worker_id,
|
||||
"action": self.action,
|
||||
"result": self.result,
|
||||
"error": self.error,
|
||||
|
|
@ -128,6 +135,7 @@ class CloseRequest:
|
|||
return CloseRequest(
|
||||
run_id=self.run_id,
|
||||
transaction_id=self.transaction_id,
|
||||
worker_id=self.worker_id,
|
||||
action=self.action,
|
||||
result=json.loads(json.dumps(self.result)),
|
||||
error=self.error,
|
||||
|
|
@ -203,6 +211,7 @@ class _Record:
|
|||
request = CloseRequest(
|
||||
run_id=value["run_id"],
|
||||
transaction_id=value["transaction_id"],
|
||||
worker_id=value["worker_id"],
|
||||
action=value["action"],
|
||||
result=value["result"],
|
||||
error=value["error"],
|
||||
|
|
@ -328,6 +337,10 @@ class CloseOutbox:
|
|||
_atomic_write_json(path, trying.payload())
|
||||
try:
|
||||
deliver(record.request.detached_copy())
|
||||
except PermanentCloseDeliveryError as exc:
|
||||
self._quarantine(path, str(exc))
|
||||
quarantined += 1
|
||||
continue
|
||||
except Exception as exc:
|
||||
retained = _Record(
|
||||
request=record.request,
|
||||
|
|
@ -370,6 +383,32 @@ class CloseOutbox:
|
|||
with self._locked():
|
||||
return len(tuple(self.pending_dir.glob("*.json")))
|
||||
|
||||
def status(self) -> dict[str, int]:
|
||||
"""Return bounded operator counts without decoding record contents."""
|
||||
with self._locked():
|
||||
quarantined = tuple(
|
||||
path
|
||||
for path in self.quarantine_dir.glob("*.json")
|
||||
if not path.name.endswith(".error.json")
|
||||
)
|
||||
return {
|
||||
"pending": len(tuple(self.pending_dir.glob("*.json"))),
|
||||
"delivered": len(tuple(self.delivered_dir.glob("*.json"))),
|
||||
"quarantined": len(quarantined),
|
||||
}
|
||||
|
||||
def entry_state(self, entry_id: str) -> str | None:
|
||||
"""Return the durable state for one already-validated entry id."""
|
||||
_validate_identifier(entry_id, "entry_id")
|
||||
with self._locked():
|
||||
if (self.delivered_dir / f"{entry_id}.json").exists():
|
||||
return "delivered"
|
||||
if (self.pending_dir / f"{entry_id}.json").exists():
|
||||
return "pending"
|
||||
if tuple(self.quarantine_dir.glob(f"{entry_id}.*.json")):
|
||||
return "quarantined"
|
||||
return None
|
||||
|
||||
def _load(self, path: Path) -> _Record:
|
||||
try:
|
||||
if path.stat().st_size > _MAX_PAYLOAD_BYTES + 8192:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue