Plan: bots.justinsforge.com, bot fleet + automations observability dashboard¶
URL: https://mkdocs.justinsforge.com/memory/plans/bots-dashboard-2026-07-14/
Date: 2026-07-14
Approved spec: Live observability and control surface at bots.justinsforge.com. New Flask service forge-bots-dashboard.service on Console port 8110, tunneled behind Cloudflare Access (Justin Secure Only). Bots page: one card per bot (capture, coordinator, general-purpose, notify-bot, remote-bridge) with systemd state, restarts, last action, today's calls/latency/success from the quota JSONL, brain-server health, watchdog status; card click opens a detail view with a live activity feed parsed from that persona's context JSONL. Tool catalog per brain persona (capture, coordinator, general) enumerated live from the brain registry, with real on/off toggles: writes data/bot_tool_overrides.json atomically; _filter_tools() / _filter_handlers() in forge_telegram_brain.py honor it at call time (effect next message, no restart, fail-open on bad file with loud log). Automations page: every forge-* systemd service and timer with state and last/next run, failed units flagged, unresolved monitor alerts strip. Vanilla HTML/CSS/JS chrome borrowing APP-PLAYBOOK sensibilities (cards, side peek, designed empty states, in-app modals only). Auto-refresh ~5s.
Out of scope: - n8n workflow visibility - Other fleet hosts (Finn CTs); Console only - Service restart/stop buttons - Persona / prompt editing - Historical analytics beyond the current quota month - Toggles for remote-bridge and notify-bot (bridge brain is a separate module; notify has no LLM). Their tools display read-only.
Task list¶
Task 1: Add runtime tool-override layer to the telegram brain¶
- Files:
/home/justinwieb/forge/scripts/forge_telegram_brain.py,/home/justinwieb/forge/.gitignore - What: New
_load_tool_overrides()readingdata/bot_tool_overrides.json(shape{"<persona>": {"disabled": ["tool_name", ...]}}), mtime-cached, fail-open (missing/corrupt file = all tools enabled, log at ERROR). Apply the disabled set in_filter_tools()and_filter_handlers()after the persona filter. Gitignore the overrides file (runtime state). - Verification:
python3 -csnippet: write a temp override disabling one capture tool, import brain, assert the tool is absent from_filter_tools("capture")and present again after removing the file. - Commit:
feat(bots): runtime per-persona tool overrides in telegram brain
Task 2: Dashboard backend (Flask, port 8110)¶
- Files:
/home/justinwieb/forge/scripts/forge_bots_dashboard.py - What: Flask app in the fitness-app pattern (system python3, bind 127.0.0.1:8110). Endpoints:
GET /api/fleet(per-bot systemd state/restarts/since viasystemctl show, last action + age from context JSONL tail, today's calls/median-latency/success fromdata/claude-quota/<YYYY-MM>.jsonl, brain-server/health, watchdog last line),GET /api/bot/<persona>/feed?limit=50(parsed context JSONL entries, newest first),GET /api/tools(live enumeration from brain registry per persona plus current override state; bridge/notify listed read-only),POST /api/tools/toggle{persona, tool, enabled}(atomic temp+rename write ofdata/bot_tool_overrides.json, validates persona/tool against the live registry),GET /api/automations(all forge-* units + timers via systemctl, last/next run, failed flag),GET /api/alerts(unresolved monitor alerts fromdata/forge-monitor-state.json/data/forge-monitor-alerts/). All responses{ok, ...}or{ok: false, error}with honest status codes; every except re-raises or returns a logged error payload. - Verification:
python3 scripts/forge_bots_dashboard.py & sleep 2 && curl -fsS localhost:8110/api/fleet | jq .ok && curl -fsS localhost:8110/api/tools | jq .ok && curl -fsS localhost:8110/api/automations | jq .ok - Commit:
feat(bots): bots dashboard backend, fleet/tools/automations/alerts API
Task 3: Frontend (single-file HTML served by the app)¶
- Files:
/home/justinwieb/forge/scripts/forge_bots_dashboard.html - What: Single-file page (inline CSS/JS, fitness-dashboard pattern): header with home button to justinsforge.com; Bots view with per-bot cards; side-peek detail with activity feed; per-persona tool catalog with toggle switches (optimistic update, toast on failure, in-app modal for confirm when disabling a tool, never window.confirm); Automations view (services + timers table, failed rows flagged, alerts strip). 5s polling refresh. Designed empty states.
- Verification:
curl -fsS localhost:8110/ | grep -q bots-dashboard && echo OK; then/screenshot http://localhost:8110/desktop + mobile. - Commit:
feat(bots): bots dashboard UI, cards + side peek + tool toggles + automations
Task 4: systemd unit¶
- Files:
/home/justinwieb/forge/infra/systemd/forge-bots-dashboard.service, installed copy at/etc/systemd/system/forge-bots-dashboard.service - What: Type=simple, User=justinwieb, WorkingDirectory=/home/justinwieb/forge, ExecStart=/usr/bin/python3 scripts/forge_bots_dashboard.py, Restart=on-failure, modeled on forge-fitness-app.service. Enable + start.
- Verification:
systemctl is-active forge-bots-dashboard.service && curl -fsS localhost:8110/api/fleet | jq .ok - Commit:
feat(bots): forge-bots-dashboard systemd unit
Task 5: Expose at bots.justinsforge.com behind Cloudflare Access¶
- Files: none in repo (tunnel config + CF API); home tile addition per feedback_new_subdomain_links_home in the justinsforge home dashboard source
- What:
/cf-add bots justinsforge.com http://localhost:8110, create Cloudflare Access app "Forge Bots" with the Justin Secure Only reusable policy (parity with every other subdomain). Add a Bots tile to the home dashboard. - Verification:
curl -sI https://bots.justinsforge.com | head -3shows the CF Access redirect (302 to cloudflareaccess.com), not the raw app. - Commit:
feat(bots): home tile for bots.justinsforge.com(tunnel/Access changes are external state)
Task 6: Register and document¶
- Files:
/home/justinwieb/forge/MEMORY.md,/home/justinwieb/forge/memory/general/reference_bots_dashboard.md - What: Topic file (service, port, endpoints, override file contract, fail-open semantics, Access app), one-line MEMORY.md index entry, run
bash scripts/forge_eval_run.sh. - Verification:
bash scripts/forge_eval_run.sh - Commit:
docs(bots): register bots dashboard in memory index