DNM: feat(coderd/database): add search parameter to GetChats - #27009
Conversation
e11a3a8 to
f367661
Compare
de5d61f to
d06ddf4
Compare
|
/coder-agents-review |
|
Chat: Review posted | View chat Review historydeep-review v0.9.0 | Round 4 | Last posted: Round 4, 15 findings (3 P3, 9 Nit, 3 Note), APPROVE. Review Finding inventoryFindings
Round logRound 1Panel (16 reviewers). 1 P3, 2 Nit new. 3 Notes. 2 dropped. Reviewed against f367661..d06ddf4. Round 2Panel (10 reviewers). CRF-1 through CRF-5 addressed, CRF-6 accepted. 1 P3, 3 Nit new. Reviewed against 554ef39..2a4e560. Round 3Panel (10 reviewers). CRF-9 through CRF-12 addressed. 1 P3, 4 Nit new. Reviewed against 01a47d9..4904714. Round 4Panel (10 reviewers). CRF-13 through CRF-17 addressed. No new findings. Convergence. Reviewed against 01a47d9..7b927dd. About deep-reviewCRF = Coder Review Finding (P0-P4, Nit, Note)
|
d06ddf4 to
98a120b
Compare
7381fb1 to
2d5bcc9
Compare
98a120b to
71399d8
Compare
2d5bcc9 to
c6a884b
Compare
71399d8 to
f17ee99
Compare
c6a884b to
7ee0e38
Compare
f17ee99 to
30d3b97
Compare
7ee0e38 to
554ef39
Compare
30d3b97 to
2a4e560
Compare
|
/coder-agents-review |
There was a problem hiding this comment.
All R1 findings addressed. CRF-1 comment rewritten with accurate websearch_to_tsquery docs. CRF-5's AND short-circuit replaced with CASE/WHEN per the SQL standard. CRF-2/3/4 comment cleanup applied. CRF-6 accepted with docs deferred to CODAGT-727.
1 P3, 3 Nit. No blockers.
"The forced
search_tsvupdate ontoolMsgandmodelMsgis the real test. Without the manual UPDATE the test would pass by accident." (Bisky)
Process note: the fix commit subject ("refactor(coderd/database): address review on GetChats search") names the process rather than the change. Something like "guard bigint cast with CASE in GetChats search" would tell a future git blame reader what changed.
🤖 This review was automatically generated with Coder Agents.
| -- PR number. websearch_to_tsquery accepts quoted phrases, OR, and | ||
| -- -negation; the 'simple' config folds case and skips stemming. | ||
| AND CASE | ||
| WHEN @search::text != '' THEN ( |
There was a problem hiding this comment.
P3 [CRF-9] Whitespace-only search returns zero rows; empty search returns all rows.
"" hits ELSE true and returns everything. " " passes != '', enters the THEN branch, and websearch_to_tsquery('simple', ' ') produces an empty tsquery that matches nothing. All four OR arms fail, zero rows returned.
| Input | Rows |
|---|---|
"" |
all |
" " |
0 |
"-" |
0 |
"OR" |
0 |
"
''vs' 'is a sharp discontinuity at the SQL layer. Every future caller inherits it." (Hisoka)
Fix: BTRIM(@search) != '' or LENGTH(BTRIM(@search)) > 0. The - and OR cases are arguably correct (operators without operands), but whitespace producing zero rows when empty produces all rows is a trap.
(Hisoka P3)
🤖
There was a problem hiding this comment.
issue(fixed): Guard is now btrim(@search::text) != '', so whitespace-only search returns all rows, same as empty. Pinned by WhitespaceSearch/ReturnsAll alongside the existing EmptySearch cases. Punctuation-only inputs ("OR", "-") still return 0 rows here by FTS semantics; the API layer rejects them with 400 via PR #27093's preflight. Fixed in f14b054.
🤖 Reply posted by Coder Agents on behalf of @johnstcn.
| ) | ||
| ELSE true | ||
| END | ||
| -- Free-text search across chat title, PR title, message bodies, and |
There was a problem hiding this comment.
Nit [CRF-10] The first sentence ("Free-text search across chat title, PR title, message bodies, and PR number") enumerates what the four OR arms below already show. Same pattern at line 689 ("Rows pending backfill (search_tsv IS NULL) match nothing" restates the IS NOT NULL predicate) and line 702 ("The length cap prevents overflow" restates the regex). Each comment has a useful trap sentence; the trailing restatement could be trimmed.
(Gon)
🤖
| }) | ||
| require.NoError(t, err) | ||
|
|
||
| // --- helpers --- |
There was a problem hiding this comment.
Nit [CRF-11] The --- helpers --- and --- fixtures --- (line 15356) separators are navigational aids in a long test function, but carry no invariant, trap, or contract. The helper names (createRoot, createChild, insertMsg, linkPR) and the subsequent variable declarations are self-documenting.
(Gon)
🤖
| toolMsg := insertMsg(ineligibleChat.ID, database.ChatMessageRoleTool, database.ChatMessageVisibilityBoth, "forbidden secret token") | ||
| modelMsg := insertMsg(ineligibleChat.ID, database.ChatMessageRoleUser, database.ChatMessageVisibilityModel, "forbidden secret token") | ||
|
|
||
| // Backfill search_tsv through the real pipeline. The backfill indexes |
There was a problem hiding this comment.
Nit [CRF-12] The leading clauses at lines 15382 ("Backfill search_tsv through the real pipeline") and 15387 ("Deleting after backfill removes the row from search results") narrate the test setup sequence. The trap sentences that follow are the real value. Trimming to:
// Ineligible rows keep search_tsv NULL after backfill.// Soft-deleted rows stay excluded even though search_tsv remains populated.keeps the invariant without the sequence narration.
(Gon)
🤖
f14b054 to
4904714
Compare
|
/coder-agents-review All prior findings addressed and replied to inline. Requesting a convergence pass. 🤖 Requested via Coder Agents on behalf of @johnstcn. |
There was a problem hiding this comment.
All R2 findings addressed. CRF-9 whitespace guard landed with btrim. CRF-10/11/12 comment cleanup applied.
1 P3, 4 Nit. No blockers.
"If someone narrowed the SQL to
cm.role = 'user'orcm.visibility = 'both', every test in this suite would still pass." (Bisky)
🤖 This review was automatically generated with Coder Agents.
| linkPR(mergedChat.ID, "https://github.com/acme/other-repo/pull/7", "merged", "Fix authentication flow", 7, "https://github.com/acme/other-repo.git") | ||
|
|
||
| msgChat := createRoot("plain one") | ||
| insertMsg(msgChat.ID, database.ChatMessageRoleUser, database.ChatMessageVisibilityBoth, "kubernetes cluster restart") |
There was a problem hiding this comment.
P3 [CRF-13] The message search positive path covers only one of four eligible (role, visibility) pairs.
The SQL accepts cm.role IN ('user', 'assistant') AND cm.visibility IN ('user', 'both'), giving four eligible combinations. Every positive message match on a root chat uses (user, both):
msgChatat line 15365:ChatMessageRoleUser,ChatMessageVisibilityBoth
The assistant role only appears on childChat (line 15372), where the assertion tests child exclusion, not role eligibility. The user visibility (distinct from both) also only appears there.
"If someone narrowed the SQL to
cm.role = 'user'orcm.visibility = 'both', every test in this suite would still pass. The exclusion tests prove thattoolrole andmodelvisibility are rejected, but they don't prove thatassistantrole oruservisibility are accepted." (Bisky)
Fix: add one message with (assistant, both) and one with (user, user) on root chats, each with a distinct search term, and add table cases asserting they match.
(Bisky P3)
🤖
There was a problem hiding this comment.
issue(fixed): Added root-chat fixtures for the three missing eligible pairs: (assistant, both), (user, user), and (assistant, user), each with a distinct term and a positive table case. Narrowing role or visibility in the SQL now fails the suite. Fixed in 7b927dd.
🤖 Reply posted by Coder Agents on behalf of @johnstcn.
| END | ||
| -- websearch_to_tsquery accepts quoted phrases, OR, and -negation; | ||
| -- the 'simple' config folds case and skips stemming. btrim makes | ||
| -- whitespace-only search behave like empty. |
There was a problem hiding this comment.
Nit [CRF-14] "btrim makes whitespace-only search behave like empty" restates the btrim(@search::text) != '' guard on the next line. Additionally, PostgreSQL btrim(text) strips ASCII space (U+0020) only, not tabs or newlines, so "whitespace-only" overstates the scope (practical impact is zero, but the comment is imprecise).
Dropping the sentence resolves both issues:
-- websearch_to_tsquery accepts quoted phrases, OR, and -negation;
-- the 'simple' config folds case and skips stemming.(Gon P2, Leorio Note)
🤖
There was a problem hiding this comment.
| pendingChat := createRoot("plain four") | ||
| insertMsg(pendingChat.ID, database.ChatMessageRoleUser, database.ChatMessageVisibilityBoth, "elasticsearch indexing") | ||
|
|
||
| // Force search_tsv onto ineligible rows to prove the role and |
There was a problem hiding this comment.
Nit [CRF-15] "Force search_tsv onto ineligible rows" restates the SQL UPDATE that follows. The why-not-what is the second half. Trimming to:
// Prove role/visibility predicates exclude rows even when search_tsv is set.(Gon)
🤖
|
|
||
| ineligibleChat := createRoot("plain three") | ||
| toolMsg := insertMsg(ineligibleChat.ID, database.ChatMessageRoleTool, database.ChatMessageVisibilityBoth, "forbidden secret token") | ||
| modelMsg := insertMsg(ineligibleChat.ID, database.ChatMessageRoleUser, database.ChatMessageVisibilityModel, "forbidden secret token") |
There was a problem hiding this comment.
Nit [CRF-16] toolMsg is named by role (tool) but modelMsg is named by visibility (model-only). A reader scanning the pair assumes symmetric naming. modelOnlyMsg or modelVisMsg would surface the visibility dimension.
(Gon)
🤖
| {"Message/DeletedNoMatch", database.GetChatsParams{Search: "terraform"}, nil}, | ||
| // Parent also excluded: EXISTS is per-chat, not per-tree. | ||
| {"Message/ChildNotSurfaced", database.GetChatsParams{Search: "orchestrator saga"}, nil}, | ||
| {"Message/IneligibleRolesNoMatch", database.GetChatsParams{Search: "forbidden secret"}, nil}, |
There was a problem hiding this comment.
Nit [CRF-17] IneligibleRolesNoMatch says "Roles" but the test covers both role exclusion (toolMsg) and visibility exclusion (modelMsg). IneligibleMessagesNoMatch would be more accurate.
(Gon)
🤖
There was a problem hiding this comment.
All 17 findings resolved across 4 rounds. The PR is clean.
R1 caught comment inaccuracies (websearch_to_tsquery semantics, case-insensitivity attribution) and a SQL-standard compliance gap (AND short-circuit for bigint cast). R2 found the whitespace/empty search discontinuity. R3 closed the (role, visibility) test coverage gap and tightened comments. R4 confirmed convergence: 10 reviewers, zero new findings.
The author went beyond fixes on two occasions: CRF-5 was treated as correctness rather than style (CASE for guaranteed evaluation order), and CRF-14's comment drop led to widening btrim to strip tabs/newlines with a pinning test.
Process note (repeated from R2): the commit eb282b97b9 subject "address review" names the process, not the change. Future git blame readers benefit from subjects that describe what changed.
"These comments explain what would break if you changed the code without understanding it. That is what comments are for." (Leorio)
🤖 This review was automatically generated with Coder Agents.
Adds a
searchparameter toGetChats. A chat matches when the search hits its title (FTS), its PR title (FTS), a message body (FTS over the backfilledchat_messages.search_tsv), or its exact PR number when the search is all digits. Empty search is a no-op and composes with the existingarchived,repo, andpr_statusfilters. A^[0-9]{1,18}$guard keeps the PR-number cast from erroring or overflowing on oversized digit strings.Stacked on #26995, which stacks on #26968; those add the schema migration and the
BackfillChatMessagesSearchTsvquery.Refs https://linear.app/codercom/issue/CODAGT-724
EXPLAIN smoke check (20k seeded chats, messages, and diff statuses). The partial GIN indexes serve the message and PR-title arms:
Cross-table OR arms force a sequential scan of
chatsfor the title arm, butidx_chats_title_ftsserves the title predicate itself:🤖 This PR was generated by Coder Agents on behalf of @johnstcn.