Repository navigation
fix(chat): give a follow-up the documents of the message before it - #329
Closed
dovvnloading wants to merge 2 commits into
Closed
dovvnloading wants to merge 2 commits into
dovvnloading wants to merge 2 commits into
Conversation
A new turn read the whole transcript to build the model's history, which copied and JSON-decoded every reply's reasoning, sources and stats only to drop them; a fork read and decoded the whole source chat just to check that one message id was in it. DatabaseManager.load_messages(thread_id, columns=...) selects only the named fields (a fixed whitelist, never passed to SQL as given), decodes them the way load_chat does, and returns None for an unknown chat. chat_exists is a SELECT 1. Both repositories expose them as get_messages and chat_exists, covered by the shared contract tests. The new-turn history read now asks for the five fields history is built from, the fork check reads only ids, and rename asks chat_exists. load_chat shares the row decoding so the two reads cannot drift. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
"Summarise this" with a document, then "now list its section headings": only attachment metadata is stored with a message, so the follow-up reached the model with no document text at all, and it invented the answer. When a turn attaches nothing, the documents attached to the user's most recent message are resolved again, through the same owner and integrity checks, and sent marked as coming from the previous message, under their own heading. They go through the attachment budget that scales with the context window like any document. Images stay with their own turn, only the immediately previous message lends documents, and a turn that attaches its own files gets only those. A document deleted or expired since is left out (a count is logged, never a name or text) and the turn proceeds. Nothing is persisted on the follow-up; regenerating its reply brings the same documents back. To have the previous turn's attachments before resolution, the new turn's one partial transcript read moves from prepare() to admission. The user turn is still appended guarded by the admitted revision, so a write landing after that read fails the append as before. A brand-new chat no longer makes a read at all. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Owner
Author
|
Superseded by #331, which combines this with the other pending fixes into one pull request. |
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.
Problem
Root cause
get_chat(everything) andget_chat_overview(no messages).Change
Two commits.
perf(persistence): read only the message fields a caller uses.DatabaseManager.load_messages(thread_id, columns=...)selects only the named fields from a fixed whitelist; a name outside it is aValueErrorand never reaches SQL. It decodes them asload_chatdoes, in one read transaction, and returnsNonefor an unknown chat.chat_existsis aSELECT 1.get_messagesandchat_exists, covered by the shared contract suite (fix(persistence): harden chat and memory storage #319).HISTORY_MESSAGE_FIELDS); the fork check reads ids; rename askschat_exists.load_chatshares the row decoding, so the two reads cannot drift.fix(chat): give a follow-up the documents of the message before it.prepare()to admission, so the previous turn's attachments are known before resolution. The user turn is still appended withexpected_revisionset to the admitted revision. Since fix(persistence): harden chat and memory storage #319 any write moves the revision, so a write landing after the read fails the append, as before.Not included: the third commit of the original branch (a sticky per-thread code contract, BE-27). It touches the code-execution prompt and needs its own review.
Compatibility and rollback
ChatRepositorygains two methods. The only implementations are the SQLite adapter and the in-memory double, both updated.Checks
Windows, Python 3.14 unless stated.
python -m pytest -q tests/test_chat_attachments.py tests/test_prompt_shape.py tests/test_event_loop_hygiene.py tests/test_chat_repository_contract.py tests/test_chat_correctness.py tests/test_generation_harness_controls.pygives 284 passed, 19 subtests.py -3.12) on the four changed test files: 195 passed.test_chat_attachments.pyandtest_prompt_shape.pycopied onto an export oforigin/main(2972c7a) give 10 failed, 78 passed. The 10 are the new follow-up tests, including deleted/expired documents, images not resent, only the previous message, and the budget at two window sizes.test_event_loop_hygiene.pyandtest_chat_repository_contract.pycannot be collected there, because they use the new repository methods.scripts/check.ps1quick tier): all 11 checks passed in 416.5 s (backend 255.7 s, vitest 91.8 s).Security, concurrency, data loss
stale_revision.Limits
Plan item: BE-34 (remainder), BE-02 (remainder)
🤖 Generated with Claude Code