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_mapping helper 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_topic method calls bot.create_forum_topic (lines 199‑201) to generate a new forum topic, then persists the association via ProjectThreadRepository.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.py validates YAML configurations at startup and indexes projects by unique slugs for fast retrieval.
  • The Thread Manager in src/projects/thread_manager.py guarantees 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_project to 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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →