fix(agent99): bound DB sessions and expire stale outcomes
Some checks failed
CD Pipeline / workflow-shape (push) Successful in 1s
CD Pipeline / cancel-stale-cd (push) Has been skipped
CD Pipeline / tests (push) Successful in 4m0s
CD Pipeline / post-deploy-checks (push) Has been cancelled
CD Pipeline / build-and-deploy (push) Has been cancelled
AWOOOI Harbor 110 Local Repair / workflow-shape (push) Successful in 1s
AWOOOI Harbor 110 Local Repair / harbor-110-local-repair (push) Successful in 17s
Some checks failed
CD Pipeline / workflow-shape (push) Successful in 1s
CD Pipeline / cancel-stale-cd (push) Has been skipped
CD Pipeline / tests (push) Successful in 4m0s
CD Pipeline / post-deploy-checks (push) Has been cancelled
CD Pipeline / build-and-deploy (push) Has been cancelled
AWOOOI Harbor 110 Local Repair / workflow-shape (push) Successful in 1s
AWOOOI Harbor 110 Local Repair / harbor-110-local-repair (push) Successful in 17s
This commit is contained in:
@@ -1752,6 +1752,200 @@ class PostgresAgent99DispatchLedger:
|
||||
"runtime_closure_verified": False,
|
||||
}
|
||||
|
||||
async def record_outcome_timeout_no_write_terminal(
|
||||
self,
|
||||
*,
|
||||
identity: Agent99DispatchIdentity,
|
||||
) -> dict[str, Any]:
|
||||
"""Fail an expired run without replaying transport or claiming closure."""
|
||||
|
||||
now = _utc_now_naive()
|
||||
error_code = "E-AGENT99-OUTCOME-TIMEOUT"
|
||||
try:
|
||||
async with get_db_context(identity.project_id) as db:
|
||||
current_result = await db.execute(
|
||||
select(
|
||||
AwoooPRunState.state,
|
||||
AwoooPRunState.error_detail,
|
||||
AwoooPRunState.timeout_at,
|
||||
)
|
||||
.where(
|
||||
AwoooPRunState.run_id == identity.run_id,
|
||||
AwoooPRunState.project_id == identity.project_id,
|
||||
)
|
||||
.with_for_update()
|
||||
)
|
||||
current = current_result.one_or_none()
|
||||
envelope = _parse_envelope(
|
||||
current.error_detail if current is not None else None
|
||||
)
|
||||
identity_matches = bool(
|
||||
isinstance(envelope, dict)
|
||||
and _stage_identity_matches(
|
||||
identity,
|
||||
envelope.get("identity")
|
||||
if isinstance(envelope.get("identity"), dict)
|
||||
else {},
|
||||
)
|
||||
)
|
||||
if (
|
||||
current is not None
|
||||
and current.state == "failed"
|
||||
and identity_matches
|
||||
and envelope.get("outcome_timeout_no_write_terminal") is True
|
||||
):
|
||||
return {
|
||||
**envelope,
|
||||
"status": "outcome_timeout_no_write_terminal_idempotent",
|
||||
"receipt_persisted": True,
|
||||
"runtime_closure_verified": False,
|
||||
}
|
||||
if current is None or not identity_matches:
|
||||
return {
|
||||
"status": "outcome_timeout_run_identity_invalid_fail_closed",
|
||||
"receipt_persisted": False,
|
||||
"runtime_closure_verified": False,
|
||||
}
|
||||
timeout_at = current.timeout_at
|
||||
if (
|
||||
current.state != "waiting_tool"
|
||||
or timeout_at is None
|
||||
or timeout_at > now
|
||||
):
|
||||
return {
|
||||
"status": "outcome_timeout_not_due_no_write",
|
||||
"receipt_persisted": False,
|
||||
"runtime_closure_verified": False,
|
||||
}
|
||||
|
||||
prior_controlled_apply_authorized = bool(
|
||||
envelope.get("controlled_apply_authorized") is True
|
||||
)
|
||||
prior_runtime_execution_authorized = bool(
|
||||
envelope.get("runtime_execution_authorized") is True
|
||||
)
|
||||
prior_runtime_execution_attempted = bool(
|
||||
envelope.get("runtime_execution_attempted") is True
|
||||
)
|
||||
terminal_receipt = {
|
||||
"schema_version": "agent99_outcome_timeout_no_write_v1",
|
||||
"status": "authenticated_outcome_timeout",
|
||||
**_stage_identity(identity),
|
||||
"timeout_at": timeout_at.isoformat(),
|
||||
"terminal_at": now.isoformat(),
|
||||
"terminalizer_runtime_write_performed": False,
|
||||
"original_runtime_write_status": (
|
||||
"unknown_without_authenticated_outcome"
|
||||
),
|
||||
"transport_replayed": False,
|
||||
"incident_resolution_authorized": False,
|
||||
"learning_writeback_authorized": False,
|
||||
"telegram_authorized": False,
|
||||
"stores_raw_evidence": False,
|
||||
}
|
||||
envelope.update({
|
||||
"status": "outcome_timeout_no_write_terminal",
|
||||
"run_terminal_state": "failed",
|
||||
"outcome_timeout_no_write_terminal": True,
|
||||
"prior_controlled_apply_authorized": (
|
||||
prior_controlled_apply_authorized
|
||||
),
|
||||
"prior_runtime_execution_authorized": (
|
||||
prior_runtime_execution_authorized
|
||||
),
|
||||
"runtime_execution_attempted": (
|
||||
prior_runtime_execution_attempted
|
||||
),
|
||||
"controlled_apply_authorized": False,
|
||||
"runtime_execution_authorized": False,
|
||||
"terminalizer_runtime_write_performed": False,
|
||||
"outcome_runtime_write_status": (
|
||||
"unknown_without_authenticated_outcome"
|
||||
),
|
||||
"transport_replayed": False,
|
||||
"automation_execution_success": False,
|
||||
"post_verifier_passed": False,
|
||||
"runtime_closure_verified": False,
|
||||
"closure_complete": False,
|
||||
"incident_resolution_authorized": False,
|
||||
"safe_next_action": (
|
||||
"await_new_source_recurrence_after_agent99_relay_recovery"
|
||||
),
|
||||
"verifier": {
|
||||
**_stage_identity(identity),
|
||||
"step_seq": 2,
|
||||
"status": "failed_authenticated_outcome_timeout",
|
||||
"post_verifier_passed": False,
|
||||
"receipt": terminal_receipt,
|
||||
},
|
||||
"learning_writeback": {
|
||||
**_stage_identity(identity),
|
||||
"step_seq": 3,
|
||||
"status": "not_applicable_outcome_timeout_no_write",
|
||||
"required_receipts": [],
|
||||
"receipt_refs": {},
|
||||
"stores_raw_evidence": False,
|
||||
},
|
||||
})
|
||||
envelope_json = _stable_json(envelope)
|
||||
updated = await db.execute(
|
||||
update(AwoooPRunState)
|
||||
.where(
|
||||
AwoooPRunState.run_id == identity.run_id,
|
||||
AwoooPRunState.project_id == identity.project_id,
|
||||
AwoooPRunState.state == "waiting_tool",
|
||||
AwoooPRunState.timeout_at <= now,
|
||||
)
|
||||
.values(
|
||||
state="failed",
|
||||
output_sha256=_sha256(envelope_json),
|
||||
error_code=error_code,
|
||||
error_detail=envelope_json,
|
||||
heartbeat_at=now,
|
||||
completed_at=now,
|
||||
lease_until=None,
|
||||
next_attempt_at=None,
|
||||
worker_id=None,
|
||||
)
|
||||
.returning(AwoooPRunState.run_id)
|
||||
)
|
||||
persisted = updated.scalar_one_or_none() is not None
|
||||
if persisted:
|
||||
await db.execute(
|
||||
update(AwoooPRunStepJournal)
|
||||
.where(
|
||||
AwoooPRunStepJournal.run_id == identity.run_id,
|
||||
AwoooPRunStepJournal.step_seq.in_([2, 3]),
|
||||
AwoooPRunStepJournal.result_status == "pending",
|
||||
)
|
||||
.values(
|
||||
output_hash=_sha256(_stable_json(terminal_receipt)),
|
||||
compensation_json=terminal_receipt,
|
||||
result_status="failed",
|
||||
error_code=error_code,
|
||||
was_blocked=True,
|
||||
block_reason="authenticated_outcome_timeout_no_write",
|
||||
completed_at=now,
|
||||
)
|
||||
)
|
||||
except Exception as exc:
|
||||
logger.warning(
|
||||
"agent99_outcome_timeout_terminal_failed_fail_closed",
|
||||
incident_id=identity.incident_id,
|
||||
run_id=str(identity.run_id),
|
||||
error=str(exc),
|
||||
)
|
||||
return {
|
||||
"status": "outcome_timeout_terminal_persistence_failed",
|
||||
"receipt_persisted": False,
|
||||
"runtime_closure_verified": False,
|
||||
}
|
||||
return {
|
||||
**envelope,
|
||||
"receipt_persisted": persisted,
|
||||
"runtime_closure_verified": False,
|
||||
}
|
||||
|
||||
async def record_learning_writeback(
|
||||
self,
|
||||
*,
|
||||
@@ -2236,6 +2430,7 @@ class PostgresAgent99DispatchLedger:
|
||||
AwoooPRunState.run_id,
|
||||
AwoooPRunState.state,
|
||||
AwoooPRunState.error_detail,
|
||||
AwoooPRunState.timeout_at,
|
||||
)
|
||||
.where(
|
||||
AwoooPRunState.project_id == (project_id or "awoooi"),
|
||||
@@ -2309,6 +2504,7 @@ class PostgresAgent99DispatchLedger:
|
||||
"identity": identity,
|
||||
"run_state": str(row.state),
|
||||
"receipt": envelope,
|
||||
"timeout_at": getattr(row, "timeout_at", None),
|
||||
})
|
||||
return items
|
||||
|
||||
|
||||
Reference in New Issue
Block a user