1ffb0fddbe
CI / benchmark-publish (pull_request) Has been skipped
CI / build (pull_request) Successful in 21s
CI / lint (pull_request) Successful in 3m18s
CI / typecheck (pull_request) Successful in 4m11s
CI / security (pull_request) Successful in 4m13s
CI / integration_tests (pull_request) Successful in 5m56s
CI / coverage (pull_request) Successful in 10m2s
CI / quality (pull_request) Successful in 3m40s
CI / unit_tests (pull_request) Successful in 6m6s
CI / docker (pull_request) Successful in 1m2s
CI / status-check (pull_request) Successful in 1s
CI / e2e_tests (pull_request) Successful in 8m19s
CI / build (push) Successful in 14s
CI / lint (push) Successful in 3m35s
CI / typecheck (push) Successful in 3m53s
CI / benchmark-regression (push) Has been skipped
CI / integration_tests (push) Successful in 5m54s
CI / unit_tests (push) Successful in 7m17s
CI / e2e_tests (push) Successful in 9m39s
CI / coverage (push) Successful in 11m22s
CI / benchmark-regression (pull_request) Successful in 58m8s
CI / benchmark-publish (push) Successful in 32m8s
CI / quality (push) Successful in 3m29s
CI / security (push) Successful in 3m40s
CI / docker (push) Successful in 51s
CI / status-check (push) Successful in 1s
Enhanced the CorrectionService to properly isolate correction scope:
1. analyze_impact() now populates excluded_decisions by computing
the set difference between all plan decisions and the affected
subtree, ensuring root and sibling decisions are tracked.
2. Added rollback_tier computation that counts parent hops from
the target decision to the root, enabling depth-aware rollback
strategies (tier 0 = root targeted, tier N = N levels deep).
3. Enhanced dry-run report with excluded decisions list, rollback
tier, and tier-0 warning when entire tree is affected.
4. Added subtree isolation validation confirming root exclusion
and sibling non-contamination for non-root corrections.
5. Fixed status state-machine regression in execute_revert() where
analyze_impact() overwrote status back to ANALYZING from EXECUTING.
Guard now only transitions to ANALYZING when status is PENDING.
execute_revert() now transitions through ANALYZING before EXECUTING
for correct lifecycle ordering.
6. Fixed validate_subtree_isolation() to use structural-only BFS for
the sibling invariant check, so that influence-DAG-caused sibling
reachability is not misreported as an isolation violation (per spec
§ Affected Subtree Computation).
7. Fixed false-positive cycle-detection warnings from convergent
(diamond) topologies by replacing per-node seen_this_round with a
global enqueued set in BFS, preventing duplicate queue insertions
from different parents.
8. Added dry_run enforcement guard in _assert_executable() to prevent
execution of dry-run-only corrections per spec (§ plan correct
--dry-run: "Show impact without executing").
9. Fixed generate_dry_run_report() to preserve request status so that
generating a preview does not advance the correction lifecycle
(dry-run is non-mutating per spec). Status restoration now uses
try/finally to guarantee recovery even when analyze_impact() raises
after transitioning the status.
10. Improved cycle-detection log message accuracy to cover both
structural tree and influence DAG sources.
11. Extracted cost/time estimation constants (_COST_PER_DECISION,
_RECOMPUTE_SECONDS_PER_DECISION) from magic numbers.
12. Added terminal-state guard in analyze_impact() to reject
re-analysis after execution (APPLIED/FAILED/CANCELLED/REJECTED),
preventing audit-data corruption. Promoted terminal-status set
to a module-level _TERMINAL_STATUSES frozenset constant.
13. Added mode validation in execute_revert()/execute_append() to
prevent mode-mismatched execution (e.g. calling execute_revert
on an APPEND correction).
14. Fixed _collect_all_decisions() to always include the target
decision in the universe, preventing broken partition invariant
for isolated single-node plans.
15. Fixed tier-0 dry-run warning to only trigger when the target
is genuinely in the structural tree (avoiding false warnings for
nodes not present in the tree).
16. Removed duplicate as_cli_dict regression scenarios in
resource_type_deferred_physical.feature.
ISSUES CLOSED: #845
255 lines
12 KiB
Gherkin
255 lines
12 KiB
Gherkin
Feature: Decision correction recomputes only affected subtree
|
|
As a developer using CleverAgents correction flows
|
|
I want corrections to recompute only the affected subtree
|
|
So that sibling and ancestor decisions are preserved unchanged
|
|
|
|
Background:
|
|
Given a subtree isolation correction service
|
|
And a subtree isolation decision tree with root "root" and children
|
|
| parent | children |
|
|
| root | A,B |
|
|
| A | C,D |
|
|
| B | E |
|
|
|
|
# --- Leaf targeting ---
|
|
|
|
Scenario: Correction targets leaf node - only leaf affected
|
|
When I subtree isolate target decision "D" with mode "revert"
|
|
And I subtree isolate analyze the impact
|
|
Then the subtree isolate affected decisions should be "D"
|
|
And the subtree isolate excluded decisions should contain "root"
|
|
And the subtree isolate excluded decisions should contain "A"
|
|
And the subtree isolate excluded decisions should contain "B"
|
|
And the subtree isolate excluded decisions should contain "C"
|
|
And the subtree isolate excluded decisions should contain "E"
|
|
|
|
# --- Middle node targeting ---
|
|
|
|
Scenario: Correction targets middle node - middle and descendants affected
|
|
When I subtree isolate target decision "A" with mode "revert"
|
|
And I subtree isolate analyze the impact
|
|
Then the subtree isolate affected decisions should contain "A"
|
|
And the subtree isolate affected decisions should contain "C"
|
|
And the subtree isolate affected decisions should contain "D"
|
|
And the subtree isolate excluded decisions should contain "root"
|
|
And the subtree isolate excluded decisions should contain "B"
|
|
And the subtree isolate excluded decisions should contain "E"
|
|
|
|
# --- Root targeting ---
|
|
|
|
Scenario: Correction targets root - all decisions affected with tier 0 warning
|
|
When I subtree isolate target decision "root" with mode "revert"
|
|
And I subtree isolate generate a dry-run report
|
|
Then the subtree isolate affected decisions should contain "root"
|
|
And the subtree isolate affected decisions should contain "A"
|
|
And the subtree isolate affected decisions should contain "B"
|
|
And the subtree isolate affected decisions should contain "C"
|
|
And the subtree isolate affected decisions should contain "D"
|
|
And the subtree isolate affected decisions should contain "E"
|
|
And the subtree isolate excluded decisions should be empty
|
|
And the subtree isolate rollback tier depth should be 0
|
|
And the subtree isolate dry-run warnings should contain "Tier 0"
|
|
|
|
# --- Rollback tier computation ---
|
|
|
|
Scenario: Rollback tier is 0 for root
|
|
When I subtree isolate compute rollback tier for "root"
|
|
Then the subtree isolate rollback tier should be 0
|
|
|
|
Scenario: Rollback tier is 1 for direct child
|
|
When I subtree isolate compute rollback tier for "A"
|
|
Then the subtree isolate rollback tier should be 1
|
|
|
|
Scenario: Rollback tier is 1 for second direct child
|
|
When I subtree isolate compute rollback tier for "B"
|
|
Then the subtree isolate rollback tier should be 1
|
|
|
|
Scenario: Rollback tier is 2 for grandchild
|
|
When I subtree isolate compute rollback tier for "C"
|
|
Then the subtree isolate rollback tier should be 2
|
|
|
|
Scenario: Rollback tier depth 3 for deep tree
|
|
Given a subtree isolation deep tree with depth 4
|
|
When I subtree isolate compute rollback tier for "depth_3"
|
|
Then the subtree isolate rollback tier should be 3
|
|
|
|
# --- Dry-run report enhancements ---
|
|
|
|
Scenario: Dry-run report includes excluded decisions and rollback tier
|
|
When I subtree isolate target decision "A" with mode "revert"
|
|
And I subtree isolate generate a dry-run report
|
|
Then the subtree isolate dry-run report should include excluded decisions
|
|
And the subtree isolate dry-run report rollback tier depth should be 1
|
|
And the subtree isolate dry-run report excluded decisions should contain "root"
|
|
And the subtree isolate dry-run report excluded decisions should contain "B"
|
|
And the subtree isolate dry-run report excluded decisions should contain "E"
|
|
|
|
# --- Subtree isolation validation ---
|
|
|
|
Scenario: Subtree isolation is valid for non-root target
|
|
When I subtree isolate validate isolation for target "A"
|
|
Then the subtree isolate isolation should be valid
|
|
|
|
Scenario: Subtree isolation is valid for leaf target
|
|
When I subtree isolate validate isolation for target "D"
|
|
Then the subtree isolate isolation should be valid
|
|
|
|
# --- Edge cases for coverage ---
|
|
|
|
Scenario: Subtree isolation with empty tree returns valid
|
|
Given a subtree isolation empty tree
|
|
When I subtree isolate validate isolation for target "X"
|
|
Then the subtree isolate isolation should be valid
|
|
|
|
Scenario: Rollback tier for unknown node is 0
|
|
When I subtree isolate compute rollback tier for "UNKNOWN"
|
|
Then the subtree isolate rollback tier should be 0
|
|
|
|
Scenario: Find parent returns None for root node
|
|
When I subtree isolate check parent of "root"
|
|
Then the subtree isolate parent should be none
|
|
|
|
# --- Influence DAG traversal ---
|
|
|
|
Scenario: Influence edge adds extra decision to affected set
|
|
Given a subtree isolate influence edge from "D" to "E"
|
|
When I subtree isolate target decision "D" with mode "revert"
|
|
And I subtree isolate analyze the impact with influence edges
|
|
Then the subtree isolate affected decisions should contain "D"
|
|
And the subtree isolate affected decisions should contain "E"
|
|
And the subtree isolate excluded decisions should contain "root"
|
|
And the subtree isolate excluded decisions should contain "A"
|
|
And the subtree isolate excluded decisions should contain "B"
|
|
And the subtree isolate excluded decisions should contain "C"
|
|
|
|
Scenario: Influence DAG cycle does not cause infinite loop
|
|
Given a subtree isolate influence cycle from "C" to "D" back to "C"
|
|
When I subtree isolate target decision "C" with mode "revert"
|
|
And I subtree isolate analyze the impact with influence edges
|
|
Then the subtree isolate affected decisions should contain "C"
|
|
And the subtree isolate affected decisions should contain "D"
|
|
|
|
Scenario: Isolation validation passes when influence DAG reaches sibling
|
|
Given a subtree isolate influence edge from "D" to "E"
|
|
When I subtree isolate validate isolation for target "D" with influence edges
|
|
Then the subtree isolate isolation should be valid
|
|
|
|
# --- Append mode ---
|
|
|
|
Scenario: Append correction spawns child plan
|
|
When I subtree isolate target decision "A" with mode "append"
|
|
And I subtree isolate execute append correction
|
|
Then the subtree isolate append result should have a spawned child plan
|
|
And the subtree isolate append result status should be "applied"
|
|
|
|
# --- Dry-run enforcement ---
|
|
|
|
Scenario: Executing a dry-run revert correction raises ValidationError
|
|
When I subtree isolate target decision "A" with mode "revert" and dry_run
|
|
Then subtree isolate executing the correction should raise a dry-run error
|
|
|
|
Scenario: Executing a dry-run append correction raises ValidationError
|
|
When I subtree isolate target decision "A" with mode "append" and dry_run
|
|
Then subtree isolate executing the correction should raise a dry-run error
|
|
|
|
# --- Negative isolation validation ---
|
|
|
|
Scenario: Isolation validation detects structural root contamination
|
|
Given a subtree isolation tree where child structurally reaches root
|
|
| parent | children |
|
|
| root | A |
|
|
| A | root |
|
|
When I subtree isolate validate isolation for target "A"
|
|
Then the subtree isolate isolation should be invalid
|
|
|
|
# --- Execute revert (non-dry-run) ---
|
|
|
|
Scenario: Execute revert correction succeeds for middle node
|
|
When I subtree isolate target decision "A" with mode "revert"
|
|
And I subtree isolate execute revert correction
|
|
Then the subtree isolate revert result status should be "applied"
|
|
And the subtree isolate revert result should contain reverted decisions "A,C,D"
|
|
And the subtree isolate revert result should have archived artifacts
|
|
|
|
# --- Status guard enforcement ---
|
|
|
|
Scenario: Executing an already-applied correction raises ValidationError
|
|
When I subtree isolate target decision "A" with mode "revert"
|
|
And I subtree isolate execute revert correction
|
|
Then subtree isolate executing the correction again should raise a status error
|
|
|
|
Scenario: Executing a cancelled correction raises ValidationError
|
|
When I subtree isolate target decision "A" with mode "revert"
|
|
And I subtree isolate cancel the correction
|
|
Then subtree isolate executing the correction should raise a status error
|
|
|
|
# --- Mode mismatch enforcement ---
|
|
|
|
Scenario: Calling execute_revert on an append correction raises ValidationError
|
|
When I subtree isolate target decision "A" with mode "append"
|
|
Then subtree isolate calling execute_revert should raise a mode error
|
|
|
|
Scenario: Calling execute_append on a revert correction raises ValidationError
|
|
When I subtree isolate target decision "A" with mode "revert"
|
|
Then subtree isolate calling execute_append should raise a mode error
|
|
|
|
# --- Single-node tree edge case ---
|
|
|
|
Scenario: Single-node tree has no excluded decisions
|
|
Given a subtree isolation single node tree
|
|
When I subtree isolate target decision "root" with mode "revert"
|
|
And I subtree isolate analyze the impact
|
|
Then the subtree isolate affected decisions should be "root"
|
|
And the subtree isolate excluded decisions should be empty
|
|
And the subtree isolate rollback tier depth should be 0
|
|
|
|
# --- Terminal state guard ---
|
|
|
|
Scenario: Re-analyzing an applied correction raises ValidationError
|
|
When I subtree isolate target decision "A" with mode "revert"
|
|
And I subtree isolate execute revert correction
|
|
Then subtree isolate re-analyzing the impact should raise a terminal state error
|
|
|
|
# --- Exact-match affected set for middle node ---
|
|
|
|
Scenario: Middle node affected count is exactly 3
|
|
When I subtree isolate target decision "A" with mode "revert"
|
|
And I subtree isolate analyze the impact
|
|
Then the subtree isolate affected decisions count should be 3
|
|
|
|
# --- Convergent (diamond) DAG topology ---
|
|
|
|
Scenario: Convergent diamond topology does not duplicate affected nodes
|
|
Given a subtree isolate diamond influence where "C" and "D" both target "E"
|
|
When I subtree isolate target decision "A" with mode "revert"
|
|
And I subtree isolate analyze the impact with influence edges
|
|
Then the subtree isolate affected decisions should contain "C"
|
|
And the subtree isolate affected decisions should contain "D"
|
|
And the subtree isolate affected decisions should contain "E"
|
|
And the subtree isolate affected decision "E" should appear exactly once
|
|
|
|
# --- Dry-run exception recovery ---
|
|
|
|
Scenario: Dry-run report restores status when analyze_impact fails
|
|
When I subtree isolate target decision "A" with mode "revert"
|
|
And I subtree isolate generate a dry-run report that fails internally
|
|
Then the subtree isolate correction status should be "pending"
|
|
|
|
# --- Execute revert with influence edges ---
|
|
|
|
Scenario: Execute revert with influence edges includes DAG-reachable decisions
|
|
Given a subtree isolate influence edge from "D" to "E"
|
|
When I subtree isolate target decision "A" with mode "revert"
|
|
And I subtree isolate execute revert correction with influence edges
|
|
Then the subtree isolate revert result status should be "applied"
|
|
And the subtree isolate revert result should contain reverted decision "E"
|
|
|
|
# --- DAG-only nodes in excluded set ---
|
|
|
|
Scenario: Nodes only in influence DAG appear in excluded set
|
|
Given a subtree isolate influence edge from "X_EXT" to "Y_EXT" only in DAG
|
|
When I subtree isolate target decision "D" with mode "revert"
|
|
And I subtree isolate analyze the impact with influence edges
|
|
Then the subtree isolate excluded decisions should contain "X_EXT"
|
|
And the subtree isolate excluded decisions should contain "Y_EXT"
|