Context
Follow-up from PR #952. _persist_policy_block_if_needed in src/gaia/ui/_chat_helpers.py updates a persisted message via non-atomic delete_message → add_message. The author acknowledged this with a comment: "ChatDatabase is single-writer SQLite today; rtk make this replacement transactional before moving the chat DB to a multi-writer backend." This pattern appears twice in the file.
Risk
Between DELETE and INSERT a crash, exception, or future multi-writer access can leave the session without the policy-block message — silent data loss. With SQLite WAL mode enabled the window is narrow but non-zero.
Fix
Add ChatDatabase.upsert_message(session_id, msg_id, content, ...) backed by a single INSERT OR REPLACE or BEGIN IMMEDIATE transaction. Replace both call sites in _chat_helpers.py:
# Before
db.delete_message(request.session_id, persisted_policy_block_msg_id)
persisted_policy_block_msg_id = db.add_message(...)
# After
persisted_policy_block_msg_id = db.upsert_message(
request.session_id, persisted_policy_block_msg_id, ...
)
Files
src/gaia/ui/chat_database.py — add upsert_message
src/gaia/ui/_chat_helpers.py — replace both delete+add call sites
tests/unit/chat/ — add concurrent-write and crash-recovery tests
Context
Follow-up from PR #952.
_persist_policy_block_if_neededinsrc/gaia/ui/_chat_helpers.pyupdates a persisted message via non-atomicdelete_message→add_message. The author acknowledged this with a comment: "ChatDatabase is single-writer SQLite today; rtk make this replacement transactional before moving the chat DB to a multi-writer backend." This pattern appears twice in the file.Risk
Between
DELETEandINSERTa crash, exception, or future multi-writer access can leave the session without the policy-block message — silent data loss. With SQLite WAL mode enabled the window is narrow but non-zero.Fix
Add
ChatDatabase.upsert_message(session_id, msg_id, content, ...)backed by a singleINSERT OR REPLACEorBEGIN IMMEDIATEtransaction. Replace both call sites in_chat_helpers.py:Files
src/gaia/ui/chat_database.py— addupsert_messagesrc/gaia/ui/_chat_helpers.py— replace both delete+add call sitestests/unit/chat/— add concurrent-write and crash-recovery tests