docs: add References header threading design spec
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,44 @@
|
||||
# Threading: Add References Header Fallback
|
||||
|
||||
## Problem
|
||||
|
||||
Ingress threading only resolves parent via `In-Reply-To`. When a client sets `References` without `In-Reply-To`, the bridge creates a new `thread_id` instead of attaching to the existing conversation, breaking thread continuity and downstream dispatch.
|
||||
|
||||
## Requirements
|
||||
|
||||
From `requirements.md:74`:
|
||||
- If `In-Reply-To` or `References` is present, find parent task in DB and inherit `thread_id`.
|
||||
- Otherwise use `message_id` as new `thread_id`.
|
||||
|
||||
## Design
|
||||
|
||||
### Current behavior (`imap.go:333-369`)
|
||||
|
||||
1. Parse MIME body headers, extract `In-Reply-To`
|
||||
2. If found, lookup parent by message ID → inherit `thread_id`
|
||||
|
||||
### New behavior
|
||||
|
||||
1. Parse MIME body headers, extract both `In-Reply-To` and `References`
|
||||
2. Try `In-Reply-To` first (unchanged)
|
||||
3. If no parent found via `In-Reply-To`, parse `References` header (space-separated `<msg-id>` list)
|
||||
4. Iterate `References` from **last to first** (most recent ancestor first)
|
||||
5. First match in DB wins → inherit `thread_id`
|
||||
|
||||
### Why last-to-first
|
||||
|
||||
`References` lists message IDs chronologically: `<oldest> ... <newest>`. The last entry is the most recent message in the chain, most likely already processed by the bridge.
|
||||
|
||||
### Scope
|
||||
|
||||
- Single file change: `internal/mail/imap.go` (threading block in `processMessage`, ~10 lines)
|
||||
- New tests: `internal/mail/imap_test.go` (References parsing, fallback, multi-ID)
|
||||
- No changes to DB, config, API, or other packages
|
||||
|
||||
### Test cases
|
||||
|
||||
1. `In-Reply-To` matches → existing behavior unchanged
|
||||
2. `In-Reply-To` absent, `References` has match → inherits thread_id from most recent match
|
||||
3. `References` has multiple IDs, only older one in DB → still matches
|
||||
4. Both headers absent → thread_id = message_id (unchanged)
|
||||
5. `References` present but no match in DB → thread_id = message_id (new thread)
|
||||
Reference in New Issue
Block a user