Repository navigation
Observer reuses a window's decoded claims while its rows are unchanged - #4777
Merged
Merged
Conversation
On every #comms message the observer re-decoded all open threads' notification windows, even the N-1 whose evidence did not move. Decoding the four-table claim join (FieldCodec) is ~a third of the observer child's per-ping CPU (observer child 6.6-7.5% per ping, over the 5% target). The decode is a pure function of the raw join rows. capture() now hashes those raw rows and reuses the decoded claims when the hash is unchanged for the same (root, selection), decoding only the window(s) whose rows actually moved. The hash covers the whole join -- claim, triage context, queue pointer and published response -- so any change re-decodes; consumption, bus responses and stored inputs are never cached and are read fresh every pass, so nothing displayed can go stale. The cache is bounded to the most recent selections (a sliding window makes a new selection each move). root is optional: callers outside the observer loop decode fresh. This supersedes PR #4776's batch (it batched reads but not the decode, so it did not move the number; removed prime/read_batch/sliced). Notification + observation suite: 112 passed, same 3 pre-existing main reds as the baseline, zero added. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XbFMSnLEc51gw21bLFFRK1
Contributor
Author
|
Installed (core-446470384) and measured, 6 throwaway-Toad runs, observer child on ping: 3.7, 4.8, 5.0, 5.2, 5.9, 6.2% (median ~5.1%), idle ~1.6%. Was 6.6-7.5% before. The cache moved it ~30% and removed the scaling with open-thread count (now the observer decodes only the one window a ping changes, not all N). It sits at the 5% line, not comfortably under: the per-ping floor is the single changed window's decode, which is inherent to showing the new message. Going lower means decoding only the changed rows within that window - diminishing returns. Clean landing: notification+observation suite had zero added failures over main's baseline of 3. |
This was referenced Nov 15, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
On every #comms message the observer re-decoded all open threads' notification windows, even the N−1 whose evidence didn't move. Decoding the four-table claim join (FieldCodec) is ~a third of the observer child's per-ping CPU — measured observer child 6.6–7.5% per ping, over the 5% target, scaling with open threads.
Fix
The decode is a pure function of the raw join rows.
capture()now hashes the raw rows and reuses the decoded claims when the hash is unchanged for the same(root, selection), decoding only the window(s) whose rows actually moved. The hash covers the whole join — claim, triage context, queue pointer, published response — so any change re-decodes. Consumption, bus responses and stored inputs are never cached and are read fresh every pass, so nothing displayed can go stale. Cache is bounded to the most recent selections (a sliding window makes a new selection value each move);rootis optional so callers outside the observer loop decode fresh.Supersedes #4776's batch (it batched reads but not the decode, so it didn't move the number): removes
prime/read_batch/sliced.Correctness
The reuse is gated by a byte-exact hash of the whole raw join, so the decoded claims are only reused when the rows are identical — a pure-function cache. Every other evidence source is read fresh.
Tests
Notification + observation suite: 112 passed. The 3 failures (
test_named_target_contracts,test_outbound_transcript_source::...read_contention...,test_recipient_activity_index::...archived_duplicate_birth) fail identically onorigin/main— pre-existing, zero added by this change (verified by running the same set on main: 3 failed there too).🤖 Generated with Claude Code
https://claude.ai/code/session_01XbFMSnLEc51gw21bLFFRK1