Skip to content

Checkpoint metadata.json exceeds GitHub's 100 MB limit: files_touched is never deduplicated, and nested git worktrees are walked #1927

Description

@ecgang

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

  1. Deduplicate files_touched before writing. One line, ~19× smaller, no
    behaviour change — the array is a set in everything but type.
  2. 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.
  3. 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.
  4. 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions