---
name: coo-inbox-poll
description: Polls _System/COO/Inbox.md every 5 minutes — classifies new requests, dispatches or executes, appends Result block.
agent: hermes-coo
schedule: "*/5 * * * *"
context_files:
  - HQ-Vault/_System/COO/Inbox.md
  - HQ-Vault/_System/COO/safety-rails.md
  - HQ-Vault/_System/COO/state.json
---

# coo-inbox-poll skill

## Behaviour

### Step 1 — read

1. Read `_System/COO/Inbox.md`.
2. Find all `### REQ-{...}` blocks under `## Open requests` that DO NOT have a `### Result —` child block.
3. Order oldest-first.

If no unresolved REQs, exit silently (no log spam).

### Step 2 — for each unresolved REQ

1. **Lock check** — `state.json.lock_holders[REQ-id]` exists?
   - Yes → another tick is working it, skip.
   - No → set lock: `state.json.lock_holders[REQ-id] = {ts, ttl: 300}`.
2. **Classify** — produce `{complexity, safety_class, route}`:
   - `complexity`: light (<5 min, <3 tool calls) | medium | heavy
   - `safety_class`: ok | greylist-confirmed | blocked (per `safety-rails.md`)
   - `route`: direct | kanban:{lane} | cc-cli | escalate
3. **Safety check** — if `safety_class == blocked`:
   - Append `### Result — {ts}` with `Closed: blocked`
   - Append to `Outbox.md` under `## Blocked actions`
   - Telegram-escalate
   - Release lock, continue to next REQ
4. **Greylist check** — if `safety_class == greylist`:
   - Telegram-ask Rocky with 5-min window
   - On yes → proceed
   - On no → close REQ with `Closed: declined by Rocky`
   - On timeout → close REQ with `Closed: timeout, no Rocky reply`
5. **Execute or dispatch:**
   - `route == direct`: run inline, capture output
   - `route == kanban:{lane}`: create Hermes Kanban task: `hermes --profile coo kanban create --board {lane} --assignee hermes-{lane} --title "{slug}" --body "{REQ body}"`. Capture task ID.
   - `route == cc-cli`: spawn Claude Code via SSH: `claude -p "{prompt}" --output-format json --max-budget-usd 1.00`. Capture session ID.
   - `route == escalate`: append to `Outbox.md` under `## Decisions needed`, Telegram-notify.
6. **Append Result block** to the REQ in `Inbox.md`:
   ```
   ### Result — {ts}
   - Classified: complexity={c}, safety={s}, route={r}
   - Action: {what was done}
   - Output: {link or summary or task-id}
   - Closed: {ts if done, else "awaiting-{kanban-task-id|cc-session-id|rocky-confirm}"}
   ```
7. **Log:**
   - Append to `dispatch-log.ndjson`
   - Append to `_AgentLog/hermes-coo-{today}.md`
8. **Release lock.**

### Step 3 — cleanup

Once per day (e.g. last tick of the day), move REQs where the most recent `### Result` has `Closed: {timestamp}` (not "awaiting-...") AND is older than 7 days, to `_Archive/Inbox-{YYYY-Wxx}.md`. Use git mv-style — preserve full content + add archived-on timestamp at top.

## Classification cheatsheet

| Lane | Keywords / patterns | Default route |
|---|---|---|
| `rr` | hunt, scraper, worker, contractor, supabase memscjot..., scraped_leads, RateRight, $50 hire | kanban:rr if mutating, direct if read-only query |
| `lfcs` | Airtable LFCS, bid, RFI, drawing, BoQ, pricing, Liam, TCE, MWS, job number 2xxx | kanban:lfcs if mutating, direct if read-only |
| `siteops` | site induction, ITP, RAMS, SWMS, white card | kanban:siteops |
| `personal` | gym, energy, schedule, Rocky-only | direct |
| `system` | vault, sync, cron, hermes config, MCP | direct (light) or escalate (heavy) |

| Type | Default route |
|---|---|
| question | direct (answer + cite sources) |
| task (mutating) | kanban:{lane} OR greylist-then-direct |
| decision | escalate |
| escalation | escalate immediately |

## Failure modes

- REQ malformed (missing required fields) → reply in Result block: "REQ malformed — need {field}", do not close
- Classification ambiguous → default to escalate; don't guess
- Kanban dispatch fails → retry once, then escalate
- Lock TTL expired with no result → assume crashed prior tick, restart classification
