Feature: Correction attempt persistence via CorrectionAttemptRepository
  As a developer
  I want correction attempt records to persist correctly
  So that decision correction workflows are durable across operations

  Background:
    Given a fresh in-memory correction attempt database
    And a prerequisite action "local/correction-action" exists for correction attempts
    And a prerequisite plan exists for correction attempts
    And a prerequisite root decision exists for correction attempts

  # ------------------------------------------------------------------
  # Create and retrieve
  # ------------------------------------------------------------------

  Scenario: Create a correction attempt and retrieve it by ID
    Given a new correction attempt in revert mode
    When I persist the correction attempt via the repository
    Then I can retrieve the correction attempt by its ID
    And the persisted correction attempt mode should be "revert"
    And the persisted correction attempt state should be "pending"

  Scenario: Create an append-mode correction attempt
    Given a new correction attempt in append mode
    When I persist the correction attempt via the repository
    Then I can retrieve the correction attempt by its ID
    And the persisted correction attempt mode should be "append"

  Scenario: Persisted correction attempt preserves all fields
    Given a new correction attempt with all fields populated
    When I persist the correction attempt via the repository
    Then I can retrieve the correction attempt by its ID
    And the persisted correction attempt guidance should match
    And the persisted correction attempt plan_id should match
    And the persisted correction attempt original_decision_id should match

  # ------------------------------------------------------------------
  # List by plan
  # ------------------------------------------------------------------

  Scenario: List correction attempts by plan returns ordered results
    Given 3 persisted correction attempts for the same plan
    When I list correction attempts by plan ID
    Then I should get 3 correction attempts in creation order

  # ------------------------------------------------------------------
  # Update state
  # ------------------------------------------------------------------

  Scenario: Update correction attempt state to executing
    Given a persisted correction attempt in pending state
    When I update the correction attempt state to "executing"
    Then the correction attempt state should be "executing"

  Scenario: Update correction attempt state to complete with timestamp
    Given a persisted correction attempt in executing state
    When I update the correction attempt to complete with timestamp
    Then the correction attempt state should be "complete"
    And the correction attempt completed_at should be set

  Scenario: Invalid state transition from pending to complete is rejected
    Given a persisted correction attempt in pending state
    When I try to update the correction attempt state to "complete"
    Then an InvalidCorrectionStateTransitionError should be raised

  Scenario: Update correction attempt with new decision ID
    Given a persisted correction attempt in pending state
    When I update the correction attempt with a new decision ID
    Then the correction attempt new_decision_id should be set

  Scenario: Update correction attempt with archived artifacts path
    Given a persisted correction attempt in pending state
    When I update the correction attempt with archived artifacts path
    Then the correction attempt archived_artifacts_path should be set

  # ------------------------------------------------------------------
  # Delete
  # ------------------------------------------------------------------

  Scenario: Delete a correction attempt by ID
    Given a persisted correction attempt in pending state
    When I delete the correction attempt
    Then the correction attempt should no longer exist

  Scenario: Delete a non-existent correction attempt returns false
    When I try to delete a non-existent correction attempt
    Then the correction attempt delete result should be false

  # ------------------------------------------------------------------
  # Duplicate detection
  # ------------------------------------------------------------------

  Scenario: Creating a duplicate correction attempt raises error
    Given a persisted correction attempt in pending state
    When I try to persist the same correction attempt again
    Then a DuplicateCorrectionAttemptError should be raised

  # ------------------------------------------------------------------
  # Cascade deletion
  # ------------------------------------------------------------------

  Scenario: Deleting a plan cascades to its correction attempts
    Given a persisted correction attempt in pending state
    When the parent plan is deleted
    Then the correction attempt should have been cascade deleted

  # ------------------------------------------------------------------
  # Not found
  # ------------------------------------------------------------------

  Scenario: Getting a non-existent correction attempt raises error
    When I try to get a non-existent correction attempt
    Then a CorrectionAttemptNotFoundError should be raised

  # ------------------------------------------------------------------
  # Edge cases
  # ------------------------------------------------------------------

  Scenario: List correction attempts for a plan with no attempts
    When I list correction attempts for a plan with no correction attempts
    Then I should get 0 correction attempts

  # ------------------------------------------------------------------
  # Additional state transition coverage
  # ------------------------------------------------------------------

  Scenario: Update correction attempt state to failed with timestamp
    Given a persisted correction attempt in executing state
    When I update the correction attempt to failed with timestamp
    Then the correction attempt state should be "failed"
    And the correction attempt completed_at should be set

  Scenario: Transition from complete state is rejected
    Given a persisted correction attempt in complete state
    When I try to update the correction attempt state to "executing"
    Then an InvalidCorrectionStateTransitionError should be raised

  Scenario: Transition from failed state is rejected
    Given a persisted correction attempt in failed state
    When I try to update the correction attempt state to "pending"
    Then an InvalidCorrectionStateTransitionError should be raised

  Scenario: Setting completed_at on non-terminal transition is rejected
    Given a persisted correction attempt in pending state
    When I try to update the correction attempt to executing with completed_at
    Then an InvalidCorrectionStateTransitionError should be raised

  # ------------------------------------------------------------------
  # Domain model guidance validation
  # ------------------------------------------------------------------

  Scenario: Empty guidance is rejected at domain model level
    When I try to create a correction attempt with empty guidance
    Then a guidance validation error should be raised

  Scenario: Whitespace-only guidance is rejected at domain model level
    When I try to create a correction attempt with whitespace-only guidance
    Then a guidance validation error should be raised

  # ------------------------------------------------------------------
  # Additional test coverage
  # ------------------------------------------------------------------

  Scenario: Updating state of a non-existent correction attempt raises error
    When I try to update the state of a non-existent correction attempt
    Then a CorrectionAttemptNotFoundError should be raised

  Scenario: Updating with non-existent new_decision_id raises FK error
    Given a persisted correction attempt in pending state
    When I try to update the correction attempt with a non-existent decision ID
    Then a DatabaseError should be raised

  Scenario: Guidance at max length is accepted
    Given a new correction attempt with guidance at max length
    When I persist the correction attempt via the repository
    Then I can retrieve the correction attempt by its ID

  Scenario: Guidance exceeding max length is rejected at domain model level
    When I try to create a correction attempt with guidance exceeding max length
    Then a guidance validation error should be raised

  # ------------------------------------------------------------------
  # Timezone normalization coverage (M3)
  # ------------------------------------------------------------------

  Scenario: Non-UTC timezone datetimes are normalised to UTC on persist
    Given a new correction attempt with non-UTC timezone timestamps
    When I persist the correction attempt via the repository
    Then I can retrieve the correction attempt by its ID
    And the persisted correction attempt created_at should be in UTC

  # ------------------------------------------------------------------
  # archived_artifacts_path round-trip on create (M4)
  # ------------------------------------------------------------------

  Scenario: Correction attempt with archived_artifacts_path round-trips on create
    Given a new correction attempt with archived_artifacts_path set
    When I persist the correction attempt via the repository
    Then I can retrieve the correction attempt by its ID
    And the persisted correction attempt archived_artifacts_path should match

  # ------------------------------------------------------------------
  # Minimum-boundary guidance (L7)
  # ------------------------------------------------------------------

  Scenario: Single-character guidance is accepted
    Given a new correction attempt with single-character guidance
    When I persist the correction attempt via the repository
    Then I can retrieve the correction attempt by its ID
    And the persisted correction attempt guidance should be a single character

  # ------------------------------------------------------------------
  # Delete in non-pending state (L4)
  # ------------------------------------------------------------------

  Scenario: Delete a correction attempt in complete state
    Given a persisted correction attempt in complete state
    When I delete the correction attempt
    Then the correction attempt should no longer exist

  # ------------------------------------------------------------------
  # Cross-plan isolation for list_by_plan (L5)
  # ------------------------------------------------------------------

  Scenario: list_by_plan returns only attempts for the specified plan
    Given a persisted correction attempt in pending state
    When I list correction attempts for a plan with no correction attempts
    Then I should get 0 correction attempts

  # ------------------------------------------------------------------
  # Auto-set completed_at for terminal states (T-1)
  # ------------------------------------------------------------------

  Scenario: Terminal transition auto-sets completed_at when not provided
    Given a persisted correction attempt in executing state
    When I update the correction attempt state to "complete" without completed_at
    Then the correction attempt state should be "complete"
    And the correction attempt completed_at should be set

  Scenario: Terminal transition to failed auto-sets completed_at when not provided
    Given a persisted correction attempt in executing state
    When I update the correction attempt state to "failed" without completed_at
    Then the correction attempt state should be "failed"
    And the correction attempt completed_at should be set

  # ------------------------------------------------------------------
  # FK violation on create (T-2)
  # ------------------------------------------------------------------

  Scenario: Creating a correction attempt with non-existent plan_id raises error
    When I try to create a correction attempt with a non-existent plan_id
    Then a DatabaseError should be raised

  Scenario: Creating a correction attempt with non-existent original_decision_id raises error
    When I try to create a correction attempt with a non-existent original_decision_id
    Then a DatabaseError should be raised

  # ------------------------------------------------------------------
  # Invalid mode at domain model level (T-3)
  # ------------------------------------------------------------------

  Scenario: Invalid mode value is rejected at domain model level
    When I try to create a correction attempt with an invalid mode
    Then a mode validation error should be raised

  # ------------------------------------------------------------------
  # Empty/whitespace new_decision_id via update_state (M-1)
  # ------------------------------------------------------------------

  Scenario: Whitespace-only new_decision_id is rejected in update_state
    Given a persisted correction attempt in pending state
    When I try to update the correction attempt with whitespace-only new_decision_id
    Then a correction attempt ValueError should be raised

  Scenario: Empty new_decision_id is rejected in update_state
    Given a persisted correction attempt in pending state
    When I try to update the correction attempt with empty new_decision_id
    Then a correction attempt ValueError should be raised

  # ------------------------------------------------------------------
  # Combined field update (L-5)
  # ------------------------------------------------------------------

  Scenario: Update with both new_decision_id and archived_artifacts_path
    Given a persisted correction attempt in pending state
    When I update the correction attempt with new_decision_id and archived_artifacts_path
    Then the correction attempt new_decision_id should be set
    And the correction attempt archived_artifacts_path should be set

  # ------------------------------------------------------------------
  # Self-transition rejection (L-3)
  # ------------------------------------------------------------------

  Scenario: Self-transition from pending to pending is rejected
    Given a persisted correction attempt in pending state
    When I try to update the correction attempt state to "pending"
    Then an InvalidCorrectionStateTransitionError should be raised

  Scenario: Self-transition from executing to executing is rejected
    Given a persisted correction attempt in executing state
    When I try to update the correction attempt state to "executing"
    Then an InvalidCorrectionStateTransitionError should be raised

  # ------------------------------------------------------------------
  # Empty/whitespace archived_artifacts_path via update_state (L-2)
  # ------------------------------------------------------------------

  Scenario: Empty archived_artifacts_path is rejected in update_state
    Given a persisted correction attempt in pending state
    When I try to update the correction attempt with empty archived_artifacts_path
    Then a correction attempt ValueError should be raised

  Scenario: Whitespace-only archived_artifacts_path is rejected in update_state
    Given a persisted correction attempt in pending state
    When I try to update the correction attempt with whitespace-only archived_artifacts_path
    Then a correction attempt ValueError should be raised

  # ------------------------------------------------------------------
  # Whitespace-padded archived_artifacts_path stripping (L-6)
  # ------------------------------------------------------------------

  Scenario: Whitespace-padded archived_artifacts_path is stripped on update
    Given a persisted correction attempt in pending state
    When I update the correction attempt with whitespace-padded archived_artifacts_path
    Then the correction attempt archived_artifacts_path should be stripped

  # ------------------------------------------------------------------
  # RESTRICT FK on original_decision_id (R-1)
  # ------------------------------------------------------------------

  Scenario: Deleting a decision referenced by original_decision_id is blocked
    Given a persisted correction attempt in pending state
    When I try to delete the original decision
    Then a database integrity error should be raised for the decision
    And the correction attempt should still exist

  # ------------------------------------------------------------------
  # Stronger cross-plan isolation (I-1)
  # ------------------------------------------------------------------

  Scenario: list_by_plan returns only first plan's attempts
    Given a second prerequisite plan exists for correction attempts
    And a persisted correction attempt for the first plan
    And a persisted correction attempt for the second plan
    When I list correction attempts for the first plan
    Then I should get exactly 1 correction attempt for the first plan

  Scenario: list_by_plan returns only second plan's attempts
    Given a second prerequisite plan exists for correction attempts
    And a persisted correction attempt for the first plan
    And a persisted correction attempt for the second plan
    When I list correction attempts for the second plan
    Then I should get exactly 1 correction attempt for the second plan

  # ------------------------------------------------------------------
  # Defensive to_domain() coercion for corrupted DB data (DC-1)
  # ------------------------------------------------------------------

  Scenario: to_domain coerces unknown mode to revert
    Given a raw correction attempt row with corrupted mode "unknown_mode"
    When I retrieve the corrupted correction attempt by ID
    Then the retrieved correction attempt mode should be "revert"

  Scenario: to_domain coerces unknown state to failed
    Given a raw correction attempt row with corrupted state "unknown_state"
    When I retrieve the corrupted correction attempt by ID
    Then the retrieved correction attempt state should be "failed"

  Scenario: to_domain coerces empty guidance to corrupted marker
    Given a raw correction attempt row with empty guidance
    When I retrieve the corrupted correction attempt by ID
    Then the retrieved correction attempt guidance should be "[corrupted]"

  # ------------------------------------------------------------------
  # format_sqlite_timestamp naive datetime rejection (TS-1)
  # ------------------------------------------------------------------

  Scenario: format_sqlite_timestamp rejects naive datetimes
    When I call format_sqlite_timestamp with a naive datetime
    Then a ValueError should be raised for naive datetime

  # ------------------------------------------------------------------
  # Domain model naive datetime normalisation (TS-2)
  # ------------------------------------------------------------------

  Scenario: CorrectionAttemptRecord normalizes naive created_at to UTC
    When I create a CorrectionAttemptRecord with naive created_at
    Then the record created_at should have UTC timezone

  Scenario: CorrectionAttemptRecord normalizes naive completed_at to UTC
    When I create a CorrectionAttemptRecord with naive completed_at
    Then the record completed_at should have UTC timezone

  # ------------------------------------------------------------------
  # Corrupted DB state in update_state (DC-2)
  # ------------------------------------------------------------------

  Scenario: update_state treats corrupted DB state as terminal failed
    Given a raw correction attempt row with corrupted state "garbage_state"
    When I try to update the corrupted correction attempt state to "executing"
    Then an InvalidCorrectionStateTransitionError should be raised
