Feature: Git worktree sandbox lifecycle
  As a developer
  I want a sandbox that isolates plan changes in a git worktree
  So that the original repository is unmodified until changes are committed

  Background:
    Given a gwt test git repository is initialised

  # --- Creation ---

  Scenario: Create a git worktree sandbox
    When a gwt sandbox is created for plan "plan-001"
    Then the gwt sandbox should be in the "created" state
    And the gwt sandbox context should reference plan "plan-001"
    And the gwt sandbox context should have strategy metadata "git_worktree"
    And the gwt sandbox worktree path should exist

  Scenario: Creating a sandbox with empty plan_id raises ValueError
    When a gwt sandbox is created with empty plan_id
    Then a gwt ValueError should be raised with message "plan_id cannot be empty"

  Scenario: Creating a sandbox with empty resource_id raises ValueError
    When a gwt sandbox is prepared with empty resource_id
    Then a gwt ValueError should be raised with message "resource_id cannot be empty"

  Scenario: Creating a sandbox with empty original_path raises ValueError
    When a gwt sandbox is prepared with empty original_path
    Then a gwt ValueError should be raised with message "original_path cannot be empty"

  Scenario: Creating a sandbox with non-positive timeout raises ValueError
    When a gwt sandbox is prepared with zero timeout
    Then a gwt ValueError should be raised with message "git_timeout must be positive"

  Scenario: Creating a sandbox on a non-git directory raises SandboxCreationError
    Given a gwt non-git directory
    When a gwt sandbox is created on the non-git directory for plan "plan-001"
    Then a gwt SandboxCreationError should be raised

  # --- Path resolution ---

  Scenario: Resolve a path in the worktree
    When a gwt sandbox is created for plan "plan-001"
    And the gwt path "src/main.py" is resolved
    Then the gwt resolved path should be inside the worktree
    And the gwt sandbox should be in the "active" state

  Scenario: Path traversal is rejected
    When a gwt sandbox is created for plan "plan-001"
    And the gwt path "../etc/passwd" is resolved
    Then a gwt ValueError should be raised with message "Path traversal not allowed"

  Scenario: Resolving a path on a cleaned-up sandbox raises SandboxStateError
    When a gwt sandbox is created for plan "plan-001"
    And the gwt sandbox is cleaned up
    And the gwt path "file.txt" is resolved on a cleaned-up sandbox
    Then a gwt SandboxStateError should be raised

  # --- Commit ---

  Scenario: Commit with no changes produces empty result
    When a gwt sandbox is created for plan "plan-001"
    And the gwt sandbox is committed with message "empty commit"
    Then the gwt commit result should indicate success
    And the gwt commit result should have 0 changed files

  Scenario: Commit with changes merges back to original branch
    When a gwt sandbox is created for plan "plan-001"
    And a gwt file "new_file.txt" is created in the worktree with content "hello"
    And the gwt sandbox is committed with message "add new file"
    Then the gwt commit result should indicate success
    And the gwt commit result should have 1 added files
    And the gwt file "new_file.txt" should exist in the original repo

  Scenario: Commit with modified file reports changes
    When a gwt sandbox is created for plan "plan-001"
    And the gwt existing file "README.md" is modified in the worktree
    And the gwt sandbox is committed with message "modify readme"
    Then the gwt commit result should indicate success
    And the gwt commit result should have 1 changed files

  Scenario: Commit on a cleaned-up sandbox raises SandboxStateError
    When a gwt sandbox is created for plan "plan-001"
    And the gwt sandbox is cleaned up
    And the gwt sandbox commit is attempted on cleaned-up sandbox
    Then a gwt SandboxStateError should be raised

  # --- Rollback ---

  Scenario: Rollback discards worktree changes
    When a gwt sandbox is created for plan "plan-001"
    And a gwt file "temp.txt" is created in the worktree with content "temporary"
    And the gwt path "temp.txt" is resolved
    And the gwt sandbox is rolled back
    Then the gwt sandbox should be in the "rolled_back" state
    And the gwt file "temp.txt" should not exist in the worktree

  Scenario: Rollback on a non-active sandbox raises SandboxStateError
    When a gwt sandbox is created for plan "plan-001"
    And the gwt sandbox rollback is attempted on created sandbox
    Then a gwt SandboxStateError should be raised

  Scenario: Rollback from COMMITTED undoes the merge on original branch
    When a gwt sandbox is created for plan "plan-rb-commit"
    And a gwt file "committed_file.txt" is created in the worktree with content "committed-data"
    And the gwt sandbox is committed with message "add committed_file"
    Then the gwt sandbox should be in the "committed" state
    And the gwt file "committed_file.txt" should exist in the original repo
    When the gwt sandbox is rolled back
    Then the gwt sandbox should be in the "rolled_back" state
    And the gwt file "committed_file.txt" should not exist in the original repo

  # --- Cleanup ---

  Scenario: Cleanup removes worktree and branch
    When a gwt sandbox is created for plan "plan-001"
    And the gwt sandbox is cleaned up
    Then the gwt sandbox should be in the "cleaned_up" state
    And the gwt sandbox worktree path should not exist
    And the gwt sandbox branch should not exist

  Scenario: Cleanup is idempotent
    When a gwt sandbox is created for plan "plan-001"
    And the gwt sandbox is cleaned up
    And the gwt sandbox is cleaned up again
    Then the gwt sandbox should be in the "cleaned_up" state

  # --- Branch sanitisation ---

  Scenario: Branch name is sanitised from plan ID
    When a gwt sandbox is created for plan "plan with spaces!@#"
    Then the gwt sandbox branch name should be safe for git

  # --- Protocol properties ---

  Scenario: Sandbox has a unique ULID identifier
    When a gwt sandbox is instantiated
    Then the gwt sandbox_id should be a valid ULID
    And the gwt sandbox status should be "pending"
    And the gwt sandbox context should be None
