# How to Configure Project Threads Mode for Effective Telegram Topic Routing

> Learn to configure project threads mode for effective Telegram topic routing. Set ENABLE_PROJECT_THREADS true and define PROJECT_THREADS_MODE for seamless chat organization. Explore the repository at RichardAtCT/claude-code-tel...

- Repository: [Richard A/claude-code-telegram](https://github.com/richardatct/claude-code-telegram)
- Tags: how-to-guide
- Published: 2026-02-20

---

**Enable project threads mode by setting `ENABLE_PROJECT_THREADS=true` and defining `PROJECT_THREADS_MODE` as either `private` or `group`, then provide a `PROJECTS_CONFIG_PATH` YAML file that maps project slugs to directory paths.**

The `claude-code-telegram` bot supports advanced conversation routing through project-specific Telegram forum topics. By configuring project threads mode, developers can isolate discussions per codebase directory, ensuring that messages about the API service land in a distinct topic from those about the web frontend. This guide walks through the complete configuration based on the RichardAtCT/claude-code-telegram source code.

## Prerequisites and Environment Setup

### Required Environment Variables

The thread-routing subsystem is controlled through Pydantic settings defined in [`src/config/settings.py`](https://github.com/RichardAtCT/claude-code-telegram/blob/main/src/config/settings.py) (lines 200–228). To activate the feature, export the following variables or add them to a `.env` file:

```dotenv
ENABLE_PROJECT_THREADS=true
PROJECT_THREADS_MODE=group
PROJECT_THREADS_CHAT_ID=-1001234567890
PROJECTS_CONFIG_PATH=/home/bot/projects.yaml

```

- **`ENABLE_PROJECT_THREADS`**: Boolean flag that activates the entire subsystem.
- **`PROJECT_THREADS_MODE`**: Must be either `private` (creates topics in private chats) or `group` (creates forum topics inside a specified group).
- **`PROJECT_THREADS_CHAT_ID`**: Required only when mode is `group`; specifies the Telegram forum chat ID.
- **`PROJECTS_CONFIG_PATH`**: Absolute path to the YAML file listing projects.

The `Settings` class validates cross-field dependencies (lines 216–228), raising `ValueError` if `project_threads_chat_id` is missing in group mode or if `projects_config_path` is absent when threading is enabled.

## Defining Projects in the Registry

### YAML Configuration Schema

Create the YAML file referenced by `PROJECTS_CONFIG_PATH`. The schema is validated by `load_project_registry` in [`src/projects/registry.py`](https://github.com/RichardAtCT/claude-code-telegram/blob/main/src/projects/registry.py) (lines 42–66):

```yaml
projects:
  - slug: api
    name: API Service
    path: api
    enabled: true
  - slug: web
    name: Web Frontend
    path: web
    enabled: true
  - slug: utils
    name: Utility Library
    path: utils
    enabled: false

```

Each entry requires:
- **`slug`**: Unique identifier used internally for routing.
- **`name`**: Human-readable label used as the Telegram topic title.
- **`path`**: Directory path relative to the approved root.
- **`enabled`**: Boolean controlling whether the project receives a topic.

### Path Validation Rules

The registry loader enforces security constraints (lines 81–99 in [`src/projects/registry.py`](https://github.com/RichardAtCT/claude-code-telegram/blob/main/src/projects/registry.py)):
- Paths must be **relative**; absolute paths raise `ValueError`.
- Resolved paths must remain within the `approved_directory` to prevent directory traversal.
- The directory must exist on disk.

## Initializing the Thread Manager

Wire the components during bot startup. The `ProjectThreadManager` class in [`src/projects/thread_manager.py`](https://github.com/RichardAtCT/claude-code-telegram/blob/main/src/projects/thread_manager.py) orchestrates topic lifecycle management:

```python
from src.config.loader import load_settings
from src.projects.registry import load_project_registry
from src.projects.thread_manager import ProjectThreadManager
from src.storage.repositories import ProjectThreadRepository

settings = load_settings()
registry = load_project_registry(
    config_path=settings.projects_config_path,
    approved_directory=settings.approved_directory,
)
repo = ProjectThreadRepository()
thread_manager = ProjectThreadManager(registry, repo)

# Expose to handlers via bot_data

app.bot_data["thread_manager"] = thread_manager
app.bot_data["settings"] = settings

```

The `ProjectThreadRepository` (defined in [`src/storage/repositories.py`](https://github.com/RichardAtCT/claude-code-telegram/blob/main/src/storage/repositories.py)) persists mappings between project slugs and Telegram `message_thread_id` values using SQLAlchemy models from [`src/storage/models.py`](https://github.com/RichardAtCT/claude-code-telegram/blob/main/src/storage/models.py).

## Synchronizing Telegram Topics

### The Sync Process

Invoke `sync_topics` to reconcile the Telegram forum state with the project registry. This method (lines 45–84 in [`src/projects/thread_manager.py`](https://github.com/RichardAtCT/claude-code-telegram/blob/main/src/projects/thread_manager.py)) performs four operations:

1. **Creation**: For each enabled project without an existing mapping, calls `bot.create_forum_topic` and stores the returned `message_thread_id`.
2. **Reuse**: Existing valid mappings are preserved to maintain conversation history.
3. **Renaming**: If a project's `name` changed in the YAML but the topic exists, updates the topic title via `bot.edit_forum_topic`.
4. **Closure**: Identifies stale mappings (projects disabled or removed) and closes those topics.

### Handling Stale Topics

The manager queries stale mappings through `repository.list_stale_active_mappings` (referenced at lines 97–100) and closes them gracefully:

```python

# Simplified excerpt from thread_manager.py lines 97-102

stale_mappings = await self.repository.list_stale_active_mappings(
    chat_id=chat_id,
    active_project_slugs=active_slugs,
)
for stale in stale_mappings:
    await bot.close_forum_topic(chat_id=chat_id, message_thread_id=stale.thread_id)

```

The implementation handles `PrivateTopicsUnavailableError` for private chat topics that may become inaccessible.

## Storage and Persistence

Topic mappings are stored in the `ProjectThreadModel` SQLAlchemy class (defined in [`src/storage/models.py`](https://github.com/RichardAtCT/claude-code-telegram/blob/main/src/storage/models.py)). The repository layer in [`src/storage/repositories.py`](https://github.com/RichardAtCT/claude-code-telegram/blob/main/src/storage/repositories.py) provides async CRUD operations:

- **`get_by_project_and_chat`**: Retrieves existing thread mapping.
- **`create_mapping`**: Stores new topic associations.
- **`list_stale_active_mappings`**: Finds orphaned mappings for cleanup.

This persistence ensures that bot restarts maintain consistent topic routing without recreating existing conversations.

## Summary

- **Enable the subsystem** by setting `ENABLE_PROJECT_THREADS=true` and choosing a `PROJECT_THREADS_MODE` (`private` or `group`).
- **Validate dependencies** through the Pydantic `Settings` class in [`src/config/settings.py`](https://github.com/RichardAtCT/claude-code-telegram/blob/main/src/config/settings.py), which enforces required fields like `PROJECT_THREADS_CHAT_ID` for group mode.
- **Define projects** in a YAML file loaded by [`src/projects/registry.py`](https://github.com/RichardAtCT/claude-code-telegram/blob/main/src/projects/registry.py), ensuring paths are relative and contained within the approved directory.
- **Initialize the manager** by wiring `ProjectThreadManager` with a `ProjectThreadRepository` during bot startup.
- **Synchronize topics** via `sync_topics` to create, rename, and close forum topics based on the current project registry state.

## Frequently Asked Questions

### What is the difference between private and group thread modes?

**Private mode** creates individual topics within private chats between the bot and each user, suitable for personal project management. **Group mode** creates forum topics inside a designated Telegram group chat (supergroup with forums enabled), allowing team-wide project discussions. Group mode requires the `PROJECT_THREADS_CHAT_ID` environment variable, while private mode uses the incoming chat ID dynamically.

### How does the bot handle renamed or deleted projects?

The `ProjectThreadManager.sync_topics` method in [`src/projects/thread_manager.py`](https://github.com/RichardAtCT/claude-code-telegram/blob/main/src/projects/thread_manager.py) detects configuration drift by comparing the active project registry against persisted mappings. When a project is renamed, the manager calls `bot.edit_forum_topic` to update the topic title. When a project is disabled or removed, the manager identifies stale mappings via `list_stale_active_mappings` and closes those topics using `bot.close_forum_topic`, ensuring no orphaned conversations remain.

### Can I use absolute paths in the projects.yaml file?

No, absolute paths are explicitly rejected by the validation logic in [`src/projects/registry.py`](https://github.com/RichardAtCT/claude-code-telegram/blob/main/src/projects/registry.py) (lines 81–83). The loader requires relative paths to prevent directory traversal attacks and ensure all project directories remain within the `APPROVED_DIRECTORY` root. If an absolute path is provided, the bot raises a `ValueError` during startup, preventing the registry from loading.

### What happens if the forum chat ID is invalid?

If `PROJECT_THREADS_MODE` is set to `group` but `PROJECT_THREADS_CHAT_ID` is missing, the Pydantic validator in [`src/config/settings.py`](https://github.com/RichardAtCT/claude-code-telegram/blob/main/src/config/settings.py) (lines 216–224) raises a `ValueError` immediately upon settings load, preventing the bot from starting. If the chat ID is provided but invalid (e.g., the bot lacks permissions or the chat doesn't exist), Telegram API calls like `create_forum_topic` will raise a `TelegramError` during the `sync_topics` execution, which the handler should catch and log appropriately.