Skip to content

fix(web): limit stored pull request details - #10109

Closed
t3dotgg wants to merge 1 commit into
mainfrom
t3code/debt-gzuf-3-detail-cache-bound
Closed

t3dotgg wants to merge 1 commit into
mainfrom
t3code/debt-gzuf-3-detail-cache-bound

Conversation

@t3dotgg

@t3dotgg t3dotgg commented Sep 5, 2026 •

Copy link
Copy Markdown
Member

Opening PRs kept adding full detail snapshots to localStorage with no limit. They could fill the browser's storage quota.

Keep at most 20 recent snapshots within 2 MiB, including keys and the recency index. Each read still decodes only the selected PR. Retire old detail-only keys and recover from partial writes without touching other stored data.

PR details can cold-load once after upgrading, then refill through live queries. There is no layout change. Missing, denied, or full storage still falls back to live loading.

Verified:

  • 108 tests in pullRequestDetail.logic.test.ts pass, including eviction, byte limits, scope, legacy cleanup, and storage failures.
  • Targeted lint passes for both changed files.
  • Web typecheck and git diff --check pass.

Created with GPT-6 Astra in Codex.

Note

Limit stored pull request details with a bounded, indexed LRU cache

  • Replaces legacy delimiter-concatenated snapshot keys with a v1 prefix plus a JSON-serialized array of environment, project, repository, and pull-request number components, so scope parts with separator characters no longer collide.
  • Introduces a recency index and a detailSnapshotsFit validator that enforce a max of 20 entries and 2 MiB of accounted UTF-16 storage across all cached pull-request detail payloads.
  • Reworks writePullRequestDetailSnapshot to evict oldest entries until the cache fits, remove legacy and unindexed v1 keys, remeasure retained payloads, and roll back a payload write when its index update fails.
  • readPullRequestDetailSnapshot now returns a snapshot only when it is indexed, decodable, and size-verified; reads move the entry to the most-recent position without discarding a valid snapshot on recency-update failure.
  • readDetailSnapshotIndex treats missing, malformed, over-limit, or error cases as an empty index without deleting unrelated storage.
  • Risk: existing stored legacy keys under the old pullRequestDetailSnapshotKey format are ignored and removed on the next write; users will see cold cache misses until snapshots are repopulated under the v1 schema.

Macroscope summarized b6a1ad7.

@github-actions github-actions Bot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:L 100-499 changed lines (additions + deletions). labels Sep 5, 2026
@macroscopeapp

macroscopeapp Bot commented Sep 5, 2026 •

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This PR replaces simple pull-request detail persistence with a versioned, indexed LRU cache that performs byte accounting, migration, eviction, and failure recovery in the production web path. The extensive tests reduce uncertainty, but the new storage state machine is a material runtime change rather than a mechanical tweak.

No code changes detected at b6a1ad7. Prior analysis still applies.

You can add or adjust custom eligibility rules. Learn more.

@github-actions

github-actions Bot commented Sep 5, 2026 •

Copy link
Copy Markdown
Contributor

Thread transfer impact

✅ Thread transfer remains within every enforced ceiling.

Provider Metric Main baseline This PR Impact PR ceiling
Codex Total thread wire 13.6 KiB 13.5 KiB −62 B (−0.4%) 15.1 KiB ✅
Codex Thread snapshot wire 7.0 KiB 7.0 KiB −5 B (−0.1%) 7.3 KiB ✅
Codex Live turn WebSocket wire 6.6 KiB 6.5 KiB −57 B (−0.8%) 7.8 KiB ✅
Codex Live turn WebSocket decoded 57.1 KiB 57.0 KiB −88 B (−0.2%) 66.4 KiB ✅
Codex Live turn messages 10 8 −2 (−20.0%) 21 ✅
Claude Total thread wire 13.5 KiB 13.5 KiB −2 B (−0.0%) 15.1 KiB ✅
Claude Thread snapshot wire 7.0 KiB 7.0 KiB +9 B (+0.1%) 7.3 KiB ✅
Claude Live turn WebSocket wire 6.5 KiB 6.5 KiB −11 B (−0.2%) 7.8 KiB ✅
Claude Live turn WebSocket decoded 57.8 KiB 57.8 KiB 0 B (0.0%) 66.4 KiB ✅
Claude Live turn messages 8 8 0 (0.0%) 21 ✅

Baseline: 84aebb7 · PR result: b6a1ad7 · Source CI: success

Scenario and decoded snapshot size

10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.

  • Codex decoded thread snapshot: 113.8 KiB
  • Claude decoded thread snapshot: 114.5 KiB

Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed.

@t3dotgg

t3dotgg commented Sep 6, 2026

Copy link
Copy Markdown
Member Author

Note

🤖 Claude Fable 5.1 responding on behalf of Theo

Closing. The leak is real but slow (one localStorage key per opened PR, quota errors already fall back to a live read). The fix is an indexed LRU with UTF-16 byte accounting, an Effect Schema for the index, a full localStorage scan on every write, remeasurement of every retained payload, rollback on index write failure, and a read path that now mutates storage to bump recency. The key format change also drops every existing snapshot on upgrade. A 20-entry cap on a small key list is about 30 lines and fixes the same problem. Would take that as a fresh PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L 100-499 changed lines (additions + deletions). vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant