@meta
  v: 1
  route: /architecture
  generated: 2026-05-11T09:19:39.434Z
  ttl: 1h
  auth: session(anon:anon)

@intent
  purpose:    Concrete architecture: what processes run where, what ports they bind to, what files they read. Reflects the v2.x agentic harness on top of the v1.x dispatch substrate.
  audience:   developer, operator, auditor, ai-agent-operator
  capability: audit, integrate, extend

@state
  current_version: 2.6.2
  input_channels[5]{kind,name,path,runtime}:
    dashboard-chat,Web dashboard chat panel,browser → SSE → /api/master/* (proxied) → master supervisor loop,any active browser tab on http://127.0.0.1:8787
    phone,Telegram bot,long-poll getUpdates → master telegram_listener → supervisor loop; reply auto-relays back to Telegram,user-owned Telegram bot (BotFather)
    human-cli,CLI prompt,exec subctl master prompt <text> → HTTP POST → supervisor loop,interactive shell on the host
    scheduled,Scheduled self-prompts,schedule_followup tool → followups.jsonl → ticker fires → supervisor loop,internal — persists across daemon restart
    worker-inbox,Dev-team inbox events,worker writes inbox.jsonl → master picks up → supervisor loop,any active dev-team tmux session
  master_daemon:
    name: subctl master
    runtime: Bun (single persistent process)
    service_manager: launchd (~/Library/LaunchAgents/com.subctl.master.plist)
    keep_alive{SuccessfulExit,Crashed}: false,true
    throttle_interval_s: 30
    exit_timeout_s: 20
    default_supervisor: LM Studio local (qwen3-coder-30b recommended)
    escalation_supervisors[2]: Anthropic Opus 4.7, Anthropic Sonnet 4.6
    tools_total: 50+
    tool_families: 13
    verifier: "Argent-style runtime claim verifier, 2 corrections per turn cap"
    watchdog{tick_s,stale_threshold_s}: 180,900
    auto_compact{tick_s,context_threshold_pct}: 300,90
    memory_tier_1_budget_chars: 3500
  dashboard_service:
    name: subctl dashboard
    runtime: Bun static + SSE proxy (no build step)
    bind: 127.0.0.1:8787
    protocols[2]: HTTP, SSE
    memory_typical_mb: 50
    service_manager: launchd (~/Library/LaunchAgents/com.subctl.dashboard.plist)
    readonly_over_accounts: true
    auto_proxy: /api/master/* → http://127.0.0.1:<master-port>/
    tabs[13]: Chat, Orchestration, Dashboard, Projects, Teams, Claude Sessions, Models, Providers, Memory, Vault, Skills, Live Logs, Settings
  data_paths{accounts,config,accounts_conf,inbox,notify_creds,rate_limit_log,decisions_log,followups,tier_1_memory,tier_3_vault,logs,tmux_socket,mcp_settings,skills}: ~/.claude-<alias>/  and  ~/.codex-<alias>/,~/.config/subctl/,~/.config/subctl/accounts.conf,~/.config/subctl/inbox.jsonl,~/.config/subctl/notify.json,~/.claude/rate-limit-events.log,~/.config/subctl/decisions.jsonl,~/.config/subctl/followups.jsonl,~/.config/subctl/user.md  +  ~/.config/subctl/memory.md,~/Obsidian/<vault>/ (configurable),~/Library/Logs/subctl/,default tmux server,~/.claude/settings.json (mcpServers.subctl),"~/.claude/skills/{subctl,autonomy,orchestrator-mode}/ — symlinked into every Claude config dir"
  http_endpoints_dashboard[10]: GET  /             (dashboard SPA), GET  /help         (full reference), GET  /cheat        (cheat sheet), GET  /api/state, GET  /api/sessions, GET  /api/orchestration, POST /api/orchestration/spawn, POST /api/orchestration/:name/msg, POST /api/orchestration/:name/kill, GET  /sse/events   (live broadcast stream)
  http_endpoints_master[6]: GET  /api/master/state, POST /api/master/prompt, GET  /api/master/transcript, POST /api/master/personality, GET  /api/master/personality/list, POST /api/master/restart
  cli_verbs[13]: subctl, subctl status, subctl doctor, subctl auth <provider> <alias>, subctl use <alias>, subctl one-off <alias> <task>, subctl teams claude -a <alias> -c <cwd> -o, subctl orch <list|status|msg|kill>, subctl master <enable|disable|status|restart|kick|prompt|personality>, subctl notify <send|ask-yesno|ask-choice|inbox|inbox-ack>, subctl install, subctl update           # safe across local-only branches (v2.6.2), subctl uninstall
  process_topology:
    single_master_bun_process_includes[8]: supervisor loop (LM Studio call), tool registry (50+ tools), verifier, watchdog ticker (3 min), auto-compact ticker (5 min), scheduler, Telegram long-poll listener, SSE broadcaster
    single_dashboard_bun_process_includes[4]: static file serving, SSE proxy to master, filesystem watchers, SSE event aggregator
    single_tmux_server_includes[4]: claude-Foothold-v3, claude-marketing-site, codex-eval-loop, <user-spawned dev teams>
  extension_model{provider_plugin,master_skill,worker_skills,personalities,tools}: "providers/<name>/{auth.sh, signals.sh, teams.sh}",components/skills/master/SKILL.md (master's own system prompt),"components/skills/{subctl,autonomy,orchestrator-mode}/SKILL.md (loaded into every Claude config dir)",components/master/personalities/<preset>.md,"components/master/tools/<family>/<tool>.ts (50+ tools, hot-discoverable)"

@actions
  - id: read_master_doc
    method: GET
    href: https://github.com/webdevtodayjason/subctl/blob/main/docs/master.md
  - id: view_dashboard_source
    method: GET
    href: https://github.com/webdevtodayjason/subctl/blob/main/dashboard/server.ts
  - id: view_master_server
    method: GET
    href: https://github.com/webdevtodayjason/subctl/blob/main/components/master/server.ts
  - id: back_to_root
    method: GET
    href: /.agent

@context
  > subctl is two persistent Bun processes (master + dashboard) plus N tmux dev-team workers. The master holds the conversational supervisor loop and ~all tool surface. The dashboard is mostly a render layer + SSE proxy. Both run under launchd with conditional KeepAlive (Crashed only, not SuccessfulExit) so a clean shutdown stays down. Dead tmux sessions are pruned from the watchdog activity map (v2.5.7 fix) so the watchdog doesn't fire on sessions you've already closed.

@nav
  self:      /architecture.agent
  parents:   [/.agent]
  peers:     [/features.agent, /install.agent, /roadmap.agent]
