ask-forum: promote to a full skill + wire into skill-first routing

Add .claude/skills/ask-forum/SKILL.md (usage contract, correlation model,
forum-only scope, access/permissions) and route to it from CLAUDE.md
(skill-first covered domains) + SKILL_ROUTING.md, so sessions invoke the
skill instead of hand-rolling the Discord API — the footgun that produced a
broken background wait. Capture that footgun as memory
feedback_background_task_no_ampersand (run_in_background, never a shell &).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-07 20:12:18 -07:00
parent 8def189a17
commit 9340cdec17
6 changed files with 126 additions and 2 deletions

View File

@@ -0,0 +1,92 @@
---
name: ask-forum
description: "Ask a teammate (Mike/Winter/Howard/anyone with forum access) a question in the private #ct-forum Discord forum and get their human answer back IN THIS SAME SESSION, then act on it - a live three-way between the user, this Claude session, and the teammate. No second bot, no DMs, one tool call (the wait runs server-side in bash, zero tokens while waiting). Triggers: ask Mike/Winter in the forum, ask the team, post a question to ct-forum, get a human answer/decision/sign-off, human-in-the-loop, run something by <person>, wait for their reply."
---
# ask-forum — human-in-the-loop questions via #ct-forum
Thin wrapper over `.claude/scripts/ask-forum.sh`. Lets THIS session put a question to a
human in the private **#ct-forum** Discord forum and receive their answer back in-session,
so it can keep going. Use it whenever you need a person's answer, decision, or sign-off
mid-task and want it to flow back to the running session — instead of stopping and asking
the user to relay it.
**USE THIS SKILL — do not hand-roll `curl` against the Discord API or the bot token.**
Free-handing the raw call is exactly what produced the `&`-on-background bug (a wait that
exited instantly and never delivered the answer). The script + this contract encode the
correct thread-create, the non-bot reply filter, and the background-wait discipline. Reach
for raw API only if this skill genuinely can't do what's needed — and say so.
## When to use
- You need a teammate's **answer / decision / approval** and want it back in this session
(e.g. "ask Mike whether to deploy", "run this naming by Winter", "get a go/no-go").
- A back-and-forth where the human's reply should drive the session's next action.
Not for: one-way copy-paste delivery of a link/command (that's `discord-dm``mike`), or
routine `[SYNCRO]`/`[RMM]` status alerts (`post-bot-alert.sh`).
## Usage
```bash
# Ask + BLOCK until a human replies, then act on the answer:
bash .claude/scripts/ask-forum.sh "your question" --tag @264814939619721216
# Re-read a thread you already asked in (one-shot, no waiting):
bash .claude/scripts/ask-forum.sh --read <thread_id>
# Resume blocking on an already-posted thread (e.g. after a timeout):
bash .claude/scripts/ask-forum.sh --wait <thread_id> --timeout 1800 --poll 10
```
Flags: `--title "short title"` (forum post name; defaults to first 60 chars of the
question) · `--timeout SEC` (default 600) · `--poll SEC` (default 8) · `--tag @<id>`
(repeatable — pings that person). Discord IDs: mike `264814939619721216`,
howard `624667664501178379`, winter `624666486362996755`, rob `261978810713505792`
(also in `.claude/users.json`).
## HARD RULE — long waits run as a proper background task (no shell `&`)
A blocking `--wait`/ask can sit for many minutes. Run it with the tool's
**`run_in_background: true`** and **nothing else** — do NOT append a shell `&`, `disown`,
or `nohup`. Adding `&` forks the wait off and the shell exits 0 immediately, orphaning the
poll loop so the answer is never delivered (logged friction, twice). Done right, the wait
costs **zero tokens** while pending and the harness notifies you the instant a human
replies — then you surface the answer to the user **unprompted**. See
[[feedback_background_task_no_ampersand]].
## How it works / correlation
- Posts a forum post via `POST /channels/<forum>/threads`, returns a **`thread_id`**.
- The session reads only **its own** `thread_id`, so an answer can never be confused with
another question's. Replies route to whoever asked — Mike's thread → Mike's session,
yours → yours, no crossover.
- **Any non-bot reply counts as the answer** (first human wins). Anyone with #ct-forum
access can answer; the script never filters by person. Bot chatter is filtered out.
## Model & scope (decided with Mike, 2026-07-08)
- **Forum-only.** No DM replies — answers stay team-visible.
- **Good-enough durability:** answers are captured while the session is open; Discord keeps
the history, so any thread is cheap to re-read (`--read`) later in the same session. It is
NOT restart-safe (close the session → nothing is watching); that was an accepted tradeoff
for the cheapest option. Full restart/close-safe capture would be the coord-backed
registry or the event-driven bot-router — build only if asked.
- The **BEAST bot ignores #ct-forum** (patch in `projects/discord-bot/bot/main.py`) so it
doesn't auto-answer and collide with the asking session.
## Access & permissions
- **Channel:** #ct-forum (`1522960388432465950`), private — `@everyone` View DENY; Howard +
the bot explicitly allowed; Mike sees it as owner. To let someone else answer, they need
**View Channel** on #ct-forum (grant a role like Techs, or the person). Winter/Rob are not
in by default.
- **Deleting threads** needs the bot to have **Manage Threads** on #ct-forum (owner grants
it). Without it, `DELETE /channels/<thread>` returns 403.
## Exit codes
`0` answered · `1` usage · `2` no token · `3` Discord API error · `4` timeout (thread stays
open — resume with `--wait <thread_id>`).
## Implementation notes
- Bot token resolves from the SOPS vault
(`projects/discord-bot/bot-token.sops.yaml` field `credentials.bot_token`), falling back
to `projects/discord-bot/.env` `DISCORD_TOKEN` — works from any machine.
- Engine: `.claude/scripts/ask-forum.sh`. Command entry point: `/ask-forum`. Related:
[[discord-dm]] (person-targeted DMs / channel posts).