fix(server): search every settled message, not just each turn's last one - #417
Merged
Merged
Conversation
Co-authored-by: maria <maria@kuuro.net>
pingdotgg#7132) Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…#6392) Co-authored-by: Shivam Sharma <91240327+shivamhwp@users.noreply.github.com>
Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com>
…ingdotgg#7082) Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
…g#7083) Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
…in GitHubPullRequestCli (pingdotgg#7385) Signed-off-by: aoright <102943475+aoright@users.noreply.github.com>
Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com>
…ngdotgg#7477) Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…tgg#6286) Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: GPT-5.6 <noreply@openai.com>
Co-authored-by: GPT-5.6 <noreply@openai.com>
Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: t3-code[bot] <269035359+t3-code[bot]@users.noreply.github.com>
Merges 66 upstream commits into fork/dev. Notable weld points: - ChatComposer: upstream pingdotgg#7150 relocated the prompt editor, length validation and footer, leaving the fork's copies as a second live layout. Ported the fork-only pieces into upstream's copies first — the environment-unavailable placeholder, the model picker's usage snapshot, and the attach-images button that drives the hidden iOS file input — then dropped the stale layout. - MessagesTimeline: upstream's new active-turn block indexes whatever it iterates; the fork walks its collapsed/interleaved list, so the block was rebased onto that list to keep the indices referring to one array. - DesktopUpdates: upstream's action reservation and widened installable check, kept compatible with the fork's Linux dir-install mode. - CommandPalette: dropped the `open` prop upstream removed while keeping the fork's file-picker / content-search overlays, and took upstream's newer reduceCommandPaletteUiState over the fork's relocated copy of the old one. - mobile-showcase-screenshots: GitHub-hosted runners (this fork has no Blacksmith) with upstream's palette-driven timeout. - ProviderRegistry.test: kept the fork's coverage for mergeProviderSnapshots / selectProvidersByKind, which upstream deleted while leaving both exported. Upstream's new t3code/no-native-title-tooltip rule flagged seven fork-only components; four had a title duplicating an existing aria-label, three needed the label moved off the native tooltip. rerere was disabled for this merge: it had replayed earlier resolutions onto new upstream content, which is exactly how a weld goes silently wrong. Co-authored-by: Patrick Roza <42661+patroza@users.noreply.github.com>
Thread search restricted assistant matches to the one row a turn names as its
terminal message:
messages.message_id IN (SELECT turns.assistant_message_id FROM projection_turns ...)
Providers stream an answer as many assistant items — roughly ten per turn now,
and climbing as chunking gets finer — and only the last is linked as
turns.assistant_message_id. Everything said before it was invisible to search,
which is why full-text search "stopped working" without anything visibly
changing.
Measured on a live state.sqlite: 7,118 of 20,094 settled messages were
searchable (35%); for assistant messages alone it was 3,430 of 16,406 (21%).
Every thread still had *some* searchable text, so the symptom was a search that
quietly missed most of what was said rather than one that missed whole threads.
A real query for "worktree" goes from 138 to 187 threads.
Tool calls were never involved: they are activities in
projection_thread_activities, not rows in projection_thread_messages, so they
stay out by construction. 'system' notices are now excluded explicitly rather
than incidentally.
Per-thread dedupe is unchanged — thread_match_rank = 1 still collapses a thread
to a single best row, preferring a user match, then the most recent.
The existing test asserted the old behaviour outright ("Interim needle must not
be searchable"), so it is inverted here, with a split-answer case added whose
matching half is not the turn's assistant_message_id. Both fail if the old
restriction comes back.
Co-authored-by: Patrick Roza <42661+patroza@users.noreply.github.com>
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.
Thread search restricted assistant matches to the single row a turn names as its
terminal message:
Providers stream an answer as many assistant items — roughly ten per turn now,
and climbing as chunking gets finer — and only the last is linked as
turns.assistant_message_id. Everything said before it was invisible to search.That is why full-text search appeared to "stop working" without anything
visibly changing: the query never changed, the shape of the data under it did.
Measured on a live
state.sqliteA real query for
worktreegoes from 138 to 187 threads.All 425 threads with assistant text had some searchable row, so the symptom
was a search quietly missing most of what was said, not one missing whole
threads — which is exactly why it was hard to pin down.
Scope
projection_thread_activities, not rows inprojection_thread_messages, sothey stay out by construction.
systemnotices are now excluded explicitly rather than incidentally.thread_match_rank = 1still collapses athread to one best row, preferring a user match, then the most recent.
is_streaming = 0.Known limit
A phrase that straddles a split boundary still will not match — each message row
is matched independently, so "the quick brown" in one item and "fox" in the next
is two rows, not one string. Fixing that means matching against a per-turn
concatenation, which is a bigger change with its own cost; happy to do it as a
follow-up if it matters in practice.
Tests
The existing test asserted the old behaviour outright — its fixture literally
read
'Interim needle must not be searchable.'— so that expectation isinverted here, and a split-answer case is added whose matching half is not the
turn's
assistant_message_id. Mutation-checked: restoring the old restrictionfails both new assertions.
Co-authored-by: Patrick Roza 42661+patroza@users.noreply.github.com