feat: auto-archive inactive chats with audit trail#24642
Conversation
Docs preview |
d8d9e0e to
4583b50
Compare
johnstcn
left a comment
There was a problem hiding this comment.
🤖
Clean implementation. The batch-paginated archive loop, the family-activity LATERAL subquery, and the status-aware filtering are well thought through. Test coverage is thorough: 8 subtests covering disabled state, active/inactive, child-activity propagation, status filtering, pinned exemptions, multi-owner audits, idempotency, and batch pagination.
5 reviewers (Test Auditor, Database, Go Architect, Edge Case Analyst, Contract Auditor) ran in parallel. After cross-checking for contradictions and weak evidence, 1 nit survived. Most flagged concerns (authz asymmetry on GetChatAutoArchiveDays, audit coupling in dbpurge, nil-deref on auditor load) are consistent with established codebase patterns (GetChatRetentionDays, autobuild.NewExecutor, ActivateDormantUser).
4583b50 to
641ee3a
Compare
|
/coder-agents-review |
Layers per-owner digest notifications onto the chat auto-archive subsystem introduced in #24642. Each tick's archived rows are grouped by owner, the top 25 titles per owner are rendered into a new 'Chats Auto-Archived' notification template, and any remainder surfaces as 'and N more'. Duplicate same-day digests collapse via the notifier's existing dedupe hash. The template body branches on retention_days: when retention is disabled (retention_days=0), users are told archived chats are kept indefinitely rather than falsely claiming imminent deletion. Adds one test subtest for the digest overflow cap, plus digest assertions to the existing archive subtests from #24642. Two notification test fixtures (standard and retention-zero) lock the two template branches into goldens. Depends on #24642. Migration 000476 adds the notification template row; 000475 (in the core PR) already created the candidate index.
There was a problem hiding this comment.
Well-structured PR. The LATERAL family-activity subquery, partial index targeting, and batch-limited purge loop are solid. Test coverage is thorough: 6 subtests with a real Postgres backend, deterministic multi-tick control via quartz.Mock, and proportional batch-size testing. The decision log in the PR description is exemplary.
3 P2, 10 P3, 1 Nit, 2 Notes. 16 reviewers ran in parallel; Ging-Go and Takumi reported no findings (Go idioms and concurrency are clean).
The dominant theme is that the cascade step (c.root_chat_id = t.id) applies no per-child filtering for status or pin_order, creating a gap between the documentation's unqualified guarantees ("Chats whose status indicates ongoing work are never auto-archived", "Pinned conversations are never auto-archived") and the actual behavior. The same cascade shape exists in manual ArchiveChatByID, so this is a consistent convention, not a regression. The simplest fix is likely scoping the doc language to root chats rather than adding complexity to the cascade.
This is a first-round panel review. Findings are the panel's output, not a request for changes.
"Shall I show you? ♥" (Hisoka, on what happens to the audit trail under load)
🤖 This review was automatically generated with Coder Agents.
9e35586 to
1766474
Compare
|
/coder-agents-review |
There was a problem hiding this comment.
Round 2. 12 of 16 R1 findings addressed, 3 acknowledged, 1 contested (DEREM-16 closed by panel vote, 4/4).
All three P2s are fixed: docs scoped to root chats (DEREM-1, DEREM-4), dispatch timeout removed with children skipped from audit (DEREM-2), non-existent Behavior tab reference replaced with API instructions (DEREM-3). The ORDER BY now matches the partial index (DEREM-5). Test coverage expanded with boundary, deleted-message, and all-four-status tests (DEREM-7/8/9). GET auth aligned with retention (DEREM-10).
The DEREM-2 fix (audit roots only, skip children) introduced a doc/code mismatch in the audit section. Two stale comments survived the DEREM-10 fix. 2 P3, 2 Nit new.
"Oh, this is a nice test suite!" (Bisky, on the 10-subtest harness)
🤖 This review was automatically generated with Coder Agents.
81ffa45 to
e1e03ba
Compare
Layers per-owner digest notifications onto the chat auto-archive subsystem introduced in #24642. Each tick's archived rows are grouped by owner, the top 25 titles per owner are rendered into a new 'Chats Auto-Archived' notification template, and any remainder surfaces as 'and N more'. Duplicate same-day digests collapse via the notifier's existing dedupe hash. The template body branches on retention_days: when retention is disabled (retention_days=0), users are told archived chats are kept indefinitely rather than falsely claiming imminent deletion. Adds one test subtest for the digest overflow cap, plus digest assertions to the existing archive subtests from #24642. Two notification test fixtures (standard and retention-zero) lock the two template branches into goldens. Depends on #24642. Migration 000476 adds the notification template row; 000475 (in the core PR) already created the candidate index.
feat(coderd): chat auto-archive owner digest notifications Layers per-owner digest notifications onto the chat auto-archive subsystem introduced in #24642. Each tick's archived rows are grouped by owner, the top 25 titles per owner are rendered into a new 'Chats Auto-Archived' notification template, and any remainder surfaces as 'and N more'. Duplicate same-day digests collapse via the notifier's existing dedupe hash. The template body branches on retention_days: when retention is disabled (retention_days=0), users are told archived chats are kept indefinitely rather than falsely claiming imminent deletion. Adds one test subtest for the digest overflow cap, plus digest assertions to the existing archive subtests from #24642. Two notification test fixtures (standard and retention-zero) lock the two template branches into goldens. Depends on #24642. Migration 000476 adds the notification template row; 000475 (in the core PR) already created the candidate index.
feat(coderd): chat auto-archive owner digest notifications Layers per-owner digest notifications onto the chat auto-archive subsystem introduced in #24642. Each tick's archived rows are grouped by owner, the top 25 titles per owner are rendered into a new 'Chats Auto-Archived' notification template, and any remainder surfaces as 'and N more'. Duplicate same-day digests collapse via the notifier's existing dedupe hash. The template body branches on retention_days: when retention is disabled (retention_days=0), users are told archived chats are kept indefinitely rather than falsely claiming imminent deletion. Adds one test subtest for the digest overflow cap, plus digest assertions to the existing archive subtests from #24642. Two notification test fixtures (standard and retention-zero) lock the two template branches into goldens. Depends on #24642.
d0cc93d to
ddbfe37
Compare
|
/coder-agents-review |
There was a problem hiding this comment.
Round 3. All 4 R2 findings addressed (e1e03ba). Branch rebased (migration renumbered 000475 -> 000477). Netero R3 clean across all 8 sections.
21 findings across 3 rounds: 17 fixed, 3 accepted, 1 panel-closed. 1 new P3.
Mafuuu and Razor reported no findings. Razor verified every doc claim against the code; all match. The implementation is sound.
🤖 This review was automatically generated with Coder Agents.
Layers per-owner digest notifications onto the chat auto-archive subsystem introduced in #24642. Each tick's archived rows are grouped by owner, the top 25 titles per owner are rendered into a new 'Chats Auto-Archived' notification template, and any remainder surfaces as 'and N more'. Duplicate same-day digests collapse via the notifier's existing dedupe hash. The template body branches on retention_days: when retention is disabled (retention_days=0), users are told archived chats are kept indefinitely rather than falsely claiming imminent deletion. Adds one test subtest for the digest overflow cap, plus digest assertions to the existing archive subtests from #24642. Two notification test fixtures (standard and retention-zero) lock the two template branches into goldens. Depends on #24642. Migration 000476 adds the notification template row; 000475 (in the core PR) already created the candidate index.
|
/coder-agents-review |
Layers per-owner digest notifications onto the chat auto-archive subsystem introduced in #24642. Each tick's archived rows are grouped by owner, the top 25 titles per owner are rendered into a new 'Chats Auto-Archived' notification template, and any remainder surfaces as 'and N more'. Duplicate same-day digests collapse via the notifier's existing dedupe hash. The template body branches on retention_days: when retention is disabled (retention_days=0), users are told archived chats are kept indefinitely rather than falsely claiming imminent deletion. Adds one test subtest for the digest overflow cap, plus digest assertions to the existing archive subtests from #24642. Two notification test fixtures (standard and retention-zero) lock the two template branches into goldens. Depends on #24642. Migration 000476 adds the notification template row; 000475 (in the core PR) already created the candidate index.
| rest of `dbpurge` during the first run. If you need to prevent the | ||
| initial backfill entirely during a migration window, set | ||
| `auto_archive_days = 0` before upgrading and re-enable it when you | ||
| are ready. |
There was a problem hiding this comment.
This is actually not possible. You need to upgrade in order to have this setting. Should we make auto-archive opt-in (i.e. default = 0)?
Layers per-owner digest notifications onto the chat auto-archive subsystem introduced in #24642. Each tick's archived rows are grouped by owner, the top 25 titles per owner are rendered into a new 'Chats Auto-Archived' notification template, and any remainder surfaces as 'and N more'. Duplicate same-day digests collapse via the notifier's existing dedupe hash. The template body branches on retention_days: when retention is disabled (retention_days=0), users are told archived chats are kept indefinitely rather than falsely claiming imminent deletion. Adds one test subtest for the digest overflow cap, plus digest assertions to the existing archive subtests from #24642. Two notification test fixtures (standard and retention-zero) lock the two template branches into goldens. Depends on #24642. Migration 000476 adds the notification template row; 000475 (in the core PR) already created the candidate index.
There was a problem hiding this comment.
Round 4. DEREM-21 addressed (23ac3ba). All 21 findings from R1-R3 resolved: 18 fixed, 3 accepted, 1 panel-closed.
Netero R4: clean. Panel (Bisky, Mafuuu, Razor): Mafuuu and Razor reported no findings. Razor verified every policy model claim, SQL query property, and doc assertion against the code. One P4 from Bisky: MultipleOwners asserts audit attribution but not the chats' Archived state; other subtests cover that class of assertion.
22 findings across 4 rounds, 21 resolved. The implementation is thorough and well-tested.
🤖 This review was automatically generated with Coder Agents.
| COALESCE(activity.last_activity_at, c.created_at) AS last_activity_at | ||
| FROM chats c | ||
| LEFT JOIN LATERAL ( | ||
| SELECT MAX(cm.created_at) AS last_activity_at |
There was a problem hiding this comment.
If a chat message is edited, does that change created_at?
There was a problem hiding this comment.
The UpdateChatMessageByID query does not. But editing a chat message in the UI calls the patchChatMessage handler. This calls EditMessage, which soft-deletes the original and all subsequent messages, and then inserts a new message with a fresh created_at. So, yes.
| )::timestamptz AS last_activity_at | ||
| FROM archived a | ||
| LEFT JOIN to_archive t ON t.id = a.id | ||
| ORDER BY (a.root_chat_id IS NULL) DESC, a.owner_id ASC, a.created_at ASC, a.id ASC; |
There was a problem hiding this comment.
Doing all of this in a single query is efficient, but I worry about having repeated logic (e.g. archival cascades to children) in multiple places. This is a case where inefficiency might be beneficial (pull target IDs and call regular archival path).
There was a problem hiding this comment.
How about GetAutoArchivableChatIDs + ArchiveChatsByIDs? Could do it as a separate post-merge followup.
Adds a background job in
dbpurgethat periodically archives chats inactive beyond a configurable threshold. Each archived root chat gets a background audit entry taggedchat_auto_archive. Disabled by default.AutoArchiveInactiveChatsSQL query with LATERAL last-activity subquery and partial index on archive candidatessite_configs-backedauto_archive_dayssetting with admin-only PUT, any-authenticated-user GETroot_chat_id; pinned chats and active threads exemptpatchChat) behaviorStacked PR #24643 adds per-owner digest notifications.
Stacked PR #24704 adds the requisite UI controls.