Skip to content

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() reading data/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 -c snippet: 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 via systemctl show, last action + age from context JSONL tail, today's calls/median-latency/success from data/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 of data/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 from data/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 -3 shows 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