You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
#927's root cause — a long-lived check_same_thread=False connection shared across threads with no serialization — is a codebase-wide pattern, not a task_store one-off. Verified on main: 25 files use check_same_thread=False; only 4 carry any lock (agent_registry, mesh_store, message_context_store, pinky_memory/store). task_store (fixed in #929) was simply the store hot enough to lose the race first.
Unguarded stores to audit: session_store, conversation_store, agent_comms, skill_store, trigger_store, app_store, user_profile_store, research_store, voice_store, activity_store, hooks, dream_runner, plugin_manager, outreach_config, presentation_store, hub_store, bearer_tokens, signer_store, federation/state, imessage (plus re-check the 4 locked ones for lock coverage of ALL access, not just RMW).
Template
#929 established the pattern: _db as a thread-local property (per-thread sqlite3.connect, per-connection pragmas — pragmas are per-connection in SQLite so the old single application was already incomplete — default check_same_thread enforcement restored, caller-local close). Apply per store with judgment, not mechanically: a store whose access is provably single-threaded (document why) may instead get an assert; a store needing cross-call transactions needs the lock approach instead.
Method (batched, not one mega-PR)
Classify each store: call-site thread census first (which daemon threads touch it).
Priority by heat: session_store, conversation_store, agent_comms first — highest concurrent traffic.
Convert in small batches (3-5 stores/PR) with a per-store concurrency hammer in the Make task store SQLite connections thread-local #929 shape (shared-row point-read-heavy — the incident poisoned during a point read).
Each PR through the standard gauntlet.
Acceptance
Zero files with an unguarded shared check_same_thread=False connection; every conversion carries its hammer; the four legacy-locked stores re-verified or converted for consistency.
Related: #927 (incident + forensics), #929 (template fix), #797/#220 (prior art in the same family).
Scope
#927's root cause — a long-lived
check_same_thread=Falseconnection shared across threads with no serialization — is a codebase-wide pattern, not a task_store one-off. Verified on main: 25 files usecheck_same_thread=False; only 4 carry any lock (agent_registry, mesh_store, message_context_store, pinky_memory/store). task_store (fixed in #929) was simply the store hot enough to lose the race first.Unguarded stores to audit: session_store, conversation_store, agent_comms, skill_store, trigger_store, app_store, user_profile_store, research_store, voice_store, activity_store, hooks, dream_runner, plugin_manager, outreach_config, presentation_store, hub_store, bearer_tokens, signer_store, federation/state, imessage (plus re-check the 4 locked ones for lock coverage of ALL access, not just RMW).
Template
#929 established the pattern:
_dbas a thread-local property (per-threadsqlite3.connect, per-connection pragmas — pragmas are per-connection in SQLite so the old single application was already incomplete — defaultcheck_same_threadenforcement restored, caller-local close). Apply per store with judgment, not mechanically: a store whose access is provably single-threaded (document why) may instead get an assert; a store needing cross-call transactions needs the lock approach instead.Method (batched, not one mega-PR)
Acceptance
Zero files with an unguarded shared
check_same_thread=Falseconnection; every conversion carries its hammer; the four legacy-locked stores re-verified or converted for consistency.Related: #927 (incident + forensics), #929 (template fix), #797/#220 (prior art in the same family).
🤖 Opened by Barsik