Quick Start
1
Reactions only
2
Reactions + label fallback + custom thresholds
How It Works
The controller renders through a reaction OR a label, chosen from platform capabilities plus which callbacks you injected.on_phase(...) reports progress and resets the watchdog; tick(elapsed_s) flushes debounced phases and arms the stall signal.
Phases and stall states map to fixed emoji and labels.
Intermediate phases debounce by
debounce_ms (default 700); the latest pending phase wins and flushes on the next tick. Terminal phases (DONE/ERROR) render immediately and clear any pending phase or stall signal.
StallWatchdog standalone
StallWatchdog maps elapsed-since-progress seconds to a StallState and needs no controller.
Configuration Options
Every option comes straight fromRunStatusController.__init__.
The controller is
enabled only when enabled=True and at least one of set_status_reaction / edit_status_label is present.
Pick reaction vs label rendering per this decision flow.
RunStatusController also exposes phase (last phase passed to on_phase, possibly un-rendered) and watchdog (the underlying StallWatchdog, so you can reset() it on external progress signals).
User interaction flow
A user sends a message and watches one emoji move through the run without reading any reply text.Best Practices
Keep thresholds above your slowest legit tool
Keep thresholds above your slowest legit tool
If a real tool call takes 40s (web scraping, image generation), set
stall_soft_s to 45–60s so users don’t see ⏳ while everything is healthy.Let on_phase reset the watchdog for you
Let on_phase reset the watchdog for you
Any phase change is treated as progress and resets the stall watchdog automatically — you rarely need to call
watchdog.reset() yourself.Inject now= in tests
Inject now= in tests
Pass
now= a controllable monotonic clock to test debounce and stall behavior deterministically without real sleeps.Provide BOTH callbacks
Provide BOTH callbacks
Supply
set_status_reaction and edit_status_label so channels without reactions degrade to a label automatically, driven by caps.Related
Bot Status Reactions
The one-flag
TelegramBot(status_reactions=True) opt-inBot Platform Adapter
Build a custom channel adapter that wires this controller

