discord-bot: fix "no response", serialize turns, attribution, mentions, post-at-bottom
client.py: send() falls back to ResultMessage.result when no TextBlock streams (the "(no response)" bug) and reconnects+retries once on a closed SDK session. message_handler.py: per-thread turn lock so messages arriving mid-turn or from a second user queue in order (nothing dropped); per-session requester-attribution env (discord_id -> users.json key), pinned to the thread opener; _USER_MAP caches only on a successful load; final answer posts as a fresh message at the BOTTOM (no edit-in-place); a <@id> tag goes out as a fresh send so it actually pings. main.py: allowed_mentions permits user pings, blocks @everyone/@here/roles. DISCORD_CLAUDE.md: no thread auto-delete; tiered close-out (Q&A -> one-line rolling log, substantive -> /save); @mention guidance; opener-pinned attribution note. whoami-block.sh / sync.sh: bot-context attribution (Executed by ClaudeTools Bot / Requested by <person>; git author = mapped requester, committer = bot). Strict no-op for interactive sessions. users.json: discord_id for Mike/Howard; added Winter Williams (bot-only, full trust). Reviewed by Code Review Agent + Grok + Gemini (Gemini's "malformed email" finding verified as a false positive). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -98,34 +98,41 @@ For every request, work this loop:
|
||||
name, ID) to determine who is asking, and address them by name.
|
||||
2. **Do the work** — perform the action or answer the question. Ask clarifying questions in
|
||||
the thread as needed; the session persists, so the conversation continues naturally.
|
||||
3. **Anything else?** — when the task is done, ask: "Anything else for this one?" Keep
|
||||
handling follow-ups in the same thread until the requester is satisfied.
|
||||
4. **Offer Syncro** — once they have nothing else, ask whether to log the work in Syncro
|
||||
("Want me to log this in Syncro?"). If yes, invoke `/syncro` to create or update the ticket.
|
||||
5. **Save** — after the loop closes, run `/save` to write the session log and sync the repo.
|
||||
6. **Kill the thread** — after `/save` completes successfully, delete the thread:
|
||||
```bash
|
||||
bash C:/Users/guru/ClaudeTools/projects/discord-bot/scripts/delete-thread.sh <Thread ID>
|
||||
```
|
||||
The Thread ID is in every `[DISCORD_CONTEXT]` block as `Thread ID: <id>`. Do not delete
|
||||
if `/save` failed or errored. Do not post a closing message — the deletion is immediate.
|
||||
3. **Anything else?** — when the task is done, ask "Anything else for this one?" and keep
|
||||
handling follow-ups in the same thread. A directly-connected second topic stays in the
|
||||
SAME thread/session; only a genuinely unrelated request warrants a fresh thread.
|
||||
4. **Close the loop — match the capture to the work:**
|
||||
- **Pure Q&A / read-only / nothing changed in the repo** → do NOT run `/save`. Append a
|
||||
one-line entry to the rolling bot log
|
||||
`session-logs/bot/<YYYY-MM>/<YYYY-MM-DD>-bot-activity.md` (create the month folder if
|
||||
needed): `HH:MM PT - <requester> - <topic> - <outcome / links>`. The Discord thread holds
|
||||
the full detail, and the on-disk transcript is recoverable via `/recover` if a full
|
||||
narrative is ever needed. No Syncro prompt unless the work is billable.
|
||||
- **Substantive work** (changed a client record, infra, a ticket, or repo files) → offer
|
||||
Syncro if it is billable/ticketable, then run `/save` to write a full session log to the
|
||||
correct client/project location.
|
||||
5. **Sync** — `/save` already syncs. For the one-line rolling-log case a `/sync` is enough, and
|
||||
batching is fine (the periodic sync sweeps it up). Never push empty commits for pure Q&A.
|
||||
6. **Keep the thread** — never auto-delete. The thread is the conversation record. Delete only
|
||||
if the requester explicitly asks (`scripts/delete-thread.sh <Thread ID>`).
|
||||
|
||||
**Attribution is automatic — do not set it manually.** Each thread runs with session env
|
||||
(`CLAUDETOOLS_ACTOR=discord-bot`, `CLAUDETOOLS_REQUESTER`, `CLAUDETOOLS_REQUESTER_USER`) derived
|
||||
from the `[DISCORD_CONTEXT]` sender. So `/save`'s User block renders as "Executed by: ClaudeTools
|
||||
Discord Bot / Requested by: <them>", and commits are authored as the mapped requester with the
|
||||
bot as committer. Attribution **pins to the thread opener**: if a second person posts in someone
|
||||
else's thread, the work is still credited to whoever started the thread (a thread = one person's
|
||||
request).
|
||||
|
||||
---
|
||||
|
||||
## Thread Lifecycle — Auto-Delete on Completion
|
||||
## Thread Lifecycle — Threads Are Kept (No Auto-Delete)
|
||||
|
||||
Every `[DISCORD_CONTEXT]` block now includes `Thread ID: <id>` (injected by the bot after
|
||||
the thread is resolved or created). This ID is what you pass to the delete script.
|
||||
Threads are the durable conversation record and are **NOT auto-deleted** on completion.
|
||||
Leave every thread in place after the task and `/save` finish.
|
||||
|
||||
**When to delete:** Only after step 6 of the Task Loop — `/save` succeeded, session log
|
||||
committed and pushed. The thread is the conversation record; don't kill it before the log lands.
|
||||
|
||||
**When NOT to delete:**
|
||||
- `/save` failed or sync errored
|
||||
- The user said "yes" to continuing (open follow-up items remain)
|
||||
- The thread is an ongoing informational channel, not a single-task session
|
||||
|
||||
**Script:**
|
||||
**Delete ONLY on explicit request** — if the requester says to delete/close the thread,
|
||||
pass the `Thread ID` (in every `[DISCORD_CONTEXT]` block) to the delete script:
|
||||
```bash
|
||||
bash C:/Users/guru/ClaudeTools/projects/discord-bot/scripts/delete-thread.sh <Thread ID>
|
||||
```
|
||||
@@ -275,6 +282,21 @@ This creates an audit trail and keeps the repo in sync.
|
||||
|
||||
---
|
||||
|
||||
## Tagging a user (@mention)
|
||||
|
||||
To actually notify someone in Discord, emit a real mention `<@THEIR_DISCORD_ID>` — NOT
|
||||
literal "@name" text (plain "@winter" pings no one). Discord IDs live in `.claude/users.json`
|
||||
under each user's `discord_id`:
|
||||
|
||||
- Mike `<@264814939619721216>` - Howard `<@624667664501178379>` - Winter
|
||||
`<@624666486362996755>` - Rob `<@261978810713505792>`
|
||||
|
||||
Read `users.json` for any ID you do not have. Tag only when it serves the task (e.g. "this
|
||||
needs <@...>'s sign-off") — never gratuitously, and never `@everyone`/`@here` (the bot blocks
|
||||
those). A reply containing a tag is posted as a fresh message so the ping actually lands.
|
||||
|
||||
---
|
||||
|
||||
## Local Machine Rules (BEAST)
|
||||
|
||||
- Working directory: `C:/Users/guru/ClaudeTools`
|
||||
|
||||
Reference in New Issue
Block a user