Telegram Fleet, Pure Phoenix Phase 4.2 + 4.2.5¶
Doctrine: FORGE-DOCTRINE.md Sections 3, 5, 6, 7. Handoff: pure-phoenix-phase-4-2-bot-redesign-2026-04-28.
The fleet¶
| Bot | Role | Service | Brain persona | Tool count |
|---|---|---|---|---|
@forge_inbox_capture_bot |
low-friction capture, voice + text | forge-inbox-capture.service |
capture | 16 |
@forge_lifeos_coordinator_bot |
deep synthesis, full tool surface | forge-lifeos-coordinator.service |
coordinator | 50 |
@forge_notify_outbound_bot |
push (notify.sh, heartbeat, morning-brief, nudge-fire) + inbound slash commands (/help, /status, /wellness, /quota) |
forge-notify-bot.service |
n/a (no LLM, dispatcher only) | n/a |
@forge_remote_bridge_bot |
spawn-by-default phone-to-tmux on-ramp; brain routes plain text to spawn / kill / propose_kill / reply | forge-remote-bridge.service |
bridge (separate, see reference_remote_bridge_bot) | 4 (spawn, kill, propose_kill, reply) |
All 4 bots use the same UX: 👀 on receive + typing indicator, then 👌 on success or 🤔 on failure. Reply text (when a tool ran) starts with a labeled action prefix like Food Item Logged:, Calendar Event Created:, Task Created:, Session Spawned:, Failed: <tool>. See feedback_telegram_bot_ux_reactions.
Plus forge-inbox-webhook.service on port 7400 for the iOS Shortcut (calls capture persona).
Tokens¶
~/.forge-secrets/telegram-{inbox-capture,lifeos-coordinator,notify-outbound}.env (chmod 600).
Each env file:
TELEGRAM_BOT_TOKEN=...
TELEGRAM_BOT_USERNAME=...
TELEGRAM_AUTHORIZED_USER_ID=7110798439 (Justin's Telegram user id; doubles as DM chat_id)
TELEGRAM_CHAT_ID=7110798439 (push helper reads this)
Brain layer¶
forge/scripts/forge_telegram_brain.py is the single brain module. Persona param selects:
- system prompt (capture: "low-friction only, route heavy work to coordinator"; coordinator: "synthesis + spawn_worker for big jobs")
- tool subset (capture filters TOOLS down to 7 names; coordinator gets all 28)
- context log file (data/capture-context.jsonl vs data/coordinator-context.jsonl)
- per-call quota record via forge_quota_tracker.record(...)
The 1120-line forge_telegram_inbox_brain.py remains as the implementation backend (tool functions, n8n helper, Notion DB IDs). The new brain module is a persona-aware facade that imports from it.
Capture persona tool surface¶
save_to_inbox, create_task, save_knowledge, log_habit,
schedule_nudge, wellness_now, create_calendar_event
Heavy tools (Gmail, Notion query/update, calendar update/delete, spawn_worker) are deliberately omitted. Capture is single-shot; capture brain refuses multi-step work and routes it to coordinator via the prompt.
Coordinator persona tool surface¶
All 29 tools, including spawn_remote_session(name, model, prompt) which shells out to forge/scripts/forge_spawn_session.sh (canonical extraction of the /spawn skill bash) and returns a claude.ai/code URL. Added 2026-04-29.
Required: the forge-lifeos-coordinator.service unit MUST NOT have PrivateTmp=true, or the spawned tmux session lands in an isolated namespace and Justin can't reach it. Removed 2026-04-29; see LESSONS.md.
Notify bot inbound (slash commands, added 2026-04-29)¶
forge/scripts/forge_telegram_notify_bot.py polls @forge_notify_outbound_bot for inbound messages. Slash-command framework with @cmd(name, description) decorator; adding a new command is a single function. /help auto-lists every registered command. Anything not starting with / gets routed back to coordinator/capture with a hint.
Initial commands:
- /help - list available commands
- /status - service health snapshot for the bot fleet
- /wellness - calls brain_core.tool_wellness_now()
- /quota - calls forge_quota_tracker.aggregate()
Service: forge-notify-bot.service, unit at /etc/systemd/system/forge-notify-bot.service. Logs at forge/logs/telegram-notify-bot.log.
Push channel¶
forge_telegram_push.sh selectors after Phase 4.2:
- capture, coordinator, notify are canonical
- legacy inbox, ava, manager, updates, chat map to notify for backwards compatibility during cutover
forge_notify.sh send_manager fn reads ~/.forge-secrets/telegram-notify-outbound.env.
forge_telegram_nudge_fire.py, morning-brief.py, heartbeat.py all push via forge_telegram_push.sh notify ....
Quota tracker¶
forge/scripts/forge_quota_tracker.py:
- record(invoker, model, prompt_chars, response_chars, latency_ms, success, extra=None) - append-only JSONL at forge/data/claude-quota/<YYYY-MM>.jsonl
- aggregate(month) - per-invoker daily + total summary
- merge_with_stats_cache(summary) - pulls ~/.claude/stats-cache.json (Claude Code interactive sessions) into the same view
- CLI: forge_quota_tracker.py [--month YYYY-MM] [--write-summary]
Foundation for Phase 4.7 (system-wide quota observability across all 9 forge scripts that call claude -p).
Operational notes¶
- Voice transcription via
forge_telegram_transcribe.py(faster-whisper base.en CPU int8). Both capture and coordinator bots use it. - Each bot enforces single-authorized-user via
TELEGRAM_AUTHORIZED_USER_ID. - Both long-pollers run under
~/.forge-venvs/inbox/bin/python(whisper venv with requests).
Retired (do not reuse)¶
| Old bot | Service | Status |
|---|---|---|
@jw_inbox_bot |
forge-telegram-inbox.service |
unit retired, file archived to memory/_archive/cron-pre-phoenix-2026-04-28/. Token still active until Justin deletes via @BotFather. |
@Ava_JForgeBot |
forge-telegram-ava.service |
same |
@Manager_JForgeBot |
n/a | push-only, no service. Token superseded by notify-outbound. |
@jw_updates_bot |
n/a | push-only, merged into notify-outbound. |
Justin's manual final step: delete the four old bots via @BotFather and revoke their tokens.
Pending work¶
- Phase 4.7: instrument the other 7 forge scripts that call
claude -p(heartbeat, morning-brief, dispatcher, etc.) withforge_quota_tracker.record(...)so the aggregator covers the whole subscription footprint. - @BotFather deletion of retired bots (Justin owns this).
- After 7-day clean operation: tighten
naming-taxonomy-serviceseval check from warning to error severity.
[Claude Code, Pure Phoenix Phase 4.2]