Files
temp/docs/reference/correction_attempts.md
freemo 700543f87e docs(v3.7.0): update API docs, README, architecture overview, and changelog
- README: add v3.7.0 highlights (first-run UX, estimation lifecycle,
  enriched domain events, correction attempts, devcontainer handler,
  A2A ValueError mapping); add What's New section; add doc links
- CHANGELOG: merge 'Unreleased (pre-3.7.0)' into v3.7.0 as a
  subsection; clear [Unreleased] block
- docs/reference/architecture_overview.md (new): high-level system
  layers, core abstractions, key services, protocols (A2A/MCP/LSP),
  estimation lifecycle, TUI architecture, server mode, observability
- docs/reference/estimation_lifecycle.md (new): EstimationResult
  model reference, configuration, PLAN_ESTIMATION_COMPLETE event,
  plan.cost_estimate_usd, writing a custom estimation actor
- docs/reference/correction_attempts.md (new): CorrectionAttemptRecord
  schema, state machine, CorrectionAttemptRepository API, DDL,
  CorrectionDryRunReport migration guide (removed redundant fields)
- docs/reference/tui.md: add first-run experience section
  (ActorSelectionOverlay, is_first_run, create_default_persona_for_actor),
  session export/import TUI section, persona export/import TUI section;
  update architecture module table with first_run.py and
  actor_selection_overlay.py entries
- mkdocs.yml: add Architecture Overview to top-level nav

ISSUES CLOSED: #1310 #1087 #1242 #1241 #891 #996 #1001
2026-04-05 09:21:53 +00:00

6.0 KiB

Correction Attempts

The correction_attempts table records every attempt to correct a decision in a plan's decision tree. Each attempt tracks the original decision being corrected, the guidance provided, the new decision produced, and the lifecycle state of the correction process.

Introduced in v3.7.0 (issue #1087). See also decision_correction.md for the correction workflow and decision_model.md for the decision domain model.

Module: cleveragents.infrastructure.database.repositories Domain model: cleveragents.domain.models.core.correction


Overview

When plan correct is invoked, the CorrectionService creates a CorrectionAttemptRecord to track the correction lifecycle:

pending ──► executing ──► complete
                       └──► failed

Terminal states (complete, failed) are irreversible. Once a correction attempt reaches a terminal state, update_state() rejects further transitions.


CorrectionAttemptRecord

Field Type Constraints Description
id str UUID, required Unique attempt identifier
plan_id str FK → plans, RESTRICT Plan containing the decision
original_decision_id str FK → decisions, RESTRICT Decision being corrected
new_decision_id str | None FK → decisions, nullable Replacement decision (set on complete)
guidance str max 10 000 chars, non-empty Human or actor guidance for the correction
mode CorrectionMode enum Correction mode (append, replace, rollback)
state CorrectionAttemptState enum Current lifecycle state
created_at datetime UTC, millisecond precision When the attempt was created
completed_at datetime | None UTC, millisecond precision When the attempt reached a terminal state

CorrectionAttemptState

Value Description
pending Attempt created, not yet executing
executing Correction actor is running
complete Correction succeeded; new_decision_id is set
failed Correction failed; new_decision_id is None

Valid Transitions

From To
pending executing
executing complete
executing failed

All other transitions raise InvalidCorrectionStateTransitionError.


CorrectionAttemptRepository

Module: cleveragents.infrastructure.database.repositories

Methods

create(attempt: CorrectionAttemptRecord) -> CorrectionAttemptRecord

Persist a new correction attempt. Raises ValueError with a descriptive message if plan_id or original_decision_id violates a foreign-key constraint.

attempt = CorrectionAttemptRecord(
    id=str(uuid4()),
    plan_id="plan-abc",
    original_decision_id="decision-xyz",
    guidance="The file path should use forward slashes.",
    mode=CorrectionMode.REPLACE,
    state=CorrectionAttemptState.PENDING,
    created_at=datetime.now(UTC),
)
saved = repo.create(attempt)

get(attempt_id: str) -> CorrectionAttemptRecord | None

Retrieve a correction attempt by ID. Returns None if not found.

list_by_plan(plan_id: str) -> list[CorrectionAttemptRecord]

Return all correction attempts for a plan, ordered by created_at ascending.

update_state(attempt_id, new_state, *, new_decision_id=None, completed_at=None) -> CorrectionAttemptRecord

Transition the attempt to a new state. Rules:

  • Validates the transition against CORRECTION_ATTEMPT_VALID_TRANSITIONS.
  • Raises InvalidCorrectionStateTransitionError for invalid transitions.
  • Auto-sets completed_at to datetime.now(UTC) when transitioning to a terminal state if completed_at is not explicitly provided.
  • Raises ValueError if completed_at is provided for a non-terminal transition.
  • new_decision_id is stripped of whitespace before storage.

State Transition Constants

from cleveragents.domain.models.core.correction import (
    CORRECTION_ATTEMPT_VALID_TRANSITIONS,
    CORRECTION_ATTEMPT_TERMINAL_STATES,
)

# CORRECTION_ATTEMPT_VALID_TRANSITIONS: dict[CorrectionAttemptState, set[CorrectionAttemptState]]
# CORRECTION_ATTEMPT_TERMINAL_STATES: frozenset[CorrectionAttemptState]

validate_correction_state_transition(current, new) raises InvalidCorrectionStateTransitionError if the transition is not in CORRECTION_ATTEMPT_VALID_TRANSITIONS.


Database Schema

CREATE TABLE correction_attempts (
    id                   TEXT PRIMARY KEY,
    plan_id              TEXT NOT NULL REFERENCES plans(id) ON DELETE RESTRICT,
    original_decision_id TEXT NOT NULL REFERENCES decisions(id) ON DELETE RESTRICT,
    new_decision_id      TEXT REFERENCES decisions(id) ON DELETE RESTRICT,
    guidance             TEXT NOT NULL,
    mode                 TEXT NOT NULL,
    state                TEXT NOT NULL DEFAULT 'pending',
    created_at           TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%f', 'now')),
    completed_at         TEXT
);

FK semantics: Both original_decision_id and new_decision_id use RESTRICT (not CASCADE) to preserve the correction audit trail when decisions are cleaned up.


CorrectionDryRunReport

CorrectionService.generate_dry_run_report() returns a CorrectionDryRunReport. Redundant top-level fields were removed in v3.7.0 (issue #1087); consumers must now access correction impact data through the embedded impact sub-object:

Old field (removed) New canonical path
excluded_decisions report.impact.excluded_decisions
rollback_tier_depth report.impact.rollback_tier_depth
child_plans_to_rollback report.impact.affected_child_plans