fix(base-image): stop appending to .gitignore from startup.sh (#953)#978
Merged
Conversation
`docker/base-image/startup.sh` had two sites that did `echo "<pattern>" >> /home/developer/.gitignore` if a `grep -q` guard missed — once inside the GitHub-clone block (for `.local/`) and once unconditionally at end of startup (for `content/` + `.local/`). The greps used inconsistent patterns: - `grep -q ".local/"` — unanchored regex; `.` matches any char so it almost always succeeds, but on first boot of a fresh clone the file may not exist yet and the prior `[ ! -f ]` block writes a comment- only header that the grep then matches against. - `grep -q "^content/$"` / `grep -q "^\.local/$"` — anchored exact match; misses on trailing whitespace, comments, CRLF, or a file missing its terminal newline. Field hypothesis (issue body) is the trailing-whitespace / missing-newline path. Result: any template whose `.gitignore` already (correctly) contained the canonical patterns could still end up with `M .gitignore` against `origin/main` immediately after deployment — verified by reproducing in a scratch git repo: with a 2-line `.gitignore` lacking `.local/`/`content/`, simulating the startup.sh block produces `M .gitignore` and a `+.local/\n+content/` diff against `origin/main`. Fix: remove both append sites entirely. The canonical pattern list lives in `_GITIGNORE_PATTERNS` (`src/backend/services/git_service.py`) and is applied via `_build_gitignore_merge_command`'s idempotent `grep -qxF` checks (fixed-string, exact-line — no anchor false negatives). Two call sites cover the lifecycle: - `initialize_git_in_container` runs the merge on first Trinity-orchestrated git init. - `_migrate_workspace_gitignore` runs it on every push so existing agents adopt new patterns without rebuilding. Trade-off for the GitHub-clone-in-container flow: between container boot and first Trinity push, `git status` may show `.local/`-class files as untracked if the cloned template's `.gitignore` is incomplete. No remote impact — the canonical merge runs before any commit hits GitHub via Trinity's push handler. Templates whose `.gitignore` ships the patterns correctly stop drifting from `origin/main` (the AC's primary concern). Regression guard: `tests/unit/test_953_startup_sh_no_gitignore_writes.py` greps startup.sh for any `> /home/developer/.gitignore` or `>>` redirect and fails if one is reintroduced. Also asserts the replacement comment block keeps a pointer to the canonical helper so future readers can find it. Existing agents are unaffected: their already-baked `startup.sh` keeps writing patterns until they pull a new base image, at which point the canonical merge takes over via the next push. Related to #953 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
vybe
approved these changes
Jun 1, 2026
vybe
left a comment
Contributor
There was a problem hiding this comment.
LGTM — verified _GITIGNORE_PATTERNS (git_service.py:677,682) covers both removed shell patterns (.local/, content/) and the canonical merge uses grep -qxF (exact-line, no false negatives). CI fully green, regression test pins the fix. Validated via /validate-pr.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Freshly-deployed agents reported
M .gitignoreagainstorigin/mainimmediately after deployment, with no user action. The drift came from twoecho "<pattern>" >> /home/developer/.gitignoresites in `docker/base-image/startup.sh` whosegrep -qguards used inconsistent / fragile patterns:grep -q ".local/"— unanchored regex, fragile against the comment-only header written by the[ ! -f ]create-if-missing block right above it.grep -q "^content/$"/grep -q "^\.local/$"— anchored exact match, false-negative on trailing whitespace, comments, CRLF, or files missing terminal newline.Result: even templates whose committed
.gitignorealready shipped the canonical patterns ended up withM .gitignoreagainstorigin/main.Reproduction (verified locally)
After the fix (no
>> .gitignorewrites from startup.sh), the same script leaves the working tree clean.Fix
Removed both append sites entirely. The canonical pattern list lives in
_GITIGNORE_PATTERNS(src/backend/services/git_service.py) and is applied via_build_gitignore_merge_command's idempotentgrep -qxFchecks (fixed-string, exact-line — no anchor false-negatives). Two call sites cover the lifecycle:initialize_git_in_containerruns the merge on first Trinity-orchestrated git init._migrate_workspace_gitignoreruns it on every push so existing agents adopt new patterns without rebuilding.Trade-off
For the GitHub-clone-in-container flow, between container boot and first Trinity push,
git statusmay show.local/-class files as untracked if the cloned template's.gitignoreis incomplete. No remote impact — the canonical merge runs at the start of Trinity's push handler before any commit hits GitHub. Templates whose.gitignoreships the patterns correctly stop drifting (AC #4).Existing agents are unaffected: their already-baked
startup.shkeeps writing patterns until they pull a new base image, at which point the canonical merge takes over via the next push.Tests
tests/unit/test_953_startup_sh_no_gitignore_writes.py— regression guard: grepsstartup.shfor any> /home/developer/.gitignoreor>>redirect and fails if one is reintroduced. Also asserts the replacement comment keeps a pointer to the canonical helper.Related to #953
Test plan
M .gitignore++.local/ +content/diff)git status.venv/bin/pytest tests/unit/test_953_startup_sh_no_gitignore_writes.py— 2 passedtrinity-agent-base:latestand deploy a fresh agent against a minimal template — confirmgit statusclean🤖 Generated with Claude Code