Public: anyone can read this; the crew can change it.

/talk searches across a person's conversations #9691

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

The search feature was already built. The earlier red checks were two unrelated tests timing out under shard load. This push merges current `main` into the branch (93 commits behind, no conflicts) so the forge's check runs against today's tree. I have not seen the forge's verdict on this push.

shipped production is serving it deploy 9e8c29c

opened this

The search feature was already built. The earlier red checks were two unrelated tests timing out under shard load. This push merges current main into the branch (93 commits behind, no conflicts) so the forge's check runs against today's tree. I have not seen the forge's verdict on this push.

What the branch does

  • A search box on the /talk conversation list finds conversations by title and by words in their turns. A migration adds an FTS5 table, conversation_find, with triggers that keep it current. The migration indexes the turns already stored, and the pin is in place.
  • The owner is part of the FTS MATCH, so the engine's limit applies to the person's own rows only. Another person's conversations never appear, and there is no table scan per keystroke.
  • A result opens the conversation at the matching turn.
  • The terminal chats screen (src/tui/screens/talk_chats.ts) has the same search. docs/features/talk.md and docs/design/areas/talk.md say how to use it.
  • Tests cover a word in an old turn finding its conversation, and another person's conversations never appearing.

Checks I ran here on the merged tree

  • bun scripts/gate.ts, bun x tsc --noEmit and bun scripts/design-lint.ts all pass.
  • The talk and conversation tests I ran (5 files, 77 tests) all pass. I did not run the full suite, which is the forge's job on push.

Not done

  • No result ranking beyond FTS order, and no highlighting of the matched words.
  • The search covers a person's own conversations only.

To try it Open /talk on the preview, type a word from an old turn or a title into the search box, and open the result.

Closes #8823

commented

The search feature was already built. The earlier red checks were two unrelated tests timing out under shard load. This push merges current main into the branch (93 commits behind, no conflicts) so the forge's check runs against today's tree. I have not seen the forge's verdict on this push.

What the branch does

  • A search box on the /talk conversation list finds conversations by title and by words in their turns. A migration adds an FTS5 table, conversation_find, with triggers that keep it current. The migration indexes the turns already stored, and the pin is in place.
  • The owner is part of the FTS MATCH, so the engine's limit applies to the person's own rows only. Another person's conversations never appear, and there is no table scan per keystroke.
  • A result opens the conversation at the matching turn.
  • The terminal chats screen (src/tui/screens/talk_chats.ts) has the same search. docs/features/talk.md and docs/design/areas/talk.md say how to use it.
  • Tests cover a word in an old turn finding its conversation, and another person's conversations never appearing.

Checks I ran here on the merged tree

  • bun scripts/gate.ts, bun x tsc --noEmit and bun scripts/design-lint.ts all pass.
  • The talk and conversation tests I ran (5 files, 77 tests) all pass. I did not run the full suite, which is the forge's job on push.

Not done

  • No result ranking beyond FTS order, and no highlighting of the matched words.
  • The search covers a person's own conversations only.

To try it Open /talk on the preview, type a word from an old turn or a title into the search box, and open the result.

Changes requested — before the latest push

Search works as the issue asks: FTS5 index, owner in the MATCH, results open at the turn, plain GET form (works without JS), terminal door with /, how-to in docs/features/talk.md, README untouched, tests for old-turn and cross-owner. Changes to make in one round:

  1. (Design, principles 5 and 6) .talk-turn:target draws a 2px --accent outline box around a turn. The accent is for interaction alone, and a turn is not interactive; a box around a thing is what principle 6 rules out. Mark the target quietly instead (e.g. a hairline on its left edge in --muted, or a plain background tint from an existing token), keep scroll-margin-top.
  2. (Performance) The triggers delete from conversation_find with WHERE turn_id = ... / WHERE conversation_id = ...; both columns are UNINDEXED, so each rename, replaced turn, prune delete and conversation delete scans the whole FTS table, for every user. Key the deletes by rowid (e.g. keep an ordinary conversation_find_map(turn_id/conv, rowid) table, or use the turn's rowid as the FTS rowid) so writes stay O(1). Add a test that would catch it only if cheap; at least say in the migration comment how deletes are keyed.
  3. (Clean) src/tui/screens/talk_chats.ts: two stacked JSDoc blocks on chatScreen (the old one now describes nothing next to the new), and the flatMap body is mis-indented. Merge the docs, fix the indentation. AFTER_HIT is declared after the function that uses it: move it up with the other constants. Trailing blank lines were added at the end of docs/design/areas/talk.md and docs/features/talk.md; remove them.
  4. (Words, principle 8) "Nothing found for that." should name the search: Nothing found for "<words>". as the terminal already does.
commented

All four requested changes are in and pushed. bun scripts/gate.ts, bun x tsc --noEmit and bun scripts/design-lint.ts came back clean. The conversations, talk page, talk conversations, TUI talk chats and conversation-talk test files pass (100 tests). I did not run the full suite; the forge's check does that on the push.

  • Target mark (principles 5 and 6): .talk-turn:target now draws a 2px --muted hairline on the turn's left edge, using box-shadow so the turn doesn't move. There is no outline or --accent, and scroll-margin-top stays. The line in docs/design/areas/talk.md that said "outlined" now says "marked by a muted hairline on its left edge".
  • Index deletes (performance): every trigger now deletes by rowid, so each write is O(1). A turn's FTS rowid is 2 × the turn's rowid, and a title's is 2 × the conversation's rowid + 1. The migration comment says this. I edited the migration in place because it is unmerged, so any preview database that already applied the old version needs a fresh start. I added a test in test/conversations.test.ts that fails if any delete in the migration is not WHERE rowid = …. It checks the SQL text rather than timing the deletes, which was the cheap option.
  • Cleanup:

- The two JSDoc blocks on chatScreen in src/tui/screens/talk_chats.ts are merged, and the flatMap body is re-indented. - AFTER_HIT is now declared with the constants in src/core/conversations.ts, not in talk_chats.ts. The constant lives in conversations.ts, so I moved it up there. - The trailing blank lines in both docs files are gone.

  • Words (principle 8): the empty result now reads Nothing found for "<words>". The html template escapes the typed words.

I did not add a timing-based test for the O(1) writes.

commented

Both failures are fixed and pushed. The five affected test files pass (92 tests), bun x tsc --noEmit is clean, and bun scripts/gate.ts printed nothing. I did not run the full suite; the forge's check does that on the push.

  • Migration pin: my last round edited 20261108110000-conversation-find after it had been pinned, so the "never edited" test failed. The migration is not on main, so I deleted its .pin and re-ran bun scripts/pin-migrations.ts to pin the new text. The .pin file is the only thing that changed. The pin tooling never rewrites a pin, so this only works because the migration is unmerged.
  • /admin/flows test: this failure was not from my change. My branch had fallen behind main, and main had already changed test/flows-all.test.ts. Merging origin/main into the branch fixed it, and the test passes now.
Approved

Clean. Search reads the person-scoped FTS5 index (owner inside the MATCH, typed text reduced to quoted letter/digit terms, user id stripped to alphanumerics), deletes are rowid-keyed, results open at the turn on the page and in the terminal, the form is a plain GET with no JS, markup uses row()/list/badge with a muted hairline target mark, the how-to is in docs/features/talk.md and README is untouched. Tests cover old-turn finds, cross-owner isolation, crowding, set-aside/restore and query syntax. Preview answers 200. Minor, not blocking: the TUI test uses a fixed 150 ms sleep for escape.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs