From 4090a44524464d0b16d4aacec2442a3135da020a Mon Sep 17 00:00:00 2001 From: thuanle Date: Tue, 28 Apr 2026 04:51:38 +0700 Subject: [PATCH] docs: add References header threading design spec Co-Authored-By: Claude Opus 4.7 --- .../2026-04-28-references-threading-design.md | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 docs/superpowers/specs/2026-04-28-references-threading-design.md diff --git a/docs/superpowers/specs/2026-04-28-references-threading-design.md b/docs/superpowers/specs/2026-04-28-references-threading-design.md new file mode 100644 index 0000000..139bebf --- /dev/null +++ b/docs/superpowers/specs/2026-04-28-references-threading-design.md @@ -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 `` 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: ` ... `. 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)