fix(plan-correction): implement list_by_decision in CorrectionAttemptRepository
CI / benchmark-publish (pull_request) Has been skipped
CI / push-validation (pull_request) Successful in 47s
CI / helm (pull_request) Successful in 56s
CI / build (pull_request) Successful in 1m12s
CI / quality (pull_request) Successful in 1m30s
CI / lint (pull_request) Failing after 1m30s
CI / typecheck (pull_request) Successful in 1m43s
CI / benchmark-regression (pull_request) Failing after 1m24s
CI / security (pull_request) Successful in 1m56s
CI / integration_tests (pull_request) Successful in 3m35s
CI / e2e_tests (pull_request) Successful in 4m25s
CI / unit_tests (pull_request) Failing after 5m2s
CI / coverage (pull_request) Has been skipped
CI / docker (pull_request) Has been skipped
CI / status-check (pull_request) Failing after 3s

The domain protocol (CorrectionRepositoryProtocol.list_by_decision) was
added in the previous commit but the concrete SQLAlchemy-backed adapter
was missing this method, causing a run-time AttributeError whenever code
called the adapter.  This resolves the spec-compliance blocker from
PR review #7887 (issue #8531).

Changes:
- CorrectionAttemptRepository.list_by_decision(decision_id, *, new_only=False)
  queries CorrectionAttemptModel rows by original_decision_id with optional
  filtering on terminal states
- Follows the same @database_retry + session pattern as other repository methods
- Properly wraps DB errors in DatabaseError

ISSUES CLOSED: #8531
This commit is contained in:
2026-05-08 07:01:18 +00:00
parent 6cec7a9672
commit cf460fdcf9
@@ -6056,6 +6056,50 @@ class CorrectionAttemptRepository:
f"Failed to list correction attempts for plan {plan_id}: {exc}"
) from exc
# --- LIST BY DECISION --------------------------------------------------
@database_retry
def list_by_decision(
self,
decision_id: str,
*,
new_only: bool = False,
) -> list[CorrectionAttemptRecord]:
"""List all correction attempts targeting a given decision.
Args:
decision_id: Decision ULID — the ``original_decision_id`` of
``CorrectionAttemptModel`` rows to filter on.
new_only: If ``True``, only return corrections that have not yet
completed (i.e. state is neither ``"complete"`` nor
``"failed"``). Defaults to ``False`` (returns all history).
Returns:
List of ``CorrectionAttemptRecord`` domain objects, ordered by
creation time ascending.
Raises:
DatabaseError: On transient or unexpected DB errors.
"""
session = self._session()
try:
query = session.query(CorrectionAttemptModel).filter_by(
original_decision_id=decision_id,
)
if new_only:
query = query.filter(
CorrectionAttemptModel.state.notin_(
("complete", "failed"),
)
)
rows = query.order_by(CorrectionAttemptModel.created_at).all()
return [row.to_domain() for row in rows]
except (OperationalError, SQLAlchemyDatabaseError) as exc:
raise DatabaseError(
f"Failed to list correction attempts for decision "
f"{decision_id}: {exc}"
) from exc
# --- UPDATE STATE ------------------------------------------------------
@database_retry