ADR-0226accepted

VIP Contextual Narrative Briefs

Context and Problem Statement

The VIP email pipeline (vip-email-received.ts) produces analytical briefs: thread stats, followed links, binary “needs attention: yes/no.” Joel wants narratives — briefs that read like an executive assistant who knows the full history, the project arc, the relationship cadence, and can calibrate urgency precisely.

The infrastructure exists (ADR-0204 shipped semantic recall, Typesense collections, gateway context refresh) but the VIP pipeline doesn’t use it. It runs its own parallel context fan-out (Front thread, Granola, memory recall, GitHub) that’s disconnected from the broader context system.

Current VIP brief output

## VIP: Alex Hillman <alex@indyhall.org> — [aih] Monday email review
 
Thread: 12 messages, last activity 2h ago
Your last reply: 3d ago
Key links: docs.google.com/... — 2026 Proposal
Needs your attention: yes — Joel has not replied in the cached thread yet

What Joel wants

## VIP: Alex Hillman — Monday Launch Email Ready
 
Alex sent the final Monday launch email for Matt's approval. This is the
third touchpoint this week on the AIH launch sequence — Tuesday was sales
page feedback (Excalidraw diagrams, learning environment screenshots),
Wednesday the email copy itself. The sales page feedback is still open.
 
This email is time-sensitive: it needs Matt's approval and scheduling before
Monday morning. You haven't replied to the sales page thread yet (3 days).
 
⏰ Reply today — launch email needs approval for Monday send.

Decision

Extend the VIP pipeline with four changes that build on ADR-0204’s infrastructure. Each is independently valuable and ships incrementally.

Change 1: Populate email_threads collection

The email_threads Typesense collection schema exists but the collection has never been created or populated. Backfill all VIP sender conversations from the Front API.

Implementation:

  • New Inngest function vip/email-threads.backfill — walks Front API conversations filtered by VIP senders, paginates through messages, indexes into email_threads
  • Each document: conversation_id, subject, participants, messages_json, followed_links, summary, SKOS concept tags
  • Runs once for backfill, then the existing VIP pipeline upserts on each new email (the cache-email-thread step already exists but the collection doesn’t)

Change 2: SKOS concept tagging on index

When indexing into any Typesense collection (email_threads, channel_messages, system_knowledge, otel_events), tag documents with SKOS concepts for graph traversal:

  • person:alex-hillman — sender/participant
  • project:ai-hero — detected from subject/content using operator-relay’s existing project pattern matching
  • topic:launch-email — extracted topic
  • role:collaborator — relationship type

Schema addition to email_threads and channel_messages:

{ name: "skos_concepts", type: "string[]", facet: true, optional: true }

This enables queries like: “everything tagged person:alex-hillman AND project:ai-hero in the last 30 days” — graph traversal via faceted search rather than hoping keyword search finds connections.

Change 3: Expand VIP context fan-out

Add three new parallel steps to the existing Promise.all fan-out in vip-email-received.ts:

  1. search-email-history — semantic search email_threads for this sender + subject. Returns past conversation summaries, reply cadence, open threads.
  2. search-channel-history — semantic search channel_messages for this person/project. Returns relevant Slack/Discord mentions.
  3. search-vault-context — semantic search system_knowledge for project + relationship notes. Returns ADRs, vault notes, contact dossiers.

Each step is ~10 lines using existing Typesense search infrastructure.

Change 4: Narrative synthesis prompt with urgency calibration

Replace the analytical brief prompt with a narrative synthesis prompt:

Urgency scale (calibrated, not binary):

  • 🔴 Reply now — deadline within 24h, blocking someone, time-sensitive decision
  • 🟠 Reply today — needs response but not immediately blocking
  • 🟡 Reply this week — important but not time-pressured
  • 🟢 FYI — informational, no response needed
  • Already handled — Joel already replied or action was taken

Prompt structure:

You are briefing an executive who's been away for a day. Given:
- The current email and full thread history
- Past email conversations with this sender (from email_threads)
- Related Slack/channel messages (from channel_messages)
- Vault notes and ADRs about this project (from system_knowledge)
- Recent meeting context (from Granola)
- Memory observations about this person/project
 
Write a 3-5 sentence narrative that:
1. Situates this email in the arc of the relationship and project
2. Notes what's changed since the last touchpoint
3. Identifies what's open/unresolved from prior threads
4. Ends with a calibrated urgency recommendation (🔴🟠🟡🟢✅) and why
 
Do NOT use bullet points. Write prose. Be specific about dates, names, and what's at stake.

Consequences

Positive

  • VIP briefs become narratives with institutional memory — Joel gets context without asking
  • Urgency calibration replaces binary “needs attention” — actionable signal
  • SKOS tagging enables graph traversal across all collections — future queries can traverse person→project→topic relationships
  • Backfilled email_threads becomes a searchable relationship history
  • All changes use existing Typesense + recall infrastructure (ADR-0204)

Negative

  • Backfill is a one-time Front API cost (~100-200 API calls for all VIP conversations)
  • SKOS tagging adds index-time computation (project pattern matching + concept extraction)
  • Narrative synthesis prompt is larger than current analytical prompt — more LLM tokens per VIP email
  • email_threads collection adds Typesense storage (~1-5MB for typical VIP email volume)

Neutral

  • Does not change the VIP sender detection logic (vip-utils.ts)
  • Does not change the newsletter auto-archive path
  • Does not change Todoist task creation from VIP analysis
  • Does not change the stutter fix (ADR-0226 ships after the ingested bucket fix)

Implementation Order

  1. Change 1 — backfill email_threads (unblocks everything else)
  2. Change 4 — narrative prompt (immediate brief quality improvement even without new context sources)
  3. Change 3 — expanded fan-out (richer context for narratives)
  4. Change 2 — SKOS tagging (enables graph queries, compounds over time)

Files to Modify

ChangeFileScope
1packages/system-bus/src/inngest/functions/vip-email-backfill.tsNew backfill function
1packages/system-bus/src/lib/typesense.tsEnsure email_threads collection creation
2packages/system-bus/src/lib/typesense.tsAdd skos_concepts field to schemas
2packages/system-bus/src/inngest/functions/vip-email-received.tsTag on upsert
3packages/system-bus/src/inngest/functions/vip-email-received.tsAdd 3 search steps to fan-out
4packages/system-bus/src/inngest/functions/vip-email-received.tsReplace brief/analysis prompts

Verification

  1. joelclaw send vip/email-threads.backfill — verify email_threads collection populated
  2. Trigger a VIP email → verify narrative brief on Telegram with urgency emoji
  3. Check email_threads docs have skos_concepts facets
  4. Semantic search: joelclaw recall "Alex Hillman launch email" returns relevant email thread hits