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