Skip to content

Add disk-backed transcript parse cache (4.2x faster subagent-heavy renders) - #119

Merged
tmck-code merged 5 commits into
mainfrom
feat/cache-transcript-parses
Aug 20, 2026
Merged

Add disk-backed transcript parse cache (4.2x faster subagent-heavy renders)#119
tmck-code merged 5 commits into
mainfrom
feat/cache-transcript-parses

Conversation

@tmck-code

@tmck-code tmck-code commented Aug 15, 2026

Copy link
Copy Markdown
Owner

Context

  • Statusline renders slow down permanently as a session accumulates subagents.
  • each render is a fresh process that re-reads every agent transcript from byte 0, twice (once for the subagent tree, once for tool counts).

Important

This PR implements a cache for a measured speed increase of 318.6 ms > 61.1 ms (5.2x)

Bumps version to 0.8.0

Changes

Transcript parse cache (claude/yas/info/parsecache.py, new)

  • Per-session JSON cache at ~/.claude/yas-cache/transcripts.<session_id>.json, version-stamped, atomic os.replace writes, 24 h retention, fail-safe: any corruption/mismatch falls back to a full re-parse.
  • Entries keyed by (path, mtime, size) plus parse arguments (resume_after, clear_epoch/skip_sidechain), so argument changes can't return stale results.
  • Finished agent transcripts are immutable → warm hit rate approaches 100%; only the main transcript tail and active agents are re-read, incrementally via the existing tail-read machinery.

Cache-backed readers (subagents.py, toolcounts.py)

  • parse_transcript, the notification/tool-result tail readers, and count_transcript all seed from and write back to the cache.
  • New totals_only parse mode for conclusively-retired agents (cold-cache fallback), with a post-visible() re-parse fail-safe so no rendered row is ever built from a stub.
  • ToolCounts.gather still covers every subagent (rendered totals unchanged) — it's just satisfied from the cache.

Lifecycle & config

  • Cache loaded once and saved once in app.render(); SessionView keeps its no-disk-writes contract; mon and tests untouched.
  • New transcript_cache knob ([cache] in yas.toml, env YAS_TRANSCRIPT_CACHE), default on.

Measured effect (real captured payloads, hyperfine): 48-agent session 318.6 ms → 74.8 ms warm (4.2×); cold render unchanged (311.6 ms); cache file 44 KB. Rendered output is byte-identical cache-on vs cache-off (frozen-clock equivalence tests).

Checklist

  • Tests added or updated for new/changed behaviour (+37 tests; suite 1547 passed, ruff + mypy clean)
  • (bug fixes) a regression test now covers the situation so it can't come back
  • N/A — no behaviour change

Screenshots / recording

  • N/A — no visible change (demo/visual gate confirmed zero diff)

Benchmark

Command Mean [ms] Min [ms] Max [ms] Relative
main 51.7 ± 4.8 42.8 66.4 1.02 ± 0.16
PR 50.6 ± 6.1 44.1 82.8 1.00

(make bench uses a small demo payload, so parity is expected — the gain is on subagent-heavy sessions: 318.6 ms → 74.8 ms warm on a real 48-agent session, measured with hyperfine on captured production payloads.)

System info

Key Value
OS Linux archlinux 7.1.8-arch1-3 #1 SMP PREEMPT_DYNAMIC Tue, 11 Aug 2026 09:16:08 +0000 x86_64 GNU/Linux
Claude Code 2.1.233 (Claude Code)
Terminal TERM=tmux-256color TERM_PROGRAM=tmux SHELL=/bin/bash COLORTERM=truecolor
Locale LANG=en_AU.UTF-8 LC_ALL=
Python Python 3.13.13
uv uv 0.12.3 (507230998 2026-08-07 x86_64-unknown-linux-gnu)

🤖 Generated with Claude Code

Test and others added 4 commits August 15, 2026 21:42
Renders of subagent-heavy sessions re-read every agent transcript from
byte 0 twice per render (fresh process each time), scaling linearly with
accumulated agents: 318.6 ms for a 48-agent session vs 61.1 ms fresh.

Persist parse results per session to ~/.claude/yas-cache/, keyed by
(path, mtime, size) plus parse args; finished transcripts are immutable
so warm renders skip them entirely. Adds totals-only parsing for
conclusively-retired agents and cache-backed ToolCounts. Warm render
drops to 74.8 ms (4.2x). New transcript_cache config knob, default on.

Implements OpenSpec change cache-transcript-parses.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…/ path consolidation

Resolves conflicts between the #120 path-consolidation (all YAS files moved
under ~/.claude/yas/) and the transcript-parse-cache feature by routing the
transcript cache path through a new transcript_cache_path() helper in
constants.py, matching the consolidated cache_dir() convention.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@tmck-code
tmck-code marked this pull request as ready for review August 20, 2026 13:31
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@tmck-code
tmck-code merged commit 219f491 into main Aug 20, 2026
6 checks passed
@tmck-code
tmck-code deleted the feat/cache-transcript-parses branch August 20, 2026 13:39
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.

1 participant