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

79 lines
3.3 KiB
Markdown

# 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)