- 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
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
InvalidCorrectionStateTransitionErrorfor invalid transitions. - Auto-sets
completed_attodatetime.now(UTC)when transitioning to a terminal state ifcompleted_atis not explicitly provided. - Raises
ValueErrorifcompleted_atis provided for a non-terminal transition. new_decision_idis 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 |