homelab/.planning/phases/02-telegram-integration/02-01-SUMMARY.md
Mikkel Georgsen 76fb57877d docs(02-01): complete persistent subprocess + telegram utils plan
Tasks completed: 2/2
- Refactor ClaudeSubprocess to persistent process with stream-json I/O
- Create telegram_utils.py with message formatting and typing indicator

SUMMARY: .planning/phases/02-telegram-integration/02-01-SUMMARY.md
2026-02-04 19:19:07 +00:00

6.9 KiB

phase plan subsystem tags requires provides affects tech-stack key-files key-decisions patterns-established duration completed
02-telegram-integration 01 infra
asyncio
subprocess
python
claude-code-cli
stream-json
telegram
markdown
typing-indicator
phase provides
01-session-process-foundation Session management and subprocess foundations
Persistent Claude Code subprocess with stream-json I/O (eliminates ~1s spawn overhead per turn)
Tool call progress notifications via on_tool_use callback
Smart message splitting respecting MarkdownV2 code block boundaries
MarkdownV2 escaping with code block awareness
Typing indicator loop for long operations
02-02
bot-integration
message-handling
added patterns
telegram.constants.ChatAction (typing indicators)
Persistent subprocess with NDJSON stdin streaming
Stream-json event routing including tool_use events
Smart message splitting at code block boundaries
Context-sensitive MarkdownV2 escaping (preserves code blocks)
Typing indicator loop with asyncio.Event cancellation
created modified
telegram/telegram_utils.py
telegram/claude_subprocess.py
Use persistent subprocess instead of fresh process per turn (eliminates ~1s overhead)
Write NDJSON to stdin with drain() to prevent pipe buffer deadlock
Persistent readers run for process lifetime, result events mark not busy but don't exit loop
Parse content_block_start events for tool_use to enable progress notifications
Split messages at 4000 chars (not 4096) to leave room for escape character expansion
Never split inside code blocks (track in_code_block state while iterating lines)
Escape MarkdownV2 special chars only outside code blocks (regex pattern to identify code regions)
Pattern 1: Persistent stdin streaming - start() spawns process, send_message() writes NDJSON, readers run until process dies
Pattern 2: Tool use notifications - Parse content_block_start with type=tool_use, extract name and input dict, pass to callback
Pattern 3: Smart message splitting - Track code block state, split only when NOT in code block and would exceed limit
Pattern 4: Code-aware escaping - Split by code regions (regex), escape only non-code text, rejoin
Pattern 5: Typing loop - Re-send every 4s via asyncio.wait_for timeout until Event is set
5min 2026-02-04

Phase 2 Plan 1: Persistent Subprocess + Telegram Utils Summary

Persistent Claude Code subprocess with stream-json I/O and Telegram message formatting utilities for bot integration

Performance

  • Duration: 5 minutes
  • Started: 2026-02-04T19:12:28Z
  • Completed: 2026-02-04T19:17:24Z
  • Tasks: 2
  • Files created: 1
  • Files modified: 1

Accomplishments

  • Refactored ClaudeSubprocess from fresh-process-per-turn to persistent process model
  • Eliminated ~1s spawn overhead per message turn
  • Added stream-json stdin I/O for NDJSON message delivery to persistent process
  • Implemented tool_use event parsing and on_tool_use callback for progress notifications
  • Created telegram_utils.py with smart message splitting, MarkdownV2 escaping, and typing indicator loop
  • Smart splitting respects code block boundaries (never splits inside triple-backtick blocks)
  • MarkdownV2 escaping preserves code blocks while escaping 17 special characters outside code
  • Typing indicator loop re-sends every 4 seconds with clean asyncio.Event cancellation

Task Commits

Each task was committed atomically:

  1. Task 1: Refactor ClaudeSubprocess to persistent process - 6a115a4 (refactor)
  2. Task 2: Create telegram_utils.py - 6b624d7 (feat)

Files Created/Modified

Created:

  • telegram/telegram_utils.py - Message formatting utilities: split_message_smart (code-block-aware splitting), escape_markdown_v2 (context-sensitive escaping), typing_indicator_loop (4s re-send pattern)

Modified:

  • telegram/claude_subprocess.py - Refactored to persistent subprocess with --input-format stream-json stdin, persistent stdout/stderr readers, tool_use event parsing, NDJSON message delivery via stdin.write + drain pattern

Decisions Made

  1. Persistent process architecture: Spawn Claude Code once with stream-json I/O, write NDJSON to stdin for each turn. Eliminates ~1s spawn overhead per message and maintains conversation context across turns.

  2. Tool use callback design: Extract tool name and raw input dict from content_block_start events, pass directly to callback. Bot layer (Plan 02) will format human-readable progress messages. Keeps subprocess layer format-agnostic.

  3. Smart splitting strategy: Track code block state (in/out of triple backticks), split only when NOT in code block and would exceed 4000 char limit. If code block itself exceeds limit, include it whole (Telegram handles overflow).

  4. Escape only outside code: Use regex to identify code regions (... and ...), escape MarkdownV2 special chars only in non-code text. Prevents breaking code syntax while properly escaping user-facing text.

  5. 4000 char split threshold: Use 4000 instead of Telegram's 4096 limit to leave room for MarkdownV2 escape character expansion (each special char becomes 2 chars with backslash).

  6. Typing loop cancellation pattern: Use asyncio.wait_for with 4s timeout on Event.wait(). Clean cancellation when Event is set, automatic re-send on timeout. Pattern from PTB community best practices.

Deviations from Plan

None - plan executed exactly as written.

Issues Encountered

Issue 1: ChatAction import path

  • Problem: Initial import from telegram import ChatAction failed with ImportError
  • Root cause: python-telegram-bot v22+ moved ChatAction to telegram.constants
  • Resolution: Changed to from telegram.constants import ChatAction
  • Impact: Minor, caught during testing, fixed immediately

User Setup Required

None - no external service configuration required.

Next Phase Readiness

Ready for Plan 02 (Bot Integration):

  • ClaudeSubprocess provides persistent process with tool call notifications
  • telegram_utils provides all message formatting needed for bot layer
  • Smart splitting and escaping handle Claude's output for Telegram delivery
  • Typing indicator loop ready for integration with message handlers
  • stdin.write + drain pattern ensures no pipe deadlock

Integration points for Plan 02:

  • Call subprocess.start() once per session
  • Pass on_tool_use callback to format and send progress messages
  • Use split_message_smart() on Claude output before sending to Telegram
  • Use escape_markdown_v2() if sending with MarkdownV2 parse mode
  • Wrap subprocess.send_message() with typing_indicator_loop() background task
  • Handle file uploads by saving to session directory
  • Implement message batching with debounce timer

No blockers or concerns.


Phase: 02-telegram-integration Completed: 2026-02-04