Skip to content

fix: repair stale checkpoints and flag worktree setup gaps - #7

Draft
MuskanPaliwal wants to merge 7 commits into
mainfrom
fix-checkpoint-metadata-followups
Draft

MuskanPaliwal wants to merge 7 commits into
mainfrom
fix-checkpoint-metadata-followups

Conversation

@MuskanPaliwal

@MuskanPaliwal MuskanPaliwal commented Sep 14, 2026 •

Copy link
Copy Markdown
Owner

karthik-rameshkumar and others added 7 commits September 13, 2026 21:27
CLI versions before v0.10.1 walked nested git checkouts (Claude Code's
.claude/worktrees/) into prompt_attributions.user_added_per_file on every
prompt, producing 70MB and 106MB per-session metadata.json blobs on
entire/checkpoints/v1. GitHub refuses blobs over 100 MiB, so one such blob
anywhere in the branch history makes it unpushable and unmirrorable there
(customer report, T-88). The v0.10.1 fix stopped new bloat but left the
history behind, and the only remedy was a hand-written filter-repo script.

Prevention: checkpoint.CapPromptAttributions drops the field above 4 MiB at
the checkpoint write boundary (both writers). Nothing reads the field back
and entire-api's ingest already salvages a metadata.json clipped at it.

Repair: `entire doctor` gains a "Checkpoint metadata size" check that scans
the branch (and the elected sync remote's tracking ref) for metadata.json
blobs over 50 MiB and offers to rewrite the branch without the field, then
force-push it with --force-with-lease against the observed tip. Commit
messages, authors, dates, checkpoint IDs and every other blob are preserved;
history before the first oversized blob keeps its hashes. `doctor --force`
deliberately does not apply it (it rewrites history and pushes); the opt-in
is an interactive yes or `entire doctor shrink-checkpoint-metadata [--yes]`.

Reconciliation is by content, not hash: local is rewritten before the
remote is reconciled with the same memoized rewriter, and the rewritten tips
are compared by tree, so an independently repaired clone (or commit signing,
which makes rewritten hashes differ) is adopted rather than replayed as
duplicates. A dedicated checkpoint_remote URL is report-only. The repair is
per clone and says so.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
TestHashComparisonsUseEqual flagged `newEntry.Hash != e.Hash`. Every hash
comparison in metadata_shrink.go now goes through Hash.Equal, including the
ones the textual guard does not reach: plumbing.Hash carries an object-format
field alongside the bytes, so == can disagree with Equal on a sha256 repo.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- Decide the dedicated checkpoint_remote case before the no-remote early
  return, so a repository whose only checkpoint destination is that URL is
  refused the repair consistently. Settings are read for the repository the
  scan was handed (new settings.LoadForWorktreeRoot), not the process cwd.
- Apply the prompt_attributions cap in updateSessionMetadata too: the
  finalize/backfill path re-marshals metadata an older CLI wrote and would
  otherwise copy a pre-cap field into every later rewrite.
- Writer-level tests for the cap on both the fresh write and the re-marshal
  path, in addition to the helper unit tests.
- Report the remote as unverified instead of printing OK when its tracking
  ref could not be read.
- Distinguish the 50 MiB warning threshold from GitHub's 100 MiB hard limit
  in the report; only files over the hard limit make the branch unpushable.
- Open test repositories through gitrepo.OpenPath, per repo convention.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Entire-Checkpoint: 01M2DSN65JETVT9YY8T4ABW2J5
Entire-Checkpoint: 01M2FNRCE1AMZZVN8C15DVKP6S
…ckpoint-metadata

# Conflicts:
#	CLAUDE.md
#	cmd/entire/cli/doctor.go
Entire-Checkpoint: 01M2SP2J7Z3PW4XVMHV24HDTMG
MuskanPaliwal pushed a commit that referenced this pull request Sep 24, 2026
…ionFile

#5 external.go reimplemented SessionStore.Name's relative branch verbatim —
same IsAbs/VolumeName pairing, same ToSlash(Clean(FromSlash(…))) — without the
comment explaining why the pairing is needed, and it had to know that the store
checks a name only after Name has produced one, which is WriteFile's private
prologue. Name's relative branch is now a named primitive both callers share,
the store exposes ValidateExternalSessionRef (every rule needing no store, plus
whether the ref is filesystem-shaped) and ValidateExternalWriteRef (the
store-backed half), and ValidateWritePath is unexported.

#6 The preflight ran after marshalling and was gated as a whole on RepoPath !=
"". Both current callers set RepoPath, which is what makes that a check that
disappears silently for the next one; the lexical rules need no repo, so only
the store-backed half is conditional now. The remaining asymmetry — an opaque
relative ref is forwarded as given while an absolute one is resolved — is the
protocol's, not this function's, and is left alone deliberately.

#7 ErrOutsideSessionStore said "path is outside the agent's session directory"
for an ID that never resolved anywhere, which names the wrong problem.
Malformed names now report ErrUnsafeSessionName; a path that genuinely left the
store still reports ErrOutsideSessionStore. validateWriteName no longer claims
to "inspect" anything — it touches no filesystem.

#8 The volume-separator check existed in both validators, worded identically,
while the shared reason helper omitted it. It moves in. ValidateSessionID keeps
its separator check ahead of the helper so a Windows absolute path still reports
the separators rather than the colon.

The ResolveSessionFile contract this branch wrote into agent.go had two live
violations: copilotcli's resolveTranscriptRef and cursor's, both passing a raw
payload ID to a resolver that puts it in a directory position. Both now resolve
through SessionStore.SessionFile, and a GitGrepGuard fails the build on the
next one. Two guards in buildAgentStop go: isSubagentAgentStop already returns
false for an unsafe ID via SessionFile, and ExtractModelFromTranscript reads a
path and never touches the ID, so gating it only dropped model attribution.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: 01M2RM92E1D4CK1JJ6NDSMTMHC
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.

2 participants