Thread Chain Implementation

Design note: envoy/thread-chain-design

Branch: thread-chain — not yet merged to m

What was added

New code in orchestrator.py

fetch_thread_chain logic

  1. Seed cache with trigger (full body already fetched)
  2. Parse References header for ancestor Message-IDs (cap at 15). Fall back to In-Reply-To if absent.
  3. Scan bundle note JSON for Message-IDs (for continuation runs where bundle_key is known)
  4. For each candidate: headers-only IMAP search across INBOX → Done → Sent → other folders
  5. Upgrade In-Reply-To (direct parent) to full body — fetch and update cache in-place
  6. Sort ancestors by date newest-first
  7. Return [trigger] + sorted_ancestors, and a set of not_available Message-IDs

call_llm changes

main() changes

Gather phase — Quick-ID upgrade-in-place

When LLM requests add_emails with a Quick-ID like #3:

  1. Build _qid_map from thread_pool + gathered_emails
  2. If the Quick-ID refers to a headers-only pool entry: fetch full body from IMAP, em.update() in-place, update cache
  3. If body already present: report 'already have full body'
  4. Remaining refs (real Message-IDs or unknown Quick-IDs) go to fetch_emails_by_id as before

Key design decisions

Files changed

Semantic validation — diag_thread_chain.py

Script: diag_thread_chain.py — inject a synthetic 3-email thread, run fetch_thread_chain, render exactly what the LLM receives, optionally send to ask for validation.

Usage: python3 diag_thread_chain.py --inject --ask

First run results

Bug found and fixed during diagnostic

LLM design feedback (not bugs)

Scenario test suite — diag_thread_chain.py --scenario

8 named scenarios covering edge cases. Run with: python3 diag_thread_chain.py --scenario all

Results (all 8 PASS)

Bug found and fixed: broken-irt

fetch_thread_chain only used In-Reply-To as a fallback when References was empty. If In-Reply-To pointed to a MID absent from References (malformed email), that MID was never searched and never appeared in not_available.

Fix: always add In-Reply-To to candidates if not already in seen. For normal emails, IRT is already in References so seen-set prevents duplicate. For broken emails, IRT is searched and, if absent, lands in not_available.

version 1  ·  created 2026-03-07