Skip to content

fix: preserve sender-aware self echoes in search - #126

Merged
nixfred merged 1 commit into
nixfred:mainfrom
GreyforgeLabs:fix/search-self-echo
Oct 2, 2026
Merged

nixfred merged 1 commit into
nixfred:mainfrom
GreyforgeLabs:fix/search-self-echo

Conversation

@GreyforgeLabs

Copy link
Copy Markdown
Contributor

Searching a group conversation can hide a real reply: an outgoing "yes" and another member's "yes" in the same second collapse to one hit because search's echo key omits the sender. Search also keeps whichever self-echo row arrives first, so your own message can appear as incoming.

Use the existing dedupeSelfEcho() shared by the collector and thread loader before search discards empty bodies. This preserves distinct group senders, makes self-echo direction independent of row order, and lets an empty outgoing row identify its decoded incoming twin. Ranking and limits stay in the search layer; message text stays in memory and travels to the bridge on stdin.

Validation:

  • All four new regressions fail against unchanged upstream and pass with the fix, including the injected-runner search path.
  • bun test: 676 pass, zero failures.
  • Repository's offscreen QML badge check, OTP adapter, Mac Python checks, shell syntax checks, and ShellCheck all pass locally.
  • Synthetic data only; no real conversations read or messages sent. No QML changes.

Reuse the thread loader's echo deduplication before shaping search hits.
This preserves distinct group senders and attributes self echoes to the
user regardless of bridge row order, including empty outgoing rows.
Message content still stays in memory and travels to the bridge on stdin.
@GreyforgeLabs
GreyforgeLabs requested a review from nixfred as a code owner October 2, 2026 06:55
nixfred added a commit that referenced this pull request Oct 2, 2026
WHAT: shapeResults hands outgoing rows to dedupeSelfEcho first. One
regression test in search.test.ts: two incoming copies, the sent row,
its echo, in the newest-first order the bridge answers, must be one hit
from me.
WHY: a self-thread message that Messages in iCloud re-synced sits in
chat.db as one sent row plus 3 or 4 incoming copies with the same
second and text. `imsg search` answers newest first, and dedupeSelfEcho
folds a copy only into a row of the OPPOSITE direction, so two incoming
copies ahead of the sent row left one behind as an incoming hit. The
same four rows oldest first fold to one (checked both tie orders).
Found while checking #126 against live rows; #126 itself is correct.
MEASURED on the live Mac, 400 rows per query, query "test": 340 hits on
old main, 286 with #126, 196 with this; self-thread hits shown as
incoming 96 -> 6. "yes" 11 -> 1, "ok" 3 -> 0, "thanks" 5 -> 1. Hits in
every other conversation identical (372, 388, 130, 389).
VERIFIED: the new test fails on #126 alone and passes here.
NOT CHANGED: dedupeSelfEcho. The collector and the thread loader keep
their exact behaviour; only search's input order moves.

LR-T: blip, search, self-thread, dedupeSelfEcho, icloud-resync, duplicate-hits
LR-D: blip
LR-R: #126

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011cTWkcA3ontxAAbXqmkfKE
@nixfred
nixfred merged commit 584aa07 into nixfred:main Oct 2, 2026
1 check passed
@nixfred

nixfred commented Oct 2, 2026

Copy link
Copy Markdown
Owner

Merged in 584aa07, thank you, and welcome to Blip. This was a good catch: search kept its own echo key with no sender in it, so a "yes" from me and a "yes" from someone else in the same second quietly became one hit. Reusing dedupeSelfEcho() instead of patching the key is the right fix, since search now follows the same rules as the conversation view and cannot drift from it again.

What I checked before merging:

  • All four of your new tests fail against the old search.ts and pass with the change, as you said. Full suite green.
  • Ran old and new shaping side by side on live rows from the Mac (four queries, 400 rows each, comparing metadata only). Every difference was in the self-thread. Hits in every other conversation were identical.

That comparison turned up one more thing, which is not a fault in your PR. My self-thread holds 3 or 4 incoming copies of each note (same second, same text, apparently iCloud re-sync). imsg search answers newest first, and dedupeSelfEcho folds a copy only into a row of the opposite direction, so two incoming copies ahead of the sent row left one behind as an incoming hit. fc2ddce applies your idea one step further: search hands outgoing rows to the dedupe first. For the query "test" here that is 340 hits on old main, 286 with your change, 196 with both, and the hits outside the self-thread do not move.

One small thing: I thank contributors by name in a weekly post on X. Your GitHub profile lists @GreyforgeLabs, so I will tag that unless you would rather not be tagged, which is an equally good answer.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants