Files
cleveragents-core/features/consolidated_correction.feature
HAL9000 f808abff86 chore(ci): fix pre-commit hook failures
Fix JSON syntax errors in .devcontainer/devcontainer.json (removed
invalid JS-style // comments) and .devcontainer/opencode.json (removed
90+ trailing commas). Apply auto-fixes for end-of-file and trailing
whitespace issues across 100+ files. Fix SIM105 ruff violations in
benchmarks/core_circuit_breaker_bench.py (use contextlib.suppress).

Note: The security fix from issue #7478 (validate_path startswith bypass)
was already delivered to master in commit e18ac5f2. This PR as currently
structured is non-atomic (35 commits across 10+ issues) and needs
significant restructure before merge. This commit only addresses the
CI/pre-commit failures.

ISSUES CLOSED: #7478
2026-06-14 09:51:14 -04:00

912 lines
43 KiB
Gherkin
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
Feature: Consolidated Correction
Combined scenarios from: correction_flows, correction_model, correction_service_coverage_r2, correction_service_new_coverage
# ============================================================
# Originally from: correction_flows.feature
# Feature: Correction flows revert and append
# ============================================================
Scenario: Simple revert invalidates target decision
Given a correction flow service
And a correction request for plan "P1" targeting decision "D1" in revert mode
And a decision tree where "D1" has no children
When I execute the revert correction
Then the correction flow result status should be "applied"
And the reverted decisions should contain "D1"
Scenario: Revert deep subtree invalidates all descendants
Given a correction flow service
And a correction request for plan "P1" targeting decision "D1" in revert mode
And a multi-parent decision tree with "D1" children "D2,D3" and "D2" children "D4,D5"
When I execute the revert correction
Then the reverted decisions should contain "D1"
And the reverted decisions should contain "D2"
And the reverted decisions should contain "D3"
And the reverted decisions should contain "D4"
And the reverted decisions should contain "D5"
Scenario: Revert archives artifacts for each affected decision
Given a correction flow service
And a correction request for plan "P1" targeting decision "D1" in revert mode
And a simple decision tree where "D1" has children "D2"
When I execute the revert correction
Then the archived artifacts should include "D1.artifact"
And the archived artifacts should include "D2.artifact"
Scenario: Revert correction transitions through expected statuses
Given a correction flow service
And a correction request for plan "P1" targeting decision "D1" in revert mode
Then the correction flow status should be "pending"
When I execute the revert correction with a decision tree where "D1" has no children
Then the correction flow status should be "applied"
# ───────────────────────────────────────────────────────────
# Append scenarios
# ───────────────────────────────────────────────────────────
Scenario: Simple append spawns child plan
Given a correction flow service
And a correction request for plan "P1" targeting decision "D1" in append mode
When I execute the append correction
Then the correction flow result status should be "applied"
And the result should have a spawned child plan id
Scenario: Append with guidance preserves guidance on request
Given a correction flow service
And a correction request for plan "P1" targeting decision "D1" in append mode with guidance "Fix auth module"
When I execute the append correction
Then the correction flow guidance should be "Fix auth module"
Scenario: Append creates new decision in result
Given a correction flow service
And a correction request for plan "P1" targeting decision "D1" in append mode
When I execute the append correction
Then the result new decisions should not be empty
# ───────────────────────────────────────────────────────────
# Impact analysis
# ───────────────────────────────────────────────────────────
Scenario: Impact analysis with BFS subtree traversal
Given a correction flow service
And a correction request for plan "P1" targeting decision "D1" in revert mode
And a multi-parent decision tree with "D1" children "D2,D3" and "D3" children "D6"
When I analyze the impact
Then the affected decisions should be "D1,D2,D3,D6"
Scenario: Impact analysis risk level low for small subtree
Given a correction flow service
And a correction request for plan "P1" targeting decision "D1" in revert mode
And a simple decision tree where "D1" has children "D2"
When I analyze the impact
Then the risk level should be "low"
Scenario: Impact analysis risk level medium for moderate subtree
Given a correction flow service
And a correction request for plan "P1" targeting decision "D1" in revert mode
And a decision tree with 7 nodes rooted at "D1"
When I analyze the impact
Then the risk level should be "medium"
Scenario: Impact analysis risk level high for large subtree
Given a correction flow service
And a correction request for plan "P1" targeting decision "D1" in revert mode
And a decision tree with 15 nodes rooted at "D1"
When I analyze the impact
Then the risk level should be "high"
Scenario: Impact analysis on leaf node returns only the target
Given a correction flow service
And a correction request for plan "P1" targeting decision "LEAF" in revert mode
And an empty decision tree
When I analyze the impact
Then the affected decisions should be "LEAF"
# ───────────────────────────────────────────────────────────
# Dry-run
# ───────────────────────────────────────────────────────────
Scenario: Dry-run report generation for revert
Given a correction flow service
And a correction request for plan "P1" targeting decision "D1" in revert mode
And a simple decision tree where "D1" has children "D2,D3"
When I generate a dry-run report
Then the report mode should be "revert"
And the report decisions to invalidate should contain "D1"
And the report estimated recompute time should be greater than 0
Scenario: Dry-run report generates warnings for high risk
Given a correction flow service
And a correction request for plan "P1" targeting decision "D1" in revert mode
And a decision tree with 15 nodes rooted at "D1"
When I generate a dry-run report
Then the report warnings should contain "High risk"
Scenario: Dry-run report generates warnings for medium risk
Given a correction flow service
And a correction request for plan "P1" targeting decision "D1" in revert mode
And a decision tree with 7 nodes rooted at "D1"
When I generate a dry-run report
Then the report warnings should contain "Medium risk"
Scenario: Dry-run report for append has no decisions to invalidate
Given a correction flow service
And a correction request for plan "P1" targeting decision "D1" in append mode
When I generate a dry-run report
Then the report decisions to invalidate should be empty
# ───────────────────────────────────────────────────────────
# Error handling
# ───────────────────────────────────────────────────────────
Scenario: Get correction with invalid id raises not found
Given a correction flow service
When I try to get correction flow "nonexistent-id"
Then a correction flow resource not found error should be raised
Scenario: Execute already applied correction raises validation error
Given a correction flow service
And a correction request for plan "P1" targeting decision "D1" in revert mode
And I execute the revert correction with a decision tree where "D1" has no children
When I try to execute the revert correction again
Then a validation error should be raised for status
Scenario: Execute already cancelled correction raises validation error
Given a correction flow service
And a correction request for plan "P1" targeting decision "D1" in revert mode
And I cancel the correction flow
When I try to execute the revert correction again
Then a validation error should be raised for status
Scenario: Cancel already applied correction raises validation error
Given a correction flow service
And a correction request for plan "P1" targeting decision "D1" in revert mode
And I execute the revert correction with a decision tree where "D1" has no children
When I try to cancel the correction flow
Then a validation error should be raised for status
# ───────────────────────────────────────────────────────────
# Status transitions
# ───────────────────────────────────────────────────────────
Scenario: Correction status transitions from pending to applied on revert
Given a correction flow service
And a correction request for plan "P1" targeting decision "D1" in revert mode
When I execute the revert correction with a decision tree where "D1" has no children
Then the correction flow status should be "applied"
Scenario: Correction status transitions from pending to cancelled
Given a correction flow service
And a correction request for plan "P1" targeting decision "D1" in revert mode
When I cancel the correction flow
Then the correction flow status should be "cancelled"
# ───────────────────────────────────────────────────────────
# Multi-correction & listing
# ───────────────────────────────────────────────────────────
Scenario: Multiple corrections on same plan are listed correctly
Given a correction flow service
And a correction request for plan "P1" targeting decision "D1" in revert mode
And a correction request for plan "P1" targeting decision "D2" in append mode
When I list correction flows for plan "P1"
Then the correction list should have 2 items
Scenario: List corrections filters by plan id
Given a correction flow service
And a correction request for plan "P1" targeting decision "D1" in revert mode
And a correction request for plan "P2" targeting decision "D2" in append mode
When I list correction flows for plan "P1"
Then the correction list should have 1 items
# ───────────────────────────────────────────────────────────
# Cancellation
# ───────────────────────────────────────────────────────────
Scenario: Cancel pending correction succeeds
Given a correction flow service
And a correction request for plan "P1" targeting decision "D1" in revert mode
When I cancel the correction flow
Then the correction flow status should be "cancelled"
# ───────────────────────────────────────────────────────────
# Attempt tracking
# ───────────────────────────────────────────────────────────
Scenario: Execution records an attempt
Given a correction flow service
And a correction request for plan "P1" targeting decision "D1" in revert mode
When I execute the revert correction with a decision tree where "D1" has no children
Then the attempt list should have 1 entries
And the first attempt should be successful
Scenario: Append execution records an attempt
Given a correction flow service
And a correction request for plan "P1" targeting decision "D1" in append mode
When I execute the append correction
Then the attempt list should have 1 entries
# ───────────────────────────────────────────────────────────
# Model validation
# ───────────────────────────────────────────────────────────
Scenario: CorrectionRequest rejects empty plan_id
Given a correction flow service
When I try to create a correction with empty plan_id
Then a validation error should be raised for empty field
Scenario: CorrectionRequest rejects empty target_decision_id
Given a correction flow service
When I try to create a correction with empty target_decision_id
Then a validation error should be raised for empty field
Scenario: CorrectionImpact rejects invalid risk level
When I try to create a correction impact with risk level "critical"
Then a correction flow pydantic validation error should be raised
Scenario: CorrectionDryRunReport contains all expected fields
Given a correction flow service
And a correction request for plan "P1" targeting decision "D1" in revert mode
And a simple decision tree where "D1" has children "D2"
When I generate a dry-run report
Then the report should have a correction_id
And the report should have an impact object
And the report should have a warnings list
Scenario: Execute correction dispatches to revert
Given a correction flow service
And a correction request for plan "P1" targeting decision "D1" in revert mode
And a decision tree where "D1" has no children
When I execute correction via dispatch
Then the correction flow result status should be "applied"
And the reverted decisions should contain "D1"
Scenario: Execute correction dispatches to append
Given a correction flow service
And a correction request for plan "P1" targeting decision "D1" in append mode
When I execute correction via dispatch
Then the correction flow result status should be "applied"
And the result should have a spawned child plan id
# ============================================================
# Originally from: correction_model.feature
# Feature: Correction Model
# ============================================================
Scenario: Create correction request in revert mode
Given a correction service
When I request a correction for plan "plan-1" decision "DEC-001" in revert mode with guidance "Use FastAPI instead"
Then the correction should be created
And the correction mode should be "revert"
And the correction status should be "pending"
And the correction plan_id should be "plan-1"
And the correction target_decision_id should be "DEC-001"
And the correction guidance should be "Use FastAPI instead"
Scenario: Create correction request in append mode
Given a correction service
When I request a correction for plan "plan-2" decision "DEC-002" in append mode with guidance "Add caching layer"
Then the correction should be created
And the correction mode should be "append"
And the correction status should be "pending"
Scenario: Create dry-run correction request
Given a correction service
When I request a dry-run correction for plan "plan-1" decision "DEC-001" in revert mode with guidance "Use SQLite"
Then the correction should be created
And the correction dry_run should be true
# ── Impact Analysis ──────────────────────────────────────────
Scenario: Dry-run returns impact analysis
Given a correction service
And a correction request for plan "plan-1" decision "DEC-001" in revert mode with guidance "Change framework"
When I analyze the correction impact
Then the impact should list affected decisions
And the impact risk level should be "low"
And the correction status should be "analyzing"
# ── Cancellation ─────────────────────────────────────────────
Scenario: Cancel a pending correction
Given a correction service
And a correction request for plan "plan-1" decision "DEC-001" in revert mode with guidance "Change approach"
When I cancel the correction
Then the correction status should be "cancelled"
Scenario: Cancel an already-cancelled correction fails
Given a correction service
And a cancelled correction for plan "plan-1" decision "DEC-001"
When I try to cancel the correction
Then a validation error should be raised with message containing "cancelled"
Scenario: Cancel an applied correction fails
Given a correction service
And an applied correction for plan "plan-1" decision "DEC-001"
When I try to cancel the correction
Then a validation error should be raised with message containing "applied"
# ── Listing ──────────────────────────────────────────────────
Scenario: List corrections by plan
Given a correction service
And a correction request for plan "plan-1" decision "DEC-001" in revert mode with guidance "Fix endpoint"
And a correction request for plan "plan-1" decision "DEC-002" in append mode with guidance "Add logging"
And a correction request for plan "plan-2" decision "DEC-003" in revert mode with guidance "Change DB"
When I list corrections for plan "plan-1"
Then I should get 2 corrections
When I list corrections for plan "plan-2"
Then I should get 1 corrections
When I list all corrections
Then I should get 3 corrections
# ── Retrieval ────────────────────────────────────────────────
Scenario: Get correction by ID
Given a correction service
And a correction request for plan "plan-1" decision "DEC-001" in revert mode with guidance "Test retrieval"
When I get the correction by its ID
Then the correction should be found
And the correction guidance should be "Test retrieval"
Scenario: Get non-existent correction raises error
Given a correction service
When I try to get correction "nonexistent-id"
Then a resource not found error should be raised
# ── Validation ───────────────────────────────────────────────
Scenario: Invalid decision ID rejection
Given a correction service
When I try to request a correction with empty decision ID
Then a validation error should be raised with message containing "decision_id"
Scenario: Invalid plan ID rejection
Given a correction service
When I try to request a correction with empty plan ID
Then a validation error should be raised with message containing "plan_id"
Scenario: Empty guidance is accepted
Given a correction service
When I try to request a correction with empty guidance
Then the correction should be created
Scenario: Whitespace-only guidance is accepted
Given a correction service
When I try to request a correction with whitespace-only guidance
Then the correction should be created
# ── Status Transitions ───────────────────────────────────────
Scenario: Correction status transitions through execution
Given a correction service
And a correction request for plan "plan-1" decision "DEC-001" in revert mode with guidance "Execute me"
Then the correction status should be "pending"
When I execute the correction
Then the correction status should be "applied"
Scenario: Execute already-applied correction fails
Given a correction service
And an applied correction for plan "plan-1" decision "DEC-001"
When I try to execute the correction
Then a validation error should be raised with message containing "applied"
Scenario: Execute already-cancelled correction fails
Given a correction service
And a cancelled correction for plan "plan-1" decision "DEC-001"
When I try to execute the correction
Then a validation error should be raised with message containing "cancelled"
# ── Execution Result ─────────────────────────────────────────
Scenario: Revert execution produces reverted decisions
Given a correction service
And a correction request for plan "plan-1" decision "DEC-001" in revert mode with guidance "Revert this"
When I execute the correction
Then the result should have reverted decisions
And the result status should be "applied"
Scenario: Append execution produces no reverted decisions
Given a correction service
And a correction request for plan "plan-1" decision "DEC-001" in append mode with guidance "Append this"
When I execute the correction
Then the result should have no reverted decisions
And the result status should be "applied"
# ── Attempts ─────────────────────────────────────────────────
Scenario: Execution records an attempt
Given a correction service
And a correction request for plan "plan-1" decision "DEC-001" in revert mode with guidance "Track attempt"
When I execute the correction
Then there should be 1 attempt recorded
And the attempt should be successful
# ── Model Validation ─────────────────────────────────────────
Scenario: CorrectionRequest validates non-empty plan_id
When I try to create a CorrectionRequest with empty plan_id
Then a pydantic validation error should be raised
Scenario: CorrectionRequest validates non-empty target_decision_id
When I try to create a CorrectionRequest with empty target_decision_id
Then a pydantic validation error should be raised
Scenario: CorrectionRequest accepts empty guidance
When I try to create a CorrectionRequest with empty guidance
Then the correction should be created
Scenario: CorrectionImpact validates risk_level
When I try to create a CorrectionImpact with invalid risk_level "extreme"
Then a pydantic validation error should be raised
Scenario: CorrectionImpact accepts valid risk levels
When I create a CorrectionImpact with risk_level "low"
Then the impact should be created
When I create a CorrectionImpact with risk_level "medium"
Then the impact should be created
When I create a CorrectionImpact with risk_level "high"
Then the impact should be created
# ── Enum Tests ───────────────────────────────────────────────
Scenario: CorrectionMode enum values
Then CorrectionMode.REVERT should equal "revert"
And CorrectionMode.APPEND should equal "append"
Scenario: CorrectionStatus enum values
Then CorrectionStatus.PENDING should equal "pending"
And CorrectionStatus.ANALYZING should equal "analyzing"
And CorrectionStatus.EXECUTING should equal "executing"
And CorrectionStatus.APPLIED should equal "applied"
And CorrectionStatus.FAILED should equal "failed"
And CorrectionStatus.CANCELLED should equal "cancelled"
# ── CorrectionResult Model ──────────────────────────────────
Scenario: CorrectionResult with defaults
When I create a CorrectionResult with correction_id "corr-1" and status "applied"
Then the result should have empty new_decisions
And the result should have empty reverted_decisions
And the result error_message should be none
# ── CorrectionAttempt Model ─────────────────────────────────
Scenario: CorrectionAttempt default values
When I create a CorrectionAttempt for correction "corr-1"
Then the attempt success should be false
And the attempt completed_at should be none
And the attempt details should be empty
# ============================================================
# Originally from: correction_service_coverage_r2.feature
# Feature: CorrectionService exception-handling coverage (round 2)
# ============================================================
Scenario: execute_revert catches internal error and returns FAILED result
Given r2corr-a fresh correction service
Given r2corr-a stored revert correction for plan "P1" targeting "D1"
And r2corr-analyze_impact is patched to raise RuntimeError "boom in analyze"
When r2corr-I execute revert on the stored correction
Then r2corr-the revert result status should be "failed"
And r2corr-the revert result error_message should contain "boom in analyze"
And r2corr-the stored correction status should be "failed"
And r2corr-the first attempt should have success false
And r2corr-the first attempt details should contain error key
And r2corr-the first attempt details error should contain "boom in analyze"
And r2corr-the first attempt completed_at should be set
And r2corr-the result should be stored in the service results dict
Scenario: execute_revert exception path preserves attempt count
Given r2corr-a fresh correction service
Given r2corr-a stored revert correction for plan "P1" targeting "D2"
And r2corr-analyze_impact is patched to raise ValueError "bad value"
When r2corr-I execute revert on the stored correction
Then r2corr-the attempts for the stored correction should have 1 entry
And r2corr-the first attempt should have success false
Scenario: execute_revert exception with TypeError message
Given r2corr-a fresh correction service
Given r2corr-a stored revert correction for plan "P1" targeting "D3"
And r2corr-analyze_impact is patched to raise TypeError "wrong type"
When r2corr-I execute revert on the stored correction
Then r2corr-the revert result status should be "failed"
And r2corr-the revert result error_message should contain "wrong type"
And r2corr-the stored correction status should be "failed"
# -------------------------------------------------------------------
# execute_append exception path (lines 320-328)
# -------------------------------------------------------------------
Scenario: execute_append catches internal error and returns FAILED result
Given r2corr-a fresh correction service
Given r2corr-a stored append correction for plan "P1" targeting "A1"
And r2corr-ULID is patched to raise RuntimeError "ulid broken"
When r2corr-I execute append on the stored correction
Then r2corr-the append result status should be "failed"
And r2corr-the append result error_message should contain "ulid broken"
And r2corr-the stored correction status should be "failed"
And r2corr-the first attempt should have success false
And r2corr-the first attempt details should contain error key
And r2corr-the first attempt details error should contain "ulid broken"
And r2corr-the first attempt completed_at should be set
And r2corr-the append result should be stored in the service results dict
Scenario: execute_append exception path preserves attempt count
Given r2corr-a fresh correction service
Given r2corr-a stored append correction for plan "P1" targeting "A2"
And r2corr-ULID is patched to raise ValueError "ulid value error"
When r2corr-I execute append on the stored correction
Then r2corr-the attempts for the stored correction should have 1 entry
And r2corr-the first attempt should have success false
Scenario: execute_append exception with OSError message
Given r2corr-a fresh correction service
Given r2corr-a stored append correction for plan "P1" targeting "A3"
And r2corr-ULID is patched to raise OSError "system failure"
When r2corr-I execute append on the stored correction
Then r2corr-the append result status should be "failed"
And r2corr-the append result error_message should contain "system failure"
And r2corr-the stored correction status should be "failed"
# -------------------------------------------------------------------
# execute_correction dispatch into exception paths
# -------------------------------------------------------------------
Scenario: dispatch to revert hits exception path when analyze_impact fails
Given r2corr-a fresh correction service
Given r2corr-a stored revert correction for plan "P1" targeting "DR1"
And r2corr-analyze_impact is patched to raise RuntimeError "dispatch revert boom"
When r2corr-I dispatch execute correction on the stored correction
Then r2corr-the dispatch result status should be "failed"
And r2corr-the dispatch result error_message should contain "dispatch revert boom"
Scenario: dispatch to append hits exception path when ULID fails
Given r2corr-a fresh correction service
Given r2corr-a stored append correction for plan "P1" targeting "DA1"
And r2corr-ULID is patched to raise RuntimeError "dispatch append boom"
When r2corr-I dispatch execute correction on the stored correction
Then r2corr-the dispatch result status should be "failed"
And r2corr-the dispatch result error_message should contain "dispatch append boom"
# ============================================================
# Originally from: correction_service_new_coverage.feature
# Feature: CorrectionService comprehensive coverage
# ============================================================
Scenario: Create a revert correction with valid parameters
Given a fresh correction service instance
When I create a correction for plan "PX1" targeting "DX1" in revert mode
Then the new correction should be stored in the service
And the new correction plan_id should equal "PX1"
And the new correction target_decision_id should equal "DX1"
And the new correction mode should equal "revert"
And the new correction status should equal "pending"
And the new correction dry_run should be false
Scenario: Create correction with empty plan_id raises ValidationError
Given a fresh correction service instance
When I attempt to create a correction with an empty plan_id
Then a service ValidationError should have been raised
Scenario: Create correction with empty target_decision_id raises ValidationError
Given a fresh correction service instance
When I attempt to create a correction with an empty target_decision_id
Then a service ValidationError should have been raised
Scenario: Create correction with guidance and dry_run flag
Given a fresh correction service instance
When I create a correction for plan "PX1" targeting "DX1" in append mode with guidance "Refactor auth" and dry_run true
Then the new correction guidance should equal "Refactor auth"
And the new correction dry_run should be true
And the new correction mode should equal "append"
# ── analyze_impact ───────────────────────────────────────────
Scenario: Analyze impact with no children yields single node and low risk
Given a fresh correction service instance
And a stored revert correction for plan "PX1" targeting "DX1"
When I analyze impact with an empty decision tree
Then the impact affected decisions should be exactly "DX1"
And the impact risk level should equal "low"
And the impact rollback tier should equal "full"
And the impact estimated cost should equal 1.5
And the impact artifacts to archive should be exactly "DX1.artifact"
And the impact affected files should be exactly "DX1.py"
Scenario: Analyze impact with deep BFS tree traversal
Given a fresh correction service instance
And a stored revert correction for plan "PX1" targeting "A"
When I analyze impact with tree "A->B,C;B->D,E;C->F"
Then the impact affected decisions should be exactly "A,B,C,D,E,F"
And the impact risk level should equal "medium"
Scenario: Analyze impact with medium risk subtree of 4 nodes
Given a fresh correction service instance
And a stored revert correction for plan "PX1" targeting "R"
When I analyze impact with tree "R->C1,C2,C3,C4"
Then the impact risk level should equal "medium"
And the impact affected decisions count should be 5
Scenario: Analyze impact with high risk subtree of 11 or more nodes
Given a fresh correction service instance
And a stored revert correction for plan "PX1" targeting "R"
When I analyze impact with a chain tree of 12 nodes rooted at "R"
Then the impact risk level should equal "high"
And the impact affected decisions count should be 12
Scenario: Analyze impact with non-existent correction raises ResourceNotFoundError
Given a fresh correction service instance
When I attempt to analyze impact for correction "bogus-id-999"
Then a service ResourceNotFoundError should have been raised
Scenario: Analyze impact sets status to analyzing
Given a fresh correction service instance
And a stored revert correction for plan "PX1" targeting "DX1"
When I analyze impact with an empty decision tree
Then the stored correction status should be "analyzing"
Scenario: Analyze impact for append mode yields append_only rollback tier
Given a fresh correction service instance
And a stored append correction for plan "PX1" targeting "DX1"
When I analyze impact with an empty decision tree
Then the impact rollback tier should equal "append_only"
# ── generate_dry_run_report ──────────────────────────────────
Scenario: Dry-run report for revert with high risk includes high-risk warning
Given a fresh correction service instance
And a stored revert correction for plan "PX1" targeting "R"
When I generate a dry-run report with a chain tree of 15 nodes rooted at "R"
Then the dry-run report mode should equal "revert"
And the dry-run report warnings should include "High risk"
And the dry-run report warnings should include "Revert will invalidate"
And the dry-run report decisions to invalidate should not be empty
And the dry-run report estimated recompute time should be 30.0
Scenario: Dry-run report for revert with medium risk includes medium-risk warning
Given a fresh correction service instance
And a stored revert correction for plan "PX1" targeting "R"
When I generate a dry-run report with a chain tree of 7 nodes rooted at "R"
Then the dry-run report warnings should include "Medium risk"
And the dry-run report warnings should include "Revert will invalidate"
Scenario: Dry-run report for append has empty decisions to invalidate
Given a fresh correction service instance
And a stored append correction for plan "PX1" targeting "DX1"
When I generate a dry-run report with an empty decision tree
Then the dry-run report decisions to invalidate should be empty
And the dry-run report mode should equal "append"
# ── execute_revert ───────────────────────────────────────────
Scenario: Execute revert with simple tree returns applied result
Given a fresh correction service instance
And a stored revert correction for plan "PX1" targeting "DX1"
When I execute revert with tree "DX1->DX2"
Then the revert result status should equal "applied"
And the revert result reverted decisions should include "DX1"
And the revert result reverted decisions should include "DX2"
And the revert result archived artifacts should include "DX1.artifact"
And the revert result archived artifacts should include "DX2.artifact"
Scenario: Execute revert records a successful attempt with completed_at
Given a fresh correction service instance
And a stored revert correction for plan "PX1" targeting "DX1"
When I execute revert with an empty decision tree
Then the service attempts for the correction should have 1 entry
And the first attempt should have success true
And the first attempt completed_at should not be none
# ── execute_append ───────────────────────────────────────────
Scenario: Execute append creates a child plan and new decision
Given a fresh correction service instance
And a stored append correction for plan "PX1" targeting "DX1"
When I execute append for the stored correction
Then the append result status should equal "applied"
And the append result spawned_child_plan_id should not be none
And the append result new decisions should not be empty
# ── execute_correction dispatch ──────────────────────────────
Scenario: Execute correction dispatches to revert for REVERT mode
Given a fresh correction service instance
And a stored revert correction for plan "PX1" targeting "DX1"
When I execute correction via the dispatch method with tree "DX1->DX2"
Then the dispatched result status should equal "applied"
And the dispatched result reverted decisions should include "DX1"
Scenario: Execute correction dispatches to append for APPEND mode
Given a fresh correction service instance
And a stored append correction for plan "PX1" targeting "DX1"
When I execute correction via the dispatch method with no tree
Then the dispatched result status should equal "applied"
And the dispatched result spawned_child_plan_id should not be none
# ── get_correction ───────────────────────────────────────────
Scenario: Get correction with valid id returns the request
Given a fresh correction service instance
And a stored revert correction for plan "PX1" targeting "DX1"
When I get the stored correction by its id
Then the retrieved correction plan_id should equal "PX1"
And the retrieved correction target_decision_id should equal "DX1"
Scenario: Get correction with invalid id raises ResourceNotFoundError
Given a fresh correction service instance
When I attempt to get correction with id "nonexistent-xyz"
Then a service ResourceNotFoundError should have been raised
# ── list_corrections ─────────────────────────────────────────
Scenario: List corrections without filter returns all corrections
Given a fresh correction service instance
And a stored revert correction for plan "PX1" targeting "DX1"
And a stored append correction for plan "PX2" targeting "DX2"
When I list all service corrections without filter
Then the service corrections list should have 2 items
Scenario: List corrections with plan_id filter returns only matching
Given a fresh correction service instance
And a stored revert correction for plan "PX1" targeting "DX1"
And a stored append correction for plan "PX1" targeting "DX2"
And a stored revert correction for plan "PX2" targeting "DX3"
When I list service corrections for plan "PX1"
Then the service corrections list should have 2 items
When I list service corrections for plan "PX2"
Then the service corrections list should have 1 items
# ── list_attempts ────────────────────────────────────────────
Scenario: List attempts for a valid correction returns attempts
Given a fresh correction service instance
And a stored revert correction for plan "PX1" targeting "DX1"
And the stored correction has been reverted with an empty tree
When I list attempts for the stored correction
Then the attempts list should have 1 entry
# ── cancel_correction ────────────────────────────────────────
Scenario: Cancel correction in pending status succeeds
Given a fresh correction service instance
And a stored revert correction for plan "PX1" targeting "DX1"
When I cancel the stored correction
Then the stored correction status should be "cancelled"
Scenario: Cancel correction in applied status raises ValidationError
Given a fresh correction service instance
And a stored revert correction for plan "PX1" targeting "DX1"
And the stored correction has been reverted with an empty tree
When I attempt to cancel the stored correction
Then a service ValidationError should have been raised
# ── _classify_risk boundary tests ────────────────────────────
Scenario: Classify risk at exact boundary values
Given a fresh correction service instance
Then classifying risk for 0 affected nodes should return "low"
And classifying risk for 1 affected nodes should return "low"
And classifying risk for 3 affected nodes should return "low"
And classifying risk for 4 affected nodes should return "medium"
And classifying risk for 10 affected nodes should return "medium"
And classifying risk for 11 affected nodes should return "high"
And classifying risk for 100 affected nodes should return "high"
# ── _compute_affected_subtree BFS order ──────────────────────
Scenario: Compute affected subtree follows BFS order
Given a fresh correction service instance
When I compute affected subtree for "A" with tree "A->B,C;B->D;C->E"
Then the affected subtree should be exactly "A,B,C,D,E" in order
Scenario: Compute affected subtree with no children returns only root
Given a fresh correction service instance
When I compute affected subtree for "LEAF" with an empty tree
Then the affected subtree should be exactly "LEAF" in order
Scenario: Compute affected subtree with wide tree
Given a fresh correction service instance
When I compute affected subtree for "R" with tree "R->A,B,C,D,E"
Then the affected subtree should be exactly "R,A,B,C,D,E" in order
And the affected subtree count should be 6