DNM: feat: wire chat search filter into chats API - #27093
Conversation
Docs preview📖 View docs preview for |
3e6ec5b to
8504a14
Compare
8017226 to
ac63be7
Compare
8504a14 to
fc7c727
Compare
ac63be7 to
cfeeeba
Compare
fc7c727 to
5384a41
Compare
cfeeeba to
744fea9
Compare
5384a41 to
688db9b
Compare
744fea9 to
d50be39
Compare
688db9b to
e740826
Compare
d50be39 to
e9896e7
Compare
|
/coder-agents-review |
|
Chat: Review posted | View chat Review historydeep-review v0.9.0 | Round 2 | Last posted: Round 2, 4 findings (1 P3, 3 Nit), APPROVE. Review Finding inventoryFindings
Round logRound 1Panel: Bisky, Hisoka, Mafu-san, Mafuuu, Pariston, Gon, Leorio, Ging-Go, Chopper, Knuckle, Komugi, Ryosuke, Meruem, Kite, Knov (wildcard). Netero: no findings. 1 P3, 3 Nit, 3 dropped (2 Note, 1 Nit). Reviewed against e740826..e9896e7. Round 2Churn guard: PROCEED (4/4 addressed). Panel: Bisky, Hisoka, Mafu-san, Mafuuu, Pariston, Gon, Leorio, Komugi, Ging-Go, Chopper, Kite, Meruem (wildcard). Netero: no findings, all R1 fixes verified. 1 new finding (CRF-8 P2) dropped as churn. All R1 findings verified fixed. No findings to post. Reviewed against 388bac7..6dc307b. About deep-reviewCRF = Coder Review Finding (P0-P4, Nit, Note)
|
There was a problem hiding this comment.
Clean, well-scoped PR. The preflight validation using the same websearch_to_tsquery('simple', ...) as the actual search eliminates divergence between what the validator accepts and what the query matches. The dbauthz design decision is flagged honestly, the test coverage is proportional (5 cases, 3.8:1 test-to-production ratio), and every handler path is exercised against a real DB.
15 reviewers found 1 P3 and 3 Nits. Severity breakdown: 0 P0, 0 P1, 0 P2, 1 P3, 3 Nit.
"I tried to build a case against this change and couldn't. The problem is correctly understood, the solution is proportional, and the fix is at the right level." *~ Pariston*
🤖 This review was automatically generated with Coder Agents.
| } | ||
|
|
||
| bothMatch := createChat(t, db, firstUser, modelConfig.ID, "kubernetes in coder repo") | ||
| linkRepo(bothMatch.ID, "[email protected]:acme/widget.git") |
There was a problem hiding this comment.
P3 [CRF-1] backfillSearchTsv processes zero rows here because no insertMessage calls precede it. The subtest creates four chats and links repos, but never inserts chat messages. The search matches by title (inline in the query), not by message-body search_tsv. The call is a no-op that misleads readers into thinking message-body search is being tested in this composition scenario.
Either drop the call, or insert a message on one of the chats to also exercise message-body search composed with the repo filter. The latter would strengthen coverage.
(Bisky)
🤖
There was a problem hiding this comment.
| // This evaluates a pure tokenization function and touches no rows, | ||
| // but the only caller is validating a chat list request, so gate it | ||
| // on the same read permission as listing chats. |
There was a problem hiding this comment.
Nit [CRF-2] Three lines of mechanism narration for one line of why-not-what. The code already shows authorizeContext(ctx, policy.ActionRead, rbac.ResourceChat) and q.db.ChatSearchQueryIsEmpty. The useful content is that the permission mirrors the calling context.
Suggested:
// Pure function, no rows. Gates on chat read to match the listing caller.(Gon)
🤖
| -- Reports whether the search text tokenizes to an empty tsquery, e.g. | ||
| -- punctuation-only input like '!!!'. numnode = 0 is the canonical | ||
| -- "tsquery is empty" check. Used to return a validation error instead | ||
| -- of silently matching nothing. |
There was a problem hiding this comment.
Nit [CRF-3] The comment restates numnode(...) = 0 as "numnode = 0 is the canonical 'tsquery is empty' check" when the SQL already says exactly that. This comment propagates to querier.go and queries.sql.go, so trimming it fixes all three copies.
Suggested:
-- Reports whether search text tokenizes to an empty tsquery (e.g. '!!!').
-- Used to reject input that would silently match nothing.(Gon)
🤖
| // Reject search input that tokenizes to an empty tsquery (e.g. | ||
| // punctuation-only text like "!!!"); it would silently match nothing. |
There was a problem hiding this comment.
Nit [CRF-4] The function name ChatSearchQueryIsEmpty and the error message "Search query contains no searchable words" already communicate the intent. The comment's useful content is the consequence ("it would silently match nothing"). "Tokenizes to an empty tsquery" and the parenthetical example are mechanism the reader doesn't need at the call site.
Suggested:
// Reject text that tokenizes to nothing; it would silently match no rows.(Gon)
🤖
7262cc4 to
388bac7
Compare
f11c5cc to
6dc307b
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 four R1 findings verified fixed. CRF-1's fix went beyond removing the dead call: it added genuine coverage proving message-body search composition with repo filters works end to end. CRF-2/3/4 comment trims applied uniformly across all three locations.
12 reviewers, 0 new findings. Netero confirmed all fixes independently. The preflight uses the same websearch_to_tsquery('simple', ...) as the actual search, the dbauthz gate is documented and tested, and the test suite covers happy path, validation, composition, regression, and mutual exclusion.
"The code held up. ♠" *~ Hisoka*
🤖 This review was automatically generated with Coder Agents.
388bac7 to
ed6453d
Compare
6dc307b to
0228edb
Compare
Wires the
search:filter (parsed in #27090, executed in #27009) intoGET /api/experimental/chats. Part of the stack #26968 <- #26995 <- #27009 <- #27090 <- this.The handler now passes
Searchthrough toGetChatsand preflights the search text with a newChatSearchQueryIsEmptyquery (numnode(websearch_to_tsquery(...)) = 0). Input that tokenizes to nothing (e.g.search:"!!!") returns a 400 validation error onsearchinstead of silently matching nothing. The preflight only runs when a search term is present, so non-search requests make no extra DB calls. The swaggerqparameter description now documentssearch:.dbauthz note:
ChatSearchQueryIsEmptyevaluates a pure function and touches no rows; there is no exact precedent for authz on side-effect-free queries, so it assertsResourceChatread to match the calling context (listing chats).Linear: https://linear.app/codercom/issue/CODAGT-725
Manual smoke (httptest server, seeded chat with title "deploy pipeline alpha" and message body "kubernetes cluster restart",
search_tsvbackfilled):q=search:"kubernetes"-> 200, returns the chat (matched via message body)q=search:"!!!"-> 400,Invalid chat search query.with validation onsearchq=repo:coder search:"pipeline"-> 200,[](search matches but repo filter excludes; both applied)Implementation plan
CODAGT-725: Wire
search:into chats APIIssue: https://linear.app/codercom/issue/CODAGT-725
Depends on: parser (#27090) and GetChats param (#27009).
Branch:
cian/codagt-725-wire-search-into-chats-api, stacked on the 723 branch (stack: #26968 <- #26995 <- #27009 <- #27090 <- this).Deliverable: one draft PR. Handler + one validation query; no frontend.
Requirements
listChatsincoderd/exp_chats.go:searchquery.Chatsalready returnsGetChatsParamsincludingSearch(723); confirm the handler passes the parsed params through unmodified (it likely already does; the real work is validation). No readiness/503 logic.params.Search != "":New sqlc query, e.g. in
queries/chats.sql:numnode = 0is the canonical "tsquery is empty" check; punctuation-only input like!!!tokenizes to nothing.If empty:
400with acodersdk.ValidationErroron fieldsearch, message like "Search query contains no searchable words." (match the file's existing validation error phrasing).dbauthz for the new method: it evaluates a pure function, touches no rows. Find the nearest precedent for authz on side-effect-free queries and mirror it; if none fits, assert
ResourceChatread (the caller is listing chats anyway). Flag the choice in the PR.searchfilter in theqparameter description.make gento regenerate API docs; no new endpoint, no codersdk shape change.Red: tests first
Handler tests in
coderd/exp_chats_test.go(find the existing listChats tests and mirror fixtures; populatesearch_tsvby executing the 722 backfill query against the test DB store):q=search:"<term>"returns chats matching by message body; a chat matching by title also returned; non-matching chat absent.q=search:"!!!"-> 400, validation error onsearch, message matches.q=repo:x search:"term"filters both;archiveddefault behavior at the API layer unchanged (handler-level default pinned here, per the 724 note that NULL-vs-false lives in the handler).search:-> existing behavior unchanged (existing tests remain green; add nothing unless a gap shows).search:a title:b) - confirm the existing searchquery-error path already covers this; add one case asserting the mutual-exclusion error reaches the client.Green: implementation
make gen(querier/dbauthz/dbmetrics/dbmock).listChats.make genagain for docs.make fmt && make lint.Refactor
params.Search != ""guard and adds zero DB calls to non-search requests.Verification
make genclean; handler tests +make lintpass.search:, punctuation-only search, and a structural-filter combo; note results in PR.This PR was generated by Coder Agents on behalf of @johnstcn.