Skip to content

fix(performance): count one transcript read per history load and measure the conversations catalog - #1352

Merged
Juliusolsson05 merged 9 commits into
mainfrom
fix/monitor-transcript-operations
Sep 27, 2026
Merged

Juliusolsson05 merged 9 commits into
mainfrom
fix/monitor-transcript-operations

Conversation

@Juliusolsson05

@Juliusolsson05 Juliusolsson05 commented Sep 27, 2026 •

Copy link
Copy Markdown
Owner

Refs #769. This is the measure-first step. Moving work to a worker waits until the data says which path stalls main (see the re-check comment on #769).

Plan: docs/plans/2026-09-26-monitor-transcript-operations.md (first commit).

Problem

Every transcript read and parse still runs on the main thread, but the owner's recordings can't say whether that is what stalls main:

  • Few slow reads: 2,436 transcript.read samples and only 3 slow-operation incidents.
  • No link to stalls: 14 main-stall incidents, none within 10 s of a slow read. Main-stall incidents carry no operation attribution.
  • Double sample: each IPC initial history load recorded transcript.read twice. The outer span ended after path resolution, and the read opened a second span with the same name, so half the samples timed only the path lookup.
  • Blind spot: the conversations catalog had no monitor boundary. That covers prompt extraction (one synchronous parse over windows of up to 16 MiB), search's prompt gathering over up to 150 rows, and discovery.

What merges

  • historyLoader: the path-resolution span is now historyLoader.resolveInitialPath. It stays in the perf journal and is not a monitor operation, so each load records one transcript.read.
  • Three monitor operations:
    • conversations.discover, from the existing span;
    • conversations.extract, from sessionIndex.extractPrompts;
    • conversations.search, a new span around search's prompt gathering.
  • Slow-operation thresholds: 250 ms for extract, and 1000 ms for discover and search, matching main-stall's band so a discovery that stalls main also names itself.
  • One histogram cap for the whole vocabulary: MAX_MONITOR_OPERATION_PAIRS (operations × outcomes, now 104) sizes the aggregator, the snapshot parser and the history store, which each hard-coded 100.
  • A discovery that rejects closes its span with fail(error), instead of turning into a timeout at the sweep.

Tests

  • historyLoader.monitor.test.ts: one initial load through loadInitialHistoryChunk, on a recorded Claude transcript fixture, records exactly one transcript.read. Red on main (two).
  • service.system.test.ts: on the recorded conversations corpus, listing, a Codex row's prompts and a search record conversations.discover, conversations.extract and conversations.search. Red on main.
  • IncidentEngine.test.ts: each catalog threshold raises a named slow-operation incident exactly at the threshold, and none just below.
  • MonitorAggregator.test.ts / MonitorHistoryStore.test.ts: every legal (operation, outcome) pair is aggregated, parsed, and persisted to operations.json.
  • Gates: on cf977d45 (main merged), npx tsc -b is clean and the focused suites (history loader, conversations service, src/main/performance, src/shared/performance) pass: 22 files, 126 tests. The corpus system tests can time out in their pre-existing install hook under heavy local load; CI runs them.

🤖 Generated with Claude Code

Juliusolsson05 and others added 2 commits September 26, 2026 19:41
…the main thread (#769)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ure the conversations catalog

#769 asks to move transcript work off the main thread, but the recordings
cannot attribute main stalls to it: each initial load was sampled twice
(half timing only path resolution), and catalog discovery, search and
prompt extraction had no monitor boundary. Measure first.

Refs #769

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Juliusolsson05 Juliusolsson05 added type:perf Performance or resource use class:C6-unbounded Unbounded growth sev:P2 Real bug with a workaround labels Sep 27, 2026
Juliusolsson05 and others added 4 commits September 26, 2026 22:50
…iscovery spans, pin the catalog thresholds

Round-1 review of #1352: 26 operations x 4 outcomes outgrew three hard-coded
100-pair caps; a rejected discovery left its span pending until the sweep
called it a timeout; the new thresholds were untested, and discovery's sat
above the main-stall band.

Refs #769

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…talog thresholds

#1352 verification b: restoring the store's 100-entry persistence slice
and raising each catalog threshold by 1 ms both survived. The store now
has an all-pairs operations.json test, and the threshold cases sample
exactly at the threshold.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Juliusolsson05

Copy link
Copy Markdown
Owner Author

Review disposition (head cf977d45). Round 1: a and b FIX-BEFORE-MERGE; c (pi) MERGE-READY with 4 minors. Verification: a MERGE-READY (round 2). b FIX-BEFORE-MERGE in round 2 (two new test gaps), then MERGE-READY in round 3. The reasoning is also in docs/plans/2026-09-26-monitor-transcript-operations.md.

Fixed

  • a1: a discovery that rejects left its span open. It now closes with fail(error). The earlier claim that discovery "cannot fail" was wrong, because a malformed cwd rejects.
  • a2: 26 operations × 4 outcomes = 104 pairs, but three places capped them at 100. One derived MAX_MONITOR_OPERATION_PAIRS now serves the aggregator, the parser and the store.
  • b, round 2: that cap in the history store was unpinned. An all-pairs operations.json test now fails if the store goes back to 100.
  • a3, b1, c2: the thresholds were untested. They are now sampled exactly AT the threshold (b round 2: +1 ms survived) and just below.
  • c1: discovery's 2000 ms threshold sat above main-stall's 1000 ms band, so a 1.5 s discovery stall named nothing. Discover and search are now 1000 ms.
  • b2: the extraction assertion now requires at least one prompt.
  • c3: the load test now proves it times the read, not the lookup.

Declined

  • b3 / c5: the corpus-install beforeAll times out under heavy local load. The hook predates this PR, CI runs it, and timeouts are never widened. It passed in 5.2 s in b's round-2 run.
  • c4: all main-scope slow operations share one cooldown slot. That is pre-existing semantics, outside this PR.
  • Threshold calibration against field data: no recorded run contains the new operations yet, which is the point of measuring first (Refs perf(sessions): run session index and history loads off the main thread #769).

🤖 Generated with Claude Code

@Juliusolsson05
Juliusolsson05 merged commit 0f6b798 into main Sep 27, 2026
2 checks passed
Juliusolsson05 added a commit that referenced this pull request Sep 27, 2026
@Juliusolsson05
Juliusolsson05 deleted the fix/monitor-transcript-operations branch September 27, 2026 19:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

class:C6-unbounded Unbounded growth sev:P2 Real bug with a workaround type:perf Performance or resource use

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant