perf(attribution): pin the Git shim interpreter off the pyenv shim #825

Merged
coilysiren merged 1 commit from aos/claude/attribution-interpreter into main 2026-08-14 05:15:25 +00:00
Owner

Closes #823.

agent_git_attribution.py is symlinked as git and executed directly, so its shebang selects the interpreter on every agent Git call. With env python3 that resolves through the pyenv shim on PATH.

Measured on kais-macbook-pro, ten runs each:

shebang per call
/usr/bin/env python3 258ms
/usr/bin/python3 67ms

Provenance

The shebang change is not mine. It was diagnosed on 2026-08-12 on branch aos/claude/git-attribution-pin-interpreter and left as an uncommitted stash entry with no issue and no commits on the branch. This adopts it so it stops living somewhere a git stash drop would erase. The original stash is untouched and still present.

I re-measured rather than trusting the stashed comment. It claimed 320ms against 43ms, I get 258 against 67. Same conclusion, different absolute numbers, so the comment now carries mine.

The guard I added

The role assumed /usr/bin/python3 exists without ever saying so. That assumption holds today because inventory/fleet.yml and inventory/local.yml both pin ansible_python_interpreter to it, so Ansible itself runs under that interpreter wherever this role converges. It breaks if a host overrides the interpreter, and the blast radius is out of proportion to the cause: the shim is git, so a missing interpreter breaks Git for agents on that host.

The assert runs before the copy. A host that cannot satisfy the pin fails the converge loudly and keeps its existing working Git, instead of receiving a shim that cannot execute.

Two alternatives considered and rejected:

  • Silent fallback to env python3. Restores the 190ms with no signal that the pin never applied. Worse than failing.
  • Resolving the shebang from ansible_python_interpreter at converge time. Cleaner, and the file has no Jinja-hostile sequences so templating would work. But scripts/agent_git_attribution_test.py imports files/agent_git_attribution.py at that exact path through importlib, so it needs the test changed too. Left as a follow-up.

Verification

  • ward exec ansible-lint passes at the production profile.
  • ward exec ansible-sync -- check local tags=claude-hooks runs the new stat and assert as ok, and the copy diff shows the new shebang.
  • Compiles clean under Apple's /usr/bin/python3 (3.9.6). Imports are stdlib only, from __future__ import annotations is present, and nothing evaluates annotations at runtime. Identical CLI behavior under 3.9.6 and pyenv 3.13.2.

Two gaps worth stating rather than burying:

scripts/agent_git_attribution_test.py does not complete in an agent session, hanging past 45 seconds under both 3.9.6 and 3.13.2. That is interpreter-independent and predates this change, so it is not evidence about the pin, but it does mean the suite did not gate this.

The same check-mode run reports one failure, in claude-hooks, looking for a script under $HOME/projects that does not exist in the AOS sandbox home. Environmental, unrelated, and also present before this change.

Note

Apple's /usr/bin/python3 is 3.9.6, end of life upstream. It runs this script correctly today. Moving to a maintained interpreter is a separate question from getting off the pyenv shim, and the new agent_git_attribution_interpreter default is the single place to change it.

🤖 Generated with Claude Code

Closes #823. `agent_git_attribution.py` is symlinked as `git` and executed directly, so its shebang selects the interpreter on **every agent Git call**. With `env python3` that resolves through the pyenv shim on PATH. Measured on kais-macbook-pro, ten runs each: | shebang | per call | | --- | --- | | `/usr/bin/env python3` | 258ms | | `/usr/bin/python3` | 67ms | ## Provenance The shebang change is not mine. It was diagnosed on 2026-08-12 on branch `aos/claude/git-attribution-pin-interpreter` and left as an uncommitted stash entry with no issue and no commits on the branch. This adopts it so it stops living somewhere a `git stash drop` would erase. The original stash is untouched and still present. I re-measured rather than trusting the stashed comment. It claimed 320ms against 43ms, I get 258 against 67. Same conclusion, different absolute numbers, so the comment now carries mine. ## The guard I added The role assumed `/usr/bin/python3` exists without ever saying so. That assumption holds today because `inventory/fleet.yml` and `inventory/local.yml` both pin `ansible_python_interpreter` to it, so Ansible itself runs under that interpreter wherever this role converges. It breaks if a host overrides the interpreter, and the blast radius is out of proportion to the cause: the shim **is** `git`, so a missing interpreter breaks Git for agents on that host. The assert runs before the copy. A host that cannot satisfy the pin fails the converge loudly and keeps its existing working Git, instead of receiving a shim that cannot execute. Two alternatives considered and rejected: * **Silent fallback to `env python3`.** Restores the 190ms with no signal that the pin never applied. Worse than failing. * **Resolving the shebang from `ansible_python_interpreter` at converge time.** Cleaner, and the file has no Jinja-hostile sequences so templating would work. But `scripts/agent_git_attribution_test.py` imports `files/agent_git_attribution.py` at that exact path through `importlib`, so it needs the test changed too. Left as a follow-up. ## Verification * `ward exec ansible-lint` passes at the production profile. * `ward exec ansible-sync -- check local tags=claude-hooks` runs the new stat and assert as `ok`, and the copy diff shows the new shebang. * Compiles clean under Apple's `/usr/bin/python3` (3.9.6). Imports are stdlib only, `from __future__ import annotations` is present, and nothing evaluates annotations at runtime. Identical CLI behavior under 3.9.6 and pyenv 3.13.2. Two gaps worth stating rather than burying: `scripts/agent_git_attribution_test.py` does not complete in an agent session, hanging past 45 seconds under **both** 3.9.6 and 3.13.2. That is interpreter-independent and predates this change, so it is not evidence about the pin, but it does mean the suite did not gate this. The same check-mode run reports one failure, in `claude-hooks`, looking for a script under `$HOME/projects` that does not exist in the AOS sandbox home. Environmental, unrelated, and also present before this change. ## Note Apple's `/usr/bin/python3` is 3.9.6, end of life upstream. It runs this script correctly today. Moving to a maintained interpreter is a separate question from getting off the pyenv shim, and the new `agent_git_attribution_interpreter` default is the single place to change it. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
perf(attribution): pin the Git shim interpreter off the pyenv shim
All checks were successful
CI / lint (pull_request) Successful in 57s
TruffleHog / Scan for secrets (pull_request) Successful in 5s
f977e8c4c6
agent_git_attribution.py is symlinked as `git` and executed directly, so
its shebang selects the interpreter on every agent Git call. With `env
python3` that resolves through the pyenv shim on PATH, measured at 258ms
against 67ms direct on kais-macbook-pro.

The fleet already standardizes on this interpreter. Both inventory files
pin ansible_python_interpreter to /usr/bin/python3, so Ansible itself
runs under it wherever this role converges.

Guard the assumption rather than leaving it implicit. The shim is `git`,
so a missing interpreter would break Git for agents on that host. Assert
the path before the shim is installed, which fails the converge loudly
and leaves the host's working Git alone. A silent fallback to `env
python3` was rejected because it would restore the cost with no signal.

Adopts work left in a stash on aos/claude/git-attribution-pin-interpreter
on 2026-08-12.

closes #823

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: Kai Siren <coilysiren@gmail.com>
Co-authored-by: Claude <noreply@anthropic.com>
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
coilyco-flight-deck/infrastructure!825
No description provided.