Conversation
…uilds to preserve prefix cache
tonydwb
approved these changes
Jun 17, 2026
tonydwb
left a comment
There was a problem hiding this comment.
Code Review Summary
Verdict: Approved
Good fix for a race condition in system prompt restoration. Uses per-session locks to prevent concurrent rebuilds that could cause duplicate on_session_start hook invocations. Includes a comprehensive test using threading.Barrier to verify concurrent threads only trigger the hook once.
Changes
- agent/conversation_loop.py: Added _system_prompt_locks dict and _get_session_prompt_lock helper, wrapped _restore_or_build_system_prompt body with lock, added double-checked locking pattern
- tests/run_agent/test_compression_persistence.py: Added test_system_prompt_restore_guards_against_concurrent_rebuild
Security
- No security concerns
Quality
- Well-scoped fix addressing a specific concurrency issue
- Proper double-checked locking pattern
- Good test coverage with threading
Reviewed by Hermes Agent
Collaborator
|
Thanks for the focused concurrency investigation. Automated hermes-sweeper review found that current
Closing as implemented on main. |
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.
Summary
Guards
_restore_or_build_system_prompt()against concurrent rebuildsthat cause duplicate
on_session_starthook invocations and unnecessaryprefix-cache misses. On the gateway path, a fresh
AIAgentisconstructed per turn, meaning two near-simultaneous incoming messages to
the same session can race through the restore function and both trigger a
full system-prompt rebuild.
Root cause
_restore_or_build_system_prompt()inagent/conversation_loop.pyhadno synchronization around the read-check-build-persist sequence. On the
gateway path, where
AIAgentis reconstructed on every turn and readsthe cached prompt from the session DB, two concurrent turns for the same
session_idcould both observestored_state = "missing"before eitherhad written the built prompt back. Both would then:
_build_system_prompt()independentlyon_session_starthook twiceseed_credits_at_session_starttwiceThe duplicate rebuild wastes the prefix cache: Anthropic caches the
system prompt prefix only when the exact same bytes are sent on
consecutive turns. Two independent builds may produce byte-identical
output, but the redundant work still adds latency and risks divergence
if any non-deterministic component (timestamps, random seeds) is
involved.
Behavioral change
Before: two concurrent turns for the same session could both rebuild the
system prompt, fire session-start hooks twice, and seed credits twice.
After: a per-session
threading.Lockserializes the restore path. Thesecond thread to acquire the lock checks whether
_cached_system_promptwas already set by the first thread and returns early without rebuilding.
Session-start hooks and credits seeding fire exactly once per session.
What changed
agent/conversation_loop.pyAdded module-level
_system_prompt_locksregistry and_get_session_prompt_lock()helper that returns a per-sessionthreading.Lock. The entire body of_restore_or_build_system_prompt()is wrapped in
with _get_session_prompt_lock(agent.session_id):. Adouble-checked locking guard (
if agent._cached_system_prompt is not None: return) is added before the rebuild path so the second threadexits immediately after the first has populated the cache.
tests/run_agent/test_compression_persistence.pyAdded
test_system_prompt_restore_guards_against_concurrent_rebuild:two threads race through
_restore_or_build_system_prompton the samesession using a
threading.Barrierto maximize overlap, then assertthat
on_session_startwas invoked exactly once.What is NOT changed
_stored_prompt_matches_runtimelogic unchangedadds no contention