How YAML Configuration and Thread Management Enable Topic-Based Routing in Claude Code Telegram
The Claude Code Telegram bot routes commands to specific projects by combining a YAML-backed Project Registry that validates project definitions with a Thread Manager that maintains persistent mappings between project slugs and Telegram forum topics.
The RichardAtCT/claude-code-telegram repository implements isolated project contexts using Telegram's native forum topics. Through a configuration-driven Project Registry and a SQLite-backed Thread Manager, the system achieves deterministic topic-based routing that connects incoming messages to their corresponding file system paths and Claude sessions.
Architecture of the Routing System
The routing mechanism relies on two tightly coupled subsystems that bridge YAML configuration with Telegram's forum topic API.
Project Registry (YAML Configuration)
Located in src/projects/registry.py, the registry loads and validates project definitions from a YAML file at startup. The load_project_registry function (lines 45‑62) validates each entry for required fields—unique slug, name, and relative path—and raises descriptive errors for duplicates or missing data (lines 74‑106).
The resulting ProjectRegistry object indexes all enabled projects by their slug (lines 24‑27, 33‑40), enabling O(1) lookups during runtime routing. Each project definition includes an absolute_path property that resolves the relative path against an approved root directory, ensuring filesystem access remains sandboxed.
Project Thread Manager
The ProjectThreadManager class in src/projects/thread_manager.py maintains the bi-directional mapping between project slugs and Telegram forum topics. It uses ProjectThreadRepository to persist these associations in a SQLite project_threads table via the ProjectThreadModel.
The manager provides two critical operations: sync_topics (line 49) for administrative synchronization, and resolve_project (lines 58‑70) for runtime request routing.
The Topic-Based Routing Workflow
When the bot processes commands, it follows a four-stage pipeline that ensures every message reaches the correct project context.
1. Configuration Loading at Startup
During initialization, the bot reads config/projects.example.yaml (or a user-supplied configuration path) through load_project_registry. This establishes the authoritative list of enabled projects. Any project missing required fields or using a duplicate slug triggers an immediate validation error, preventing ambiguous routing configurations.
2. Topic Synchronization
When an administrator invokes /sync_threads, the ProjectThreadManager.sync_topics method iterates over registry.list_enabled() (line 49). For each project:
- Existing mappings: The
_sync_existing_mappinghelper validates that the Telegram topic remains usable. If the topic was closed, it reopens it; if the project name changed in YAML, it renames the topic to maintain consistency. - New mappings: The
_create_and_map_topicmethod callsbot.create_forum_topic(lines 199‑201) to generate a new forum topic, then persists the association viaProjectThreadRepository.upsert_mapping.
3. Runtime Message Routing
When a user sends a message within a forum topic, the bot extracts chat_id and message_thread_id from the update object. It calls resolve_project(chat_id, thread_id) (lines 58‑70), which queries the SQLite mapping table to retrieve the project_slug. The manager then looks up the full ProjectDefinition in the registry via registry.get_by_slug, obtaining the absolute_path required to execute commands in the correct directory context.
4. Stale Topic Cleanup
After processing all enabled projects, sync_topics identifies mappings for projects no longer present in the registry. The manager closes these obsolete Telegram topics using bot.close_forum_topic (lines 1004‑1006) and removes their database entries, ensuring the forum structure mirrors the current YAML configuration exactly.
Implementation Examples
Loading the Project Registry
from pathlib import Path
from src.projects.registry import load_project_registry
config_path = Path("config/projects.example.yaml")
approved_dir = Path("/approved/root")
registry = load_project_registry(config_path, approved_dir)
# Access all enabled projects
for proj in registry.list_enabled():
print(proj.slug, proj.name, proj.absolute_path)
Relevant source: load_project_registry validates and builds the registry – see lines 45‑62, 71‑84, 99‑122 in src/projects/registry.py.
Synchronizing Topics for a Chat
from telegram import Bot
from src.projects.thread_manager import ProjectThreadManager
from src.storage.repositories import ProjectThreadRepository
bot = Bot(token="YOUR_TOKEN")
chat_id = -1001234567890 # Telegram forum ID
manager = ProjectThreadManager(registry, ProjectThreadRepository())
result = await manager.sync_topics(bot, chat_id)
print(result) # TopicSyncResult shows created/renamed/closed counts
Relevant source: ProjectThreadManager.sync_topics loops over enabled projects and creates or updates topics – see lines 45‑78, 90‑101 in src/projects/thread_manager.py.
Resolving a Project from an Incoming Message
async def handle_message(update, context):
chat_id = update.effective_chat.id
thread_id = update.message.message_thread_id
project = await manager.resolve_project(chat_id, thread_id)
if project:
# Route to the project's directory
cwd = project.absolute_path
# ...process command in this context...
Relevant source: resolve_project fetches the mapping and returns the matched ProjectDefinition – see lines 58‑70 in src/projects/thread_manager.py.
Key Files and Components
| File | Purpose |
|---|---|
src/projects/registry.py |
Loads and validates the YAML project list, provides lookup by slug via load_project_registry. |
src/projects/thread_manager.py |
Manages persistent mappings between projects and Telegram forum topics; implements sync_topics and resolve_project. |
src/storage/models.py |
Defines ProjectThreadModel for persisting topic mappings. |
src/storage/repositories.py |
Provides ProjectThreadRepository for CRUD operations on the mapping table. |
config/projects.example.yaml |
Example YAML configuration consumed by the registry loader. |
Summary
- The Project Registry in
src/projects/registry.pyvalidates YAML configurations at startup and indexes projects by unique slugs for fast retrieval. - The Thread Manager in
src/projects/thread_manager.pyguarantees a one-to-one relationship between each enabled project and a dedicated Telegram forum topic. - Topic synchronization handles the full lifecycle of forum topics—creating new ones, renaming changed projects, reopening closed threads, and cleaning up stale mappings.
- Runtime routing uses
resolve_projectto map incoming chat threads to their corresponding project definitions, enabling isolated command execution within the correct filesystem context.
Frequently Asked Questions
What is the required format for the YAML project configuration?
Each project entry in the YAML file must specify a unique slug identifier, a human-readable name, and a relative path pointing to the project directory under an approved root folder. The load_project_registry function validates these fields during startup and raises errors for missing attributes or duplicate slugs.
How does the bot handle renamed or closed Telegram topics?
During the synchronization process, the _sync_existing_mapping method checks each stored topic mapping. If a topic was manually closed in Telegram, the bot reopens it automatically; if the project's display name changed in the YAML configuration, the bot renames the corresponding forum topic to maintain consistency.
Can multiple projects share the same Telegram forum topic?
No. The architecture enforces a strict one-to-one mapping between project slugs and forum topics. The ProjectThreadManager creates a unique topic for each project during synchronization, and the resolve_project method assumes exclusive ownership of a thread ID by a single project.
Where are the topic-to-project mappings stored?
The mappings persist in a SQLite database table managed by ProjectThreadRepository. Each ProjectThreadModel record stores the chat_id, message_thread_id, and associated project_slug, enabling the bot to resolve project contexts across restarts without relying on Telegram topic titles or ephemeral state.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →