Summary
On a repo that uses git worktrees, entire/checkpoints/v1 grows a
metadata.json large enough that GitHub refuses the push outright. Once that
happens the checkpoint branch can never be pushed again without rewriting its
history, and the failure surfaces only as a warning attached to an unrelated
git push.
Two separate causes compound. Either one alone would be survivable.
Symptom
Every git push in the host repo now ends with:
[entire] Warning: failed to push entire/checkpoints/v1 after sync: push failed:
remote: error: File 53/596dc8fea0/1/metadata.json is 111.79 MB; this exceeds
GitHub's file size limit of 100.00 MB
remote: error: GH001: Large files detected.
! refs/heads/entire/checkpoints/v1 -> [remote rejected] (pre-receive hook declined)
The user's own branches push fine, so this is easy to miss for a long time —
it reads as noise attached to a successful push.
Environment: Entire CLI 0.8.42, go1.26.4, darwin/arm64.
Cause 1 — files_touched is appended to, never deduplicated
That metadata.json is a single checkpoint record with
"checkpoints_count": 213. Measured on the blob:
|
|
| file size |
111.79 MB |
| lines |
1,292,267 |
files_touched entries |
1,292,257 |
| unique entries |
67,340 |
files_touched share of the file |
105.6 MB of 111.79 MB (94%) |
A 19× duplication factor. No single path dominates — the most repeated one
appears 77 times — so this is broad re-appending across checkpoints rather than
one pathological entry. Deduplicating alone takes the file to roughly 5.5 MB
and puts it back under the limit.
Cause 2 — nested git worktrees are walked as if they were project files
Of the 67,340 unique paths, broken down by top-level segment:
| prefix |
unique paths |
.claude/ (agent worktrees live here) |
43,184 |
worktrees/ |
21,833 |
| the actual project |
~1,241 |
96% of what is being recorded lives inside other git checkouts nested under
the repo root. Linked worktrees have their own .git and their own history;
their contents are not changes to the host repo. node_modules is correctly
excluded, so the walker already has an ignore mechanism — nested worktrees just
aren't in it.
This is the part that makes the growth unbounded in practice. Anyone driving
parallel coding agents accumulates worktrees, and each one multiplies the
recorded set.
Scale of the resulting branch
|
|
commits on entire/checkpoints/v1 |
8,842 |
| files |
32,799 |
| unique blob bytes |
1,336.9 MB |
| distinct blobs over 50 MB |
3 |
Many identical 50 MB full.jsonl blobs exist, but git dedupes those by hash,
so they cost storage once. The 111.79 MB metadata.json is the only blob that
actually blocks the push.
Suggested fixes, in order of leverage
- Deduplicate
files_touched before writing. One line, ~19× smaller, no
behaviour change — the array is a set in everything but type.
- Do not descend into nested git worktrees. A directory containing a
.git entry that is not the repo root is a separate checkout. This is where
the remaining ~50× lives.
- Store
files_touched as a newline-delimited sidecar rather than inline
in a JSON object. It streams, it chunks, and it stops one growing field from
deciding whether the whole record is writable.
- Check size before pushing and fail loudly. A blob over the remote's
limit is unrecoverable once committed; catching it at write time is cheap,
and catching it at push time is too late. The current warning is easy to
scroll past.
Fix 1 is the emergency stop. Fix 2 is the actual bug. Fixes 3 and 4 are what
stop the next version of this.
Offer
Happy to send a PR for 1 and 2 — they look self-contained and the repo has CI
in test.yml. Say which shape you'd prefer for 2 (skip any dir containing
.git, versus consulting git worktree list), since that's a design call
rather than a mechanical one. I can also supply the checkpoint that triggered
this if a reproduction is useful, though it is 111 MB.
Disclosure: this report was researched and written with AI assistance
(Claude Code). Every number above is measured from the actual blob with
git cat-file, not estimated.
Summary
On a repo that uses git worktrees,
entire/checkpoints/v1grows ametadata.jsonlarge enough that GitHub refuses the push outright. Once thathappens the checkpoint branch can never be pushed again without rewriting its
history, and the failure surfaces only as a warning attached to an unrelated
git push.Two separate causes compound. Either one alone would be survivable.
Symptom
Every
git pushin the host repo now ends with:The user's own branches push fine, so this is easy to miss for a long time —
it reads as noise attached to a successful push.
Environment: Entire CLI 0.8.42, go1.26.4, darwin/arm64.
Cause 1 —
files_touchedis appended to, never deduplicatedThat
metadata.jsonis a single checkpoint record with"checkpoints_count": 213. Measured on the blob:files_touchedentriesfiles_touchedshare of the fileA 19× duplication factor. No single path dominates — the most repeated one
appears 77 times — so this is broad re-appending across checkpoints rather than
one pathological entry. Deduplicating alone takes the file to roughly 5.5 MB
and puts it back under the limit.
Cause 2 — nested git worktrees are walked as if they were project files
Of the 67,340 unique paths, broken down by top-level segment:
.claude/(agent worktrees live here)worktrees/96% of what is being recorded lives inside other git checkouts nested under
the repo root. Linked worktrees have their own
.gitand their own history;their contents are not changes to the host repo.
node_modulesis correctlyexcluded, so the walker already has an ignore mechanism — nested worktrees just
aren't in it.
This is the part that makes the growth unbounded in practice. Anyone driving
parallel coding agents accumulates worktrees, and each one multiplies the
recorded set.
Scale of the resulting branch
entire/checkpoints/v1Many identical 50 MB
full.jsonlblobs exist, but git dedupes those by hash,so they cost storage once. The 111.79 MB
metadata.jsonis the only blob thatactually blocks the push.
Suggested fixes, in order of leverage
files_touchedbefore writing. One line, ~19× smaller, nobehaviour change — the array is a set in everything but type.
.gitentry that is not the repo root is a separate checkout. This is wherethe remaining ~50× lives.
files_touchedas a newline-delimited sidecar rather than inlinein a JSON object. It streams, it chunks, and it stops one growing field from
deciding whether the whole record is writable.
limit is unrecoverable once committed; catching it at write time is cheap,
and catching it at push time is too late. The current warning is easy to
scroll past.
Fix 1 is the emergency stop. Fix 2 is the actual bug. Fixes 3 and 4 are what
stop the next version of this.
Offer
Happy to send a PR for 1 and 2 — they look self-contained and the repo has CI
in
test.yml. Say which shape you'd prefer for 2 (skip any dir containing.git, versus consultinggit worktree list), since that's a design callrather than a mechanical one. I can also supply the checkpoint that triggered
this if a reproduction is useful, though it is 111 MB.
Disclosure: this report was researched and written with AI assistance
(Claude Code). Every number above is measured from the actual blob with
git cat-file, not estimated.