Skip to content

016 US5: git_log — the history the repository already keeps - #16

Merged
jlgore merged 1 commit into
mainfrom
018-gitlog-us5
Jul 28, 2026
Merged

016 US5: git_log — the history the repository already keeps#16
jlgore merged 1 commit into
mainfrom
018-gitlog-us5

Conversation

@jlgore

@jlgore jlgore commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

The last deferred user story from 016 that was worth building. Independent of #15 (017) — different files, no conflicts, either order merges.

What it does

git_log answers two questions a security reader asks constantly and bee could not: what changed here (mode: "log" — the commits touching a path, newest first) and which commit introduced this line (mode: "blame").

gix is the engine, per 016's two-tier rule: the value is the implementation, not someone's curated corpus, so bee links the crate. The exec allowlist stays empty — no git binary is admitted to run this — and the answer does not depend on which git is installed or on parsing output formatted for humans.

Where it runs

In a scope-joined child (bee gitlog-worker), exec'd through the sandbox exactly like search-worker and astgrep-worker. .git is a directory of files like any other; a policy that denies it means it, and reading history in the harness would be reading around the sandbox.

The part that needed care

Three outcomes are indistinguishable from outside a child that printed nothing:

Case Exit What the model sees
Commits found 0 the lines
No commit touches the path, but there is a repository 0 no commits in this repository touch that path
The path is not inside a repository 3 did not run: … is not inside a repository
The request cannot be answered (bad line, unreadable object) 4 failed: <diagnostic>

The worker separates them by exit code, and the wrapper maps those to Completed / Unavailable { NotARepository } / Failed. Grepping the diagnostic would tie the distinction to wording any later edit could break. The empty case is spelled out in a sentence rather than left as silence — FR-012 is about a reader being able to tell "found nothing" from "did not run" at a glance.

NotARepository was already in the outcome enum: 016 laid the groundwork and left the tool unbuilt.

Tests

The fixture is a real repository built with the git CLI — a .git directory cannot be checked into this repo, and a hand-built one would assert against bee's idea of a repository rather than git's. Two commits, the second changing exactly one line, which is what makes blame falsifiable: line 3 must be the second commit and line 1 must still be the first. A blame that always answered HEAD passes one and fails the other.

Also covered: the untracked-file case (empty history, exit 0 — the case the refusal must stay distinguishable from), the limit taking from the newest end, and a line past the end of the file being a diagnostic rather than an invented attribution.

Housekeeping

git_log joins SEC_TOOLS, so a build without --features gitlog registers the refusing NotCompiledIn stub rather than rejecting the name as unknown. README's feature table and quickstart get a §US5 walkthrough; the "Deferred to follow-on" section now lists only US6 (CodeQL), which stays deferred deliberately — its traced languages would mean admitting every compiler and linker a build invokes, the widening SC-006 exists to prevent.

cargo fmt/clippy -D warnings clean; full workspace suite green at default and sec features.

🤖 Generated with Claude Code

`git_log` answers two questions a security reader asks constantly and
bee could not: what changed here, and which commit introduced this line.
`gix` is the engine, so the exec allowlist stays empty — no `git` binary
is admitted to run this, and the answer does not depend on which `git`
happens to be installed or on parsing output meant for humans.

Same seam as `search` and `ast_grep`: the library reads inside a
scope-joined child (`bee gitlog-worker`), because `.git` is a directory
of files like any other and a policy that denies it means it. Reading
history in the harness would read around the sandbox.

The care is all in one place. Three outcomes look identical from outside
a child that printed nothing — no commits touch the path, the path is
not in a repository, the request could not be answered — so the worker
separates them by exit code (0 / 3 / 4) and the wrapper maps those to
Completed / Unavailable{NotARepository} / Failed. Grepping the
diagnostic would tie that distinction to wording; an empty history is
also spelled out in words rather than left as silence.

Blame is tested against both halves: line 3 (changed by the second
commit) and line 1 (still the first). A blame that always answered HEAD
would pass on one and fail the other.

Read-only by construction — nothing here writes an object, a ref, or a
config entry.

Closes T054–T057.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@jlgore
jlgore merged commit 1975a14 into main Jul 28, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant