diff --git a/docs/specification.md b/docs/specification.md index f0803345d..5abfab6c4 100644 --- a/docs/specification.md +++ b/docs/specification.md @@ -47095,3 +47095,172 @@ These architectural invariants must be maintained across all milestones: 8. **BDD tests**: All unit-level tests expressed as Behave/Gherkin scenarios. No xUnit-style tests. 9. **File size limit**: No source file exceeds 500 lines. Split into modules if approaching limit. 10. **Atomic commits**: One logical change per commit. No mixed concerns. + +--- + +## Plan Correction Engine (v3.2.0) + +### Overview + +The Plan Correction Engine allows users to retroactively correct decisions in a plan's decision tree. It supports two correction modes: + +- **Revert mode** (`--mode revert`): Re-executes the plan from the targeted decision point with new guidance. The affected subtree is recomputed; only the branch from the target decision forward is re-executed. +- **Append mode** (`--mode append`): Adds guidance to the plan without recomputing past decisions. Future decisions will include the correction annotation in their context. + +Correction triggers selective subtree recomputation — only the affected branch is re-executed, not the entire plan. + +### Module Boundaries + +- **Module**: `cleveragents.corrections` +- **Layer**: Application (orchestrates Domain modules) +- **Responsibilities**: + - Accepting correction requests from CLI + - Identifying the affected subtree for recomputation + - Orchestrating re-execution of the affected subtree + - Merging corrected results back into the plan + - Persisting correction history +- **Public Interfaces**: + - `PlanCorrectionEngine` — main entry point for correction operations + - `CorrectionRequest` — value object representing a correction request + - `CorrectionResult` — value object representing the outcome of a correction +- **Allowed Dependencies**: + - `cleveragents.decisions.DecisionTreeQuery` — to find affected subtree + - `cleveragents.plans.PlanExecutor` — to re-execute affected subtree + - `cleveragents.invariants.InvariantEnforcer` — to apply invariants during re-execution +- **Forbidden Dependencies**: Must not import from `cleveragents.cli` or `cleveragents.tui` + +### Data Models + +#### CorrectionRequest + +```python +@dataclass +class CorrectionRequest: + plan_id: UUID + target_decision_id: UUID # The decision to correct from + mode: Literal["revert", "append"] + guidance: str # User-provided correction guidance + requested_by: str + requested_at: datetime +``` + +#### CorrectionResult + +```python +@dataclass +class CorrectionResult: + correction_id: UUID + plan_id: UUID + mode: Literal["revert", "append"] + affected_decision_ids: list[UUID] # Decisions that were recomputed (revert mode) + new_decision_ids: list[UUID] # New decisions created + status: Literal["success", "partial", "failed"] + summary: str +``` + +#### CorrectionRecord (persisted) + +```python +@dataclass +class CorrectionRecord: + id: UUID + plan_id: UUID + target_decision_id: UUID + mode: Literal["revert", "append"] + guidance: str + requested_by: str + requested_at: datetime + completed_at: Optional[datetime] + status: Literal["pending", "running", "success", "failed"] + result: Optional[CorrectionResult] +``` + +### Database Schema + +```sql +CREATE TABLE correction_records ( + id UUID PRIMARY KEY, + plan_id UUID NOT NULL REFERENCES plans(id) ON DELETE CASCADE, + target_decision_id UUID NOT NULL REFERENCES decisions(id), + mode VARCHAR(10) NOT NULL CHECK (mode IN ('revert', 'append')), + guidance TEXT NOT NULL, + requested_by VARCHAR(255) NOT NULL, + requested_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + completed_at TIMESTAMPTZ, + status VARCHAR(20) NOT NULL DEFAULT 'pending' + CHECK (status IN ('pending', 'running', 'success', 'failed')), + result JSONB +); + +CREATE INDEX idx_corrections_plan_id ON correction_records(plan_id); +CREATE INDEX idx_corrections_target_decision ON correction_records(target_decision_id); +``` + +### Correction Algorithms + +#### Revert Mode Algorithm + +1. Identify the target decision node in the decision tree via `DecisionTreeQuery` +2. Find all descendant decisions (the "affected subtree") using `DecisionTreeQuery.get_subtree(target_decision_id)` +3. Mark all affected decisions as `superseded` in the database +4. Re-execute the plan from the target decision point with the correction guidance injected into the LLM context +5. New decisions are recorded via `DecisionRecorder` and replace the superseded subtree +6. Plan state is updated to reflect the new execution path +7. `CorrectionRecord` is updated with status `success` and the result + +#### Append Mode Algorithm + +1. Identify the target decision node +2. Add the correction guidance as a `CorrectionAnnotation` on the target decision (stored in `correction_records`) +3. Do NOT recompute any past decisions +4. Future decisions in the plan will include the correction annotation in their context window +5. `CorrectionRecord` is updated with status `success` + +### CLI Interface Specification + +#### `agents plan correct --mode revert --decision --guidance ""` + +- Triggers revert-mode correction +- Required flags: `--mode revert|append`, `--decision `, `--guidance ` +- Optional flags: + - `--dry-run` — shows what would be recomputed without executing + - `--yes` — skip confirmation prompt +- Shows progress as subtree is recomputed +- Output: Summary of affected decisions and new decisions created + +#### `agents plan correct --mode append --decision --guidance ""` + +- Triggers append-mode correction +- Adds guidance annotation without recomputation +- Output: Confirmation with correction record ID + +### Integration Points + +| Integration | Direction | Description | +|---|---|---| +| Decision Recording System | Uses | `DecisionTreeQuery` to identify affected subtree; new decisions recorded via `DecisionRecorder` | +| Plan Executor | Uses | Re-execution uses `PlanExecutor` with correction context injected | +| Invariant System | Uses | Active invariants re-applied during revert-mode re-execution | +| CLI | Called by | `agents plan correct` command delegates to `PlanCorrectionEngine` | + +### Error Handling + +- `CorrectionTargetNotFoundError(decision_id)` — target decision does not exist +- `CorrectionConflictError(plan_id)` — another correction is already running on this plan +- `CorrectionSubtreeError(decision_id, reason)` — subtree identification failed +- `CorrectionExecutionError(correction_id, reason)` — re-execution failed + +All errors are application-layer exceptions (not domain or infrastructure exceptions). + +### Cross-Cutting Concerns + +- **Idempotency**: Each correction gets a unique ID; re-submitting the same correction is a no-op if already completed +- **Concurrency**: Only one correction may run per plan at a time (enforced by optimistic locking on `correction_records.status`) +- **Logging**: All correction operations logged at INFO level with correction ID and plan ID +- **Audit Trail**: All corrections are persisted in `correction_records` for audit purposes +- **Dry Run**: `--dry-run` flag shows the affected subtree without executing; useful for previewing impact + +--- +**Automated by CleverAgents Bot** +Supervisor: Architecture | Agent: architecture-pool-supervisor +Worker: [AUTO-ARCH-3B]