Files
obsidian-vault/work/plans/no-topic-routing.md
T

3.3 KiB

Plan: (no topic) Thread Routing

Date: 2026-05-14 Status: Planning Priority: Medium

Problem

When Alex sends a message in Zulip's (no topic) thread (the default unnamed thread), it usually means one of two things:

  1. He missed the correct topic — it should go to the most recent active thread in the same stream
  2. He's starting a genuinely new discussion

Eagle needs to automatically detect which case it is and act accordingly.

Design

Detection Logic

Step 1: Find most recent active thread in stream

  • Query Zulip API for recent messages in the current stream (last N hours)
  • Exclude (no topic) thread itself
  • Get the thread with the most recent message → candidate_thread

Step 2: Relevance check

  • Take first ~200 chars of (no topic) message
  • Take thread name + last 2-3 messages from candidate_thread as context
  • Ask LLM (cheap/fast call): "Is this message topically related to this thread? yes/no + confidence"

Step 3: Action

If related (confidence > 0.7):

  • Quote Alex's message in candidate_thread with attribution: [Cross-posted from (no topic) — @Alex]
  • Reply in (no topic): Переношу в тред «{thread_name}» ↗
  • Continue discussion in candidate_thread
  • ⚠️ Coordination: check if another Eagle instance is active in candidate_thread (see below)

If not related (new topic):

  • Rename (no topic) thread to a descriptive topic name (2-5 words, inferred from message)
  • Reply normally in the renamed thread

Multi-instance Coordination

Eagle may have multiple instances running in different Zulip topics simultaneously. To avoid conflicts when routing to an existing thread:

Option A: DB mutex (preferred)

  • zulip_thread_locks table: (stream, topic, locked_by_session, locked_at)
  • Before posting in another thread → INSERT lock (fail on conflict)
  • Release lock after posting
  • Stale lock (>5 min) → auto-expire

Option B: Message-based handoff

  • Before cross-posting → send a "silent" message to candidate_thread (deletable): [Eagle routing incoming from (no topic)]
  • If another instance replies with [ACK] → let it handle
  • If no reply in 30s → proceed

Recommendation: Option A — simpler, no timing issues.

Multi-instance Awareness

Each Eagle instance should know its own Zulip topic context. When spawned in a topic, it registers itself in zulip_thread_locks. Eagle reads this to avoid double-posting.

Implementation Steps

  1. Add Zulip stream message history query to Eagle's toolkit (REST API call via hermes)
  2. Create zulip_thread_locks table in personal_os DB
  3. Write topic routing logic as a function in the Eagle prompt or as a cron-triggered script
  4. Test: send message in (no topic), verify routing + rename behavior

Questions

  • Does Eagle get invoked automatically for every (no topic) message, or only when mentioned?
  • Is Eagle a single process or multiple instances per stream? (affects coordination complexity)
  • What's the stream scope? Only master? Or all streams?

Notes

  • Current session: this (no topic) thread in master stream should be renamed to something like planning: system improvements 2026-05
  • Zulip topic rename API: PATCH /api/v1/messages/{message_id} with topic param (requires first message ID of the thread)