fix(channels): archive group messages before the pending buffer drops them - #1514
Merged
clark-cant merged 1 commit intoAug 15, 2026
Merged
Conversation
… them channel_pending_messages is a buffer, not an archive. Rows were deleted outright on two paths: the bot being mentioned hands the buffer to the agent and clears the key, and LLM compaction replaces old rows with a summary. For group capture that buffer held the only copy of the raw text, so a single mention or compaction pass destroyed days of messages nothing had read yet. Copy every row into channel_message_archive inside the same transaction as the delete, tagged with the reason (consumed, compacted, stale). Archived rows keep their original id, so a replayed delete is a no-op. Add ListArchivedByKey so consumers that need full history read the archive instead of the buffer.
clark-cant
approved these changes
Aug 15, 2026
clark-cant
left a comment
Contributor
There was a problem hiding this comment.
Approved: archive-before-delete is transactionally implemented for PostgreSQL and SQLite, tenant-scoped reads are covered, migration/schema parity is present, duplicate gate is clear, and all required CI checks pass. No critical or important findings.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
channel_pending_messagesis a buffer, not an archive. Rows leave it on two paths, both plain deletes:Clear()wipes the key;For group capture that buffer holds the only stored copy of the raw text, so a single mention or one compaction pass permanently destroyed every message nothing had read yet. Observed in production: a compaction run erased ~4.5 days of a Zalo group's history, and a later mention erased the rest along with the summary that replaced it.
Change
channel_message_archive(migration 97; SQLite schema 60).DeleteByKey,CompactandDeleteStalecopy rows into the archive inside the same transaction as the delete, tagged witharchive_reason(consumed,compacted,stale).id, so a replayed delete is a no-op.ListArchivedByKey(channel, key, since, limit)for consumers that need full history; tenant-scoped like every other store read.Wiring lives in the store layer, so every channel (Telegram, Discord, Slack, Feishu, WhatsApp, Zalo) and the HTTP compact endpoint are covered with no call-site changes.
Tests
internal/store/sqlitestore/pending_message_archive_test.go)tests/integration/pending_message_archive_test.go), run against a clean pgvector instance migrating 1 → 97Covers clear, compaction, idempotency across compact-then-clear, the
sincefilter, and tenant scoping.go buildandgo vetclean under both the default andsqliteonlytags. The 6 pre-existing Ollama failures ininternal/httpreproduce on a cleanorigin/devcheckout and are unrelated.