Skip to content

fix: resolve entire explain hanging on repos with many checkpoints#551

Open
dvydra wants to merge 6 commits intomainfrom
fix/explain-hang-performance
Open

fix: resolve entire explain hanging on repos with many checkpoints#551
dvydra wants to merge 6 commits intomainfrom
fix/explain-hang-performance

Conversation

@dvydra
Copy link
Contributor

@dvydra dvydra commented Feb 27, 2026

Summary

entire explain (and any code path calling getBranchCheckpoints) hangs indefinitely on repos that have accumulated hundreds of checkpoints. This repo has 236 checkpoints on main and the command never returns — requires Ctrl-Z + kill -9.

Root cause: Two O(N) go-git operations that don't scale:

1. Full transcript parsing for list view prompts

collectCheckpoint called ReadLatestSessionContent + extractPromptsFromTranscript for every checkpoint — reading multi-MB JSONL transcript blobs from git and parsing them as JSON, just to extract the first user prompt for the list view display.

Introduced in: 0e7a05f1 (Feb 2, PR #130) — replaced the cheap ReadSessionPromptFromTree (reads tiny prompt.txt) with full transcript parsing.

Fix: New readPromptFromMetadataTree reads prompt.txt directly from the metadata branch tree. The metadata tree is fetched once and reused for all checkpoints — O(1) per checkpoint instead of O(transcript_size).

2. Full tree diff for temporary checkpoint filtering

convertTemporaryCheckpoint called hasCodeChanges which does parentTree.Diff(commitTree) via go-git for every temporary checkpoint. Each diff resolves hundreds of tree/blob objects through go-git's pure-Go SHA1 packfile reader (no ARM64 assembly), making each call very slow.

Fix: New hasAnyChanges compares tree hashes in O(1). For the list view, showing a few metadata-only checkpoints is acceptable — the full hasCodeChanges diff is preserved for contexts that need precise filtering.

Performance

Metric Before After
entire explain Hangs indefinitely 344ms
Transcript reads 236 × multi-MB blobs 0 (reads prompt.txt)
Tree diffs 32 × full diffs 32 × hash comparisons

Test plan

  • mise run fmt — clean
  • mise run lint — 0 issues
  • mise run test — all pass
  • mise run test:integration — all pass
  • Manual test: entire explain --no-pager completes in 344ms
  • Verify prompt text still shows correctly in list view
  • Verify entire explain --checkpoint <id> detail view still works (uses different code path)

@pfleidi — this was a gradual degradation (code introduced Feb 2, grew worse as checkpoints accumulated + large packfile created Feb 25). The fix is surgical — only changes the list view's data fetching strategy, doesn't touch the detail view or any other code paths.

🤖 Generated with Claude Code

Two performance regressions in getBranchCheckpoints caused `entire explain`
(and any command that lists checkpoints) to hang indefinitely on repos with
hundreds of checkpoints:

1. collectCheckpoint called ReadLatestSessionContent + extractPromptsFromTranscript
   for every checkpoint (236 on this repo's main branch). This reads multi-MB
   JSONL transcript blobs from git and parses them as JSON — just to extract the
   first user prompt for the list view. Replaced with readPromptFromMetadataTree
   which reads the tiny prompt.txt file directly from the metadata branch tree.
   Introduced in 0e7a05f (Feb 2, PR #130).

2. convertTemporaryCheckpoint called hasCodeChanges which does a full tree diff
   (parentTree.Diff(commitTree)) via go-git for every temporary checkpoint.
   Each diff resolves hundreds of tree/blob objects through go-git's slow
   pure-Go SHA1 packfile reader. Replaced with hasAnyChanges which compares
   tree hashes in O(1) — acceptable for list views where showing a few
   metadata-only checkpoints is better than hanging.

Result: `entire explain` goes from hanging indefinitely to completing in ~350ms.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: ab4fe695467e
@dvydra dvydra requested a review from a team as a code owner February 27, 2026 21:28
Copilot AI review requested due to automatic review settings February 27, 2026 21:28
@cursor
Copy link

cursor bot commented Feb 27, 2026

PR Summary

Medium Risk
Changes checkpoint list-view data fetching and filtering to avoid expensive go-git transcript reads/diffs; behavior may shift slightly (e.g., prompts missing in edge cases or metadata-only temporary checkpoints appearing).

Overview
Fixes entire explain hanging on repos with many checkpoints by removing two expensive per-checkpoint operations in the list view.

Committed checkpoint prompts are now read by fetching the metadata branch tree once and pulling prompt.txt directly via readPromptFromMetadataTree (instead of loading/parsing full session transcripts). Temporary checkpoint filtering now uses new hasAnyChanges (tree-hash comparison) rather than the full-diff hasCodeChanges, trading perfect “code vs metadata” filtering for much faster list rendering.

Written by Cursor Bugbot for commit eff1e18. Configure here.

@dvydra
Copy link
Contributor Author

dvydra commented Feb 27, 2026

The second fix definitely fixed the problem. The first one might be a red herring

Full transcript parsing for list view prompts

Check if reverting that bit makes a difference to timing.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Improves the performance of entire explain list view by removing per-checkpoint expensive go-git operations (full transcript parsing and tree diffs) that don’t scale with hundreds of checkpoints.

Changes:

  • Adds a fast path to read the session prompt preview directly from prompt.txt in the metadata-branch tree (avoids reading/parsing transcripts per checkpoint).
  • Replaces diff-based temporary-checkpoint filtering with an O(1) tree-hash comparison to avoid repeated expensive go-git tree diffs.

pfleidi and others added 2 commits February 27, 2026 15:04
Address PR feedback:
- Move readPromptFromMetadataTree logic into strategy.ReadLatestSessionPromptFromCommittedTree
  to avoid hard-coding the metadata branch directory layout in explain.go
- Add test for getBranchCheckpoints reading prompts from committed checkpoints
- Add tests for hasAnyChanges (first commit, metadata-only, no-op tree)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: 019ae8860bb1
@pfleidi pfleidi requested a review from Copilot February 27, 2026 23:30
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

Entire-Checkpoint: 192b108bb2d7
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

Entire-Checkpoint: 9fb87f0597df
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

3 participants