HQ Inbox: Interrupts + Idle Autopilot

Each HQ in aide gets its own inbox queue under ~/.aide/hq-queue/<hq-name>/. The queue exists so the aide binary can unify three kinds of incoming events into one stream that the HQ chat session (or the daemon, when nobody's home) can consume:

  1. GitHub issues opened against the HQ repo.
  2. Member-agent completions (local tasks routed by [router] finishing).
  3. Manual / external pushes from the CLI (aide hq-inbox push ...).

Two consumers attach to the same queue:

  • Online (you're chatting with the HQ): the HQ Claude session has a long-running Monitor watching aide hq-inbox watch <hq>. When a new event arrives mid-conversation, Claude summarizes it and asks how you want to handle it.
  • Offline (chat idle ≥ 5 min): the daemon's auto_handle kicks in and routes the inbox via the existing [router] selector (bandit / round-robin / fanout) without waiting for you.

Layout

~/.aide/hq-queue/<hq>/
  inbox/        pending JSON files, one per inbound message (FIFO)
  processing/   atomic move from inbox during handling
  done/         finished, with result/summary/acked_at appended
  last_activity touched by `aide hq-touch <hq>` (mtime tracked)

Each inbox file is JSON:

{
  "id": "<nanos-based id>",
  "ts": "<RFC3339>",
  "source": "github-issue|member-completed|cli|other",
  "title": "<short>",
  "body": "<full>",
  "context": { "...arbitrary..." }
}

Bootstrap an HQ

The fastest way to get a new HQ wired up:

aide hq-init ~/projects/my-hq
aide register ~/projects/my-hq

aide hq-init does three things:

  1. Ensures Aidefile has a [router] block (creates the file if missing).
  2. Writes/merges .claude/settings.local.json to add Claude Code hooks (SessionStart and UserPromptSubmit) that call aide hq-touch <hq> — so every user turn refreshes the activity marker.
  3. Appends an "HQ inbox protocol" section to CLAUDE.md instructing the Claude session about the watcher pattern below.

Online pattern (Monitor + interrupt)

In the HQ project, attach a long-running Monitor to the inbox watcher:

/monitor aide hq-inbox watch my-hq

watch polls the inbox dir every 2s and prints one JSON line per new file:

{"id": "172...", "source": "github-issue", "title": "fix the thing", "ts": "..."}

Each line is a Monitor notification. The CLAUDE.md protocol tells the model:

  1. Summarize the new item for you in one sentence.
  2. Ask how to handle it (route to a member, defer, ignore).
  3. After you decide, dispatch via:
    aide dispatch --auto --local "<task>"
    aide hq-inbox ack my-hq <id> --summary "<one-line>" --success
    

Offline pattern (idle autopilot)

If you don't reply within [router].auto_idle_secs (default 300 = 5 min), the daemon calls auto_handle on its next tick. auto_handle:

  • Checks last_activity mtime — if newer than the threshold, no-op.
  • Otherwise, drains the inbox in FIFO order, routing each item via router::route_issue using the same selector you've configured (bandit, round-robin, or fanout).
  • Acks each item with the route outcome.

Tune the threshold per HQ via auto_idle_secs:

[router]
mode = "bandit"
auto_idle_secs = 600   # 10 min instead of default 5 min

CLI reference

aide hq-inbox push <hq> --source <s> --title <t> [--body <b>] [--context <json>]

Write a new inbox JSON file. Body reads from stdin if --body omitted.

aide hq-inbox push my-hq --source cli --title "manual ping" --body "do the thing"

aide hq-inbox ls <hq> [--state inbox|processing|done] [--limit N]

List files in the chosen state directory, newest first. Default state is inbox.

aide hq-inbox pop <hq>

Atomically moves the oldest inbox file → processing/, prints the full JSON to stdout. Returns exit 0 with empty stdout if nothing pending.

aide hq-inbox ack <hq> <id> [--summary <s>] [--success]

Atomically moves processing/<id>.jsondone/<id>.json with appended result + summary + acked_at fields.

aide hq-inbox watch <hq> — long-running

Tails the inbox directory and prints one JSON line per new file (line-buffered, Monitor-friendly). Runs forever until SIGTERM.

aide hq-inbox auto-handle <hq> [--max-idle 5m]

If last_activity mtime > --max-idle ago AND inbox non-empty: drains via router::route_issue and acks each. Otherwise no-op. Prints exactly one summary line.

aide hq-touch <hq>

Touches ~/.aide/hq-queue/<hq>/last_activity (creates dir + file if missing). Used by Claude Code's UserPromptSubmit hook so each user turn extends the idle window.

aide hq-init <hq-dir>

Bootstrap helper described above.

Recovery

If the HQ chat session crashes mid-processing, files in processing/ are left in place. On restart, you can:

aide hq-inbox ls my-hq --state processing
# Inspect or move them back manually:
mv ~/.aide/hq-queue/my-hq/processing/<id>.json ~/.aide/hq-queue/my-hq/inbox/

auto_handle will pick them up on the next idle window.

Sources fed by aide-binary

SourceProducer
github-issuerouter::route_issue after dispatching to a member
member-completeddispatch::run_local_task finalize when routed_from_hq is set
cliaide hq-inbox push <hq> ...