Files
HAL9000 39175dd284
CI / benchmark-publish (push) Has started running
CI / push-validation (push) Successful in 43s
CI / helm (push) Successful in 44s
CI / build (push) Successful in 1m22s
CI / benchmark-regression (push) Has been skipped
CI / lint (push) Successful in 1m49s
CI / quality (push) Successful in 2m2s
CI / typecheck (push) Successful in 2m5s
CI / security (push) Successful in 2m26s
CI / integration_tests (push) Successful in 5m1s
CI / e2e_tests (push) Successful in 6m11s
CI / unit_tests (push) Successful in 11m38s
CI / docker (push) Successful in 2m20s
CI / coverage (push) Successful in 16m24s
CI / status-check (push) Successful in 4s
CI / benchmark-publish (pull_request) Has been skipped
CI / benchmark-regression (pull_request) Failing after 1m8s
CI / coverage (pull_request) Successful in 12m53s
CI / unit_tests (pull_request) Successful in 8m11s
CI / quality (pull_request) Successful in 1m35s
CI / e2e_tests (pull_request) Successful in 4m58s
CI / helm (pull_request) Successful in 40s
CI / push-validation (pull_request) Successful in 21s
CI / build (pull_request) Successful in 1m4s
CI / lint (pull_request) Successful in 1m10s
CI / typecheck (pull_request) Successful in 1m35s
CI / security (pull_request) Successful in 2m19s
CI / integration_tests (pull_request) Successful in 4m12s
CI / docker (pull_request) Successful in 2m15s
CI / status-check (pull_request) Successful in 3s
fix(git_tools): eliminate TOCTOU race in _get_base_env() with double-checked locking
Add module-level _BASE_ENV_LOCK: threading.Lock and replace the bare
if _BASE_ENV is None assignment with double-checked locking. The outer
check keeps the warm-cache path lock-free; the inner check inside
with _BASE_ENV_LOCK prevents duplicate initialisation when two threads
race on the very first call.

Add three BDD scenarios in features/git_tools.feature (step definitions
in features/steps/git_tools_thread_safety_steps.py) verifying caching
identity, content correctness, and thread safety under 20 concurrent
threads using threading.Barrier.

Update CHANGELOG.md and CONTRIBUTORS.md per contribution guidelines.

ISSUES CLOSED: #7619
2026-05-05 05:25:28 +00:00

286 lines
12 KiB
Gherkin

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