Feature: Built-in Git Tools
  As a developer
  I want built-in git operation tools
  So that agents can inspect repository state safely

  # ---- Git Status ----

  Scenario: Git status in a clean repository
    Given a temporary git repository
    When I execute the "builtin/git-status" tool
    Then the tool result should be successful
    And the git output should contain "nothing to commit"

  Scenario: Git status shows modified files
    Given a temporary git repository
    And a tracked file "tracked.txt" with content "original"
    And the file "tracked.txt" is modified to "changed"
    When I execute the "builtin/git-status" tool
    Then the tool result should be successful
    And the git output should contain "tracked.txt"

  Scenario: Git status with short format
    Given a temporary git repository
    And a tracked file "tracked.txt" with content "original"
    And the file "tracked.txt" is modified to "changed"
    When I execute the "builtin/git-status" tool with short format
    Then the tool result should be successful
    And the git output should contain "tracked.txt"

  # ---- Git Diff ----

  Scenario: Git diff shows no changes in clean repo
    Given a temporary git repository
    When I execute the "builtin/git-diff" tool
    Then the tool result should be successful
    And the git output should be empty

  Scenario: Git diff shows unstaged changes
    Given a temporary git repository
    And a tracked file "diff-test.txt" with content "before"
    And the file "diff-test.txt" is modified to "after"
    When I execute the "builtin/git-diff" tool
    Then the tool result should be successful
    And the git output should contain "before"
    And the git output should contain "after"

  Scenario: Git diff with staged changes
    Given a temporary git repository
    And a tracked file "staged.txt" with content "old"
    And the file "staged.txt" is modified and staged to "new"
    When I execute the "builtin/git-diff" tool with staged flag
    Then the tool result should be successful
    And the git output should contain "staged.txt"

  Scenario: Git diff with stat format
    Given a temporary git repository
    And a tracked file "stat-test.txt" with content "hello"
    And the file "stat-test.txt" is modified to "world"
    When I execute the "builtin/git-diff" tool with stat flag
    Then the tool result should be successful
    And the git output should contain "stat-test.txt"

  Scenario: Git diff against a specific ref
    Given a temporary git repository
    And a tracked file "ref-test.txt" with content "original"
    And the file "ref-test.txt" is modified to "changed"
    When I execute the "builtin/git-diff" tool with ref "HEAD"
    Then the tool result should be successful
    And the git output should contain "ref-test.txt"

  Scenario: Git diff limited to a specific path
    Given a temporary git repository
    And a tracked file "pathA.txt" with content "aaa"
    And a tracked file "pathB.txt" with content "bbb"
    And the file "pathA.txt" is modified to "aaa-changed"
    And the file "pathB.txt" is modified to "bbb-changed"
    When I execute the "builtin/git-diff" tool with path "pathA.txt"
    Then the tool result should be successful
    And the git output should contain "pathA.txt"
    And the git output should not contain "pathB.txt"

  Scenario: Git diff against a nonexistent ref reports a user-friendly error
    Given a temporary git repository
    When I execute the "builtin/git-diff" tool with ref "nonexistent_ref_xyz"
    Then the tool result should not be successful
    And the tool result error should mention "Unknown revision or path"

  Scenario: Git diff with ref starting with dash is rejected
    Given a temporary git repository
    When I execute the "builtin/git-diff" tool with ref "--exec=whoami"
    Then the tool result should not be successful
    And the tool result error should mention "must not start with"

  Scenario: Git diff path traversal is rejected
    Given a temporary git repository
    When I execute the "builtin/git-diff" tool with path "../../etc/passwd"
    Then the tool result should not be successful
    And the tool result error should mention "traversal"

  # ---- Git Log ----

  Scenario: Git log shows commit history
    Given a temporary git repository
    When I execute the "builtin/git-log" tool
    Then the tool result should be successful
    And the git output should contain "Initial commit"

  Scenario: Git log with max_count limit returns only requested commits
    Given a temporary git repository with multiple commits
    When I execute the "builtin/git-log" tool with max_count 1
    Then the tool result should be successful
    And the git log output should contain at most 1 commit

  Scenario: Git log with oneline format uses compact output
    Given a temporary git repository
    When I execute the "builtin/git-log" tool with oneline format
    Then the tool result should be successful
    And the git log output should use oneline format

  Scenario: Git log filtered by author
    Given a temporary git repository
    When I execute the "builtin/git-log" tool with author "Test User"
    Then the tool result should be successful
    And the git output should contain "Test User"

  Scenario: Git log filtered by since date
    Given a temporary git repository
    When I execute the "builtin/git-log" tool with since "2000-01-01"
    Then the tool result should be successful
    And the git output should contain "Initial commit"

  Scenario: Git log filtered by until date
    Given a temporary git repository
    When I execute the "builtin/git-log" tool with until "2099-12-31"
    Then the tool result should be successful
    And the git output should contain "Initial commit"

  Scenario: Git log filtered by path
    Given a temporary git repository
    And a tracked file "log-path-test.txt" with content "logged"
    When I execute the "builtin/git-log" tool with path "log-path-test.txt"
    Then the tool result should be successful
    And the git output should contain "Add log-path-test.txt"

  # ---- Git Blame ----

  Scenario: Git blame shows file attribution
    Given a temporary git repository
    And a tracked file "blame-test.txt" with content "line one"
    When I execute the "builtin/git-blame" tool for "blame-test.txt"
    Then the tool result should be successful
    And the git output should contain "line one"

  Scenario: Git blame with line range returns only requested lines
    Given a temporary git repository
    And a tracked file "range.txt" with multiline content
    When I execute the "builtin/git-blame" tool for "range.txt" lines 1 to 2
    Then the tool result should be successful
    And the git blame output should contain exactly 2 lines

  Scenario: Git blame for nonexistent file
    Given a temporary git repository
    When I execute the "builtin/git-blame" tool for "nonexistent.txt"
    Then the tool result should not be successful
    And the tool result error should mention "does not exist"

  Scenario: Git blame with partial line range is rejected
    Given a temporary git repository
    And a tracked file "partial.txt" with content "just a line"
    When I execute the "builtin/git-blame" tool for "partial.txt" with only line_start 1
    Then the tool result should not be successful
    And the tool result error should mention "Both line_start and line_end"

  Scenario: Git blame path traversal is rejected
    Given a temporary git repository
    When I execute the "builtin/git-blame" tool for "../../etc/passwd"
    Then the tool result should not be successful
    And the tool result error should mention "traversal"

  # ---- Error Mapping ----

  Scenario: Git status on non-git directory
    Given a temporary non-git directory
    When I execute the "builtin/git-status" tool in the non-git directory
    Then the tool result should not be successful
    And the tool result error should mention "not a git repository"

  Scenario: Git log on empty repository with no commits
    Given a temporary git repository with no commits
    When I execute the "builtin/git-log" tool in the empty repository
    Then the tool result should not be successful
    And the tool result error should mention "No commits exist"

  Scenario: Unrecognized git error falls back to raw stderr output
    Given an unrecognized git error message "unexpected: xyzzy internal failure"
    When I map the git error to a user-friendly message
    Then the mapped error should be "unexpected: xyzzy internal failure"

  # ---- Sandbox and Validation Internals ----

  Scenario: Validate repo path defaults to sandbox root when no path is given
    Given a sandbox root directory
    When I validate the repo path with no user-supplied path
    Then the validated path should equal the sandbox root

  Scenario: Sandbox root resolver returns nothing for absent input
    When I resolve a sandbox root from an empty value
    Then the resolved sandbox root should be absent

  # ---- Timeout Handling ----

  Scenario: Git status reports a friendly error when the command times out
    Given a temporary git repository
    And the git subprocess is configured to time out
    When I execute the "builtin/git-status" tool against the timed-out subprocess
    Then the tool result should not be successful
    And the tool result error should mention "timed out"

  Scenario: Git diff reports a friendly error when the command times out
    Given a temporary git repository
    And the git subprocess is configured to time out
    When I execute the "builtin/git-diff" tool against the timed-out subprocess
    Then the tool result should not be successful
    And the tool result error should mention "timed out"

  Scenario: Git log reports a friendly error when the command times out
    Given a temporary git repository
    And the git subprocess is configured to time out
    When I execute the "builtin/git-log" tool against the timed-out subprocess
    Then the tool result should not be successful
    And the tool result error should mention "timed out"

  Scenario: Git blame reports a friendly error when the command times out
    Given a temporary git repository
    And a tracked file "timeout-test.txt" with content "content"
    And the git subprocess is configured to time out
    When I execute the "builtin/git-blame" tool for "timeout-test.txt" against the timed-out subprocess
    Then the tool result should not be successful
    And the tool result error should mention "timed out"

  # ---- Path Traversal Prevention ----

  Scenario: Path traversal in repo_path is rejected
    Given a temporary git repository
    When I execute the "builtin/git-status" tool with repo_path "../../etc"
    Then the tool result should not be successful
    And the tool result error should mention "traversal"

  Scenario: Git log path traversal is rejected
    Given a temporary git repository
    When I execute the "builtin/git-log" tool with path "../../etc/passwd"
    Then the tool result should not be successful
    And the tool result error should mention "traversal"

  # ---- Tool Registration ----

  Scenario: Register all git tools
    Given a tool registry
    When I register all git tools
    Then the registry should contain 4 tools
    And the registry should contain tool "builtin/git-status"
    And the registry should contain tool "builtin/git-diff"
    And the registry should contain tool "builtin/git-log"
    And the registry should contain tool "builtin/git-blame"

  Scenario: All git tools are read-only
    Given a tool registry
    When I register all git tools
    Then all registered git tools should be read-only

  # ---- Thread Safety (_get_base_env TOCTOU fix) ----

  Scenario: _get_base_env returns the same dict object on repeated calls
    When I call _get_base_env twice
    Then both calls should return the same dict object

  Scenario: _get_base_env returns a dict containing git override keys
    When I call _get_base_env
    Then the result should contain key "GIT_PAGER"
    And the result should contain key "NO_COLOR"
    And the result should contain key "GIT_TERMINAL_PROMPT"

  Scenario: _get_base_env is safe under concurrent initialisation
    When I call _get_base_env concurrently from 20 threads
    Then all threads should receive the same dict object
