docs: runbook for a live agent-vs-agent Werewolf game in Slack - #809
docs: runbook for a live agent-vs-agent Werewolf game in Slack#809Poytr1 wants to merge 2 commits into
Conversation
Operationalizes the arena-validated message-only Werewolf (evals/games/werewolf.ts, collaboration-arena-baseline.md) for a real Slack workspace, with the wolves' den as a group DM (mpim) among the wolf agents and the referee agent. Key ground truths the runbook is built on, verified in code: - mpim scopes/events come from the one canonical manifest (SLACK_BOT_SCOPES / SLACK_BOT_EVENTS in packages/protocol/src/slack-app-manifest.ts) — every dedicated per-agent app, player or referee, uses the same set. - Group DMs are channel-like and mention-gated (isDm=false, isGroupDm; routing-table.ts), and verified agent-authored messages continue the thread via the ordinary ladder with the author excluded (#549) — which is what makes den negotiation self-propagating and a silent AC_NO_RESPONSE referee possible. - A live model referee is a bot, so unlike the arena's trusted-code referee its messages reset neither the loop-guard automatic counter nor hop depth — the runbook makes the human host post at every phase boundary to re-fund the budgets. - Operating limits stated as such: 7 players validated at real pace, 9 sequential speakers per human-funded round, MAX_AGENT_CALL_HOPS = 20. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Requesting changes for one runbook-blocking mismatch in the private-message choreography.
The guide's referee↔player 1:1 Slack-DM leg is not representable by the current AgentConnect sendMessage contract. A platform DM is exposed through toUser, which is explicitly restricted to human platform members (packages/daemon/src/mcp/tools.ts); toAgent without a channel is intentionally a postless/headless peer wake (docs/product-conventions.md and packages/daemon/src/mcp/ops.ts). Consequently, a referee following this guide has no supported operation that creates the claimed Slack DM with a player agent, so role delivery and the seer/doctor request-response loop do not execute as written. Please either rewrite the private leg around postless toAgent calls—with explicit needsReply/parent-session choreography and without calling those conversations Slack DMs—or add and smoke-test a supported agent-to-agent Slack-DM operation.
I also left a correctness note on the live sequential-speaker limit: because this referee is a bot, its discussion announcement consumes the first automatic admission after the host reset. The documented flow therefore supports eight sequential player speeches per boundary, not the arena's nine; seven players remains within the safe bound.
The remaining scope/event lists, mpim mention-gating description, human reset/latch behavior, and hop-limit guidance match the referenced code. git diff --check passes.
sent by review-bot (Codex · gpt-5.6-sol) · open in session
| with event subscriptions (`SLACK_BOT_EVENTS`) including `app_mention`, | ||
| `message.channels`, `message.groups`, `message.im`, and — required for the den — | ||
| `message.mpim`. The group-DM scopes are `mpim:history` + `mpim:read`; `im:write` | ||
| is what lets the referee open a 1:1 DM it has not received first |
There was a problem hiding this comment.
The referee cannot exercise this 1:1-DM path through the current AgentConnect tool contract. sendMessage exposes Slack DMs only through toUser, whose descriptor explicitly limits recipients to HUMAN members and forbids AgentConnect/bot identities (packages/daemon/src/mcp/tools.ts). The agent-target form without channel is deliberately postless/headless (packages/daemon/src/mcp/ops.ts), so it does not call openDirectMessage or create the Slack conversation described here. That leaves role delivery and the nightly seer/doctor reply loop without a supported route. Please either document those private exchanges as postless toAgent calls (including needsReply/parent-session handling), or add an actual agent-to-agent Slack-DM operation and cover it in Phase 0.
| | Limit | Value | Source | | ||
| | ------------------------------------------------- | ----------- | ----------------------------------------------------------------------------------------------------------- | | ||
| | Players validated at real pace | **7** | Arena baseline §5.3 (three full games, zero stalls) | | ||
| | Sequential speakers per host/referee-opened round | **9** | Arena baseline §6.5; `MAX_AUTOMATIC_TURNS_PER_WINDOW = 8` per 60 s window (`packages/daemon/src/daemon.ts`) | |
There was a problem hiding this comment.
Nine is the arena bound when the discussion-opening referee event is itself trusted human input and resets the counter. In this live script, the host resets the counter and then the model referee posts an automatic bot announcement. That announcement consumes count 1 for every player; with maxAutomatic = 8, player 8 can speak after seven predecessors, while player 9's wake trips at count 9. Please say 8 sequential player speakers for the documented live flow (or explain that 9 requires the human host to post the speaking-order announcement directly). The proposed seven-player game is still safe.
Review finding (PR #809): an agent cannot open a Slack 1:1 DM with another agent — sendMessage toUser is restricted to human platform members, and bare toAgent is a deliberately postless peer wake (packages/daemon/src/mcp/tools.ts, product-conventions "What sendMessage is for"). The referee↔player private leg is therefore rewritten as postless toAgent calls with explicit needsReply choreography, a private-leg smoke test is added to Phase 0, and the leak sweep now points at session transcripts for those exchanges. Also applies the reviewer's speaker-limit correction: the live referee's own discussion announcement consumes the first automatic admission after the host's reset, so the guide now states 8 sequential player speeches per host-funded round rather than the arena's 9. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
The previous review's two findings are addressed: the private leg now uses supported postless toAgent calls with needsReply, and the live sequential-player limit is correctly documented as eight.
One runbook-blocking state-flow gap remains. AgentConnect session identity includes channel and thread (sessionKey in packages/daemon/src/store/local-store.ts; docs/designs/session-concept.md §1.1), so the referee's host DM, wolves' den thread, and town thread are separate ACP/model sessions. In the documented choreography, the host-DM session receives the canaries, the den session observes the wolf kill, and the town session receives seer/doctor replies through needsReply and is later asked to resolve day. There is no supported handoff or shared-state step connecting those sessions. The town referee therefore cannot reliably embed the DM-provided canaries during role assignment or combine the den kill with the doctor's protection when resolving the night.
Please add an explicit, supported state-transfer mechanism and smoke-test it across these surfaces (or redesign the referee flow so all authoritative facts meet in one session). Relying on one model identity to remember across conversation-scoped sessions is not sufficient. The rest of the synchronized changes look consistent, and git diff --check passes.
sent by review-bot (Codex · gpt-5.6-sol) · open in session
| > NIGHT. When the host announces night: in the den, prompt the wolves — "NIGHT | ||
| > N. Talk here and agree on tonight's victim. When you have agreed, ONE of you | ||
| > says it plainly, e.g. 'we kill player-3'. The first clear statement of a | ||
| > valid target is the pack's choice." — mentioning each living wolf by name. | ||
| > Privately call the seer and the doctor with their night prompts, with | ||
| > `needsReply` set. Then OBSERVE. In the den, after your prompt, do not | ||
| > respond to the negotiation at all; record the first clear statement of a | ||
| > valid living non-wolf target as the kill. In every conversation, when a | ||
| > message needs no reply from you, stay silent. | ||
| > | ||
| > DAY. When the host announces day: resolve the night (a protected victim |
There was a problem hiding this comment.
The session that records the kill here is keyed to the den's channel/thread, but the later Day N breaks message activates the referee's separate town channel/thread session. sessionKey includes (platform, channel, thread, agentId, transportScope), so those sessions do not share live model context. Meanwhile, needsReply returns the seer/doctor actions to the town origin session, leaving the kill only in the den session; the canaries supplied in the host DM are in a third session. As written, the town referee cannot combine these facts to resolve the night. Please document a concrete supported bridge/shared-state mechanism (and exercise it in Phase 0), or restructure the flow so the authoritative state stays in one session.
|
Withdrawing per the user's direction — this will live as a local operational document for now rather than repo docs. Both review rounds' findings (the toAgent/needsReply private leg, the 8-speech live limit, and the conversation-scoped-session state flow) were incorporated into the final local version. |
|
Understood. Thanks for carrying the review findings into the local operational version. No further review action is needed on this PR. sent by |
What
Adds
docs/guides/werewolf-live-slack.md— a docs-only, afternoon-sized runbook for hosting a live Werewolf game between AgentConnect agents in a Slack workspace. It operationalizes the arena-validated message-only Werewolf (evals/games/werewolf.ts, measured indocs/designs/collaboration-arena-baseline.md) for real Slack, with the wolves' private den as a group DM (mpim) among the wolf agents and the referee agent.Structure
#werewolf-townpublic channel, wolves' den group DM, referee↔player 1:1 DMs (Mermaid diagram, neutral placeholder names throughout).SLACK_BOT_SCOPESincl.mpim:history/mpim:read,message.mpim), player personas, and a full model-referee moderator prompt adapted from the arena referee (role DMs, den prompting, sequential-day-speech announcement, spoken votes, first-clear-statement-carries parsing, win detection, secrecy + silent-observer rules).!resume, hop-cap exhaustion, and the Claude CodeSendMessagecollision (Claude Code's built-in SendMessage tool collides with the AgentConnect messaging tool and silently swallows parent reports #800/fix(daemon): lead the collaboration guidance with a tool-precedence rule (#800) #801).Verified against code (not prompt lore)
isDm=false/isGroupDmand stay mention-gated; verified agent-authored messages continue the thread through the ordinary ladder with the author excluded (feat: let agent messages continue a conversation without naming a recipient #549) — cited topackages/daemon/src/router/routing-table.tsandpackages/message/src/slack-message.ts.isTrustedHumanTurn) nor agent-call hop depth — the human host's phase-boundary messages are what re-fund both budgets each round.MAX_AUTOMATIC_TURNS_PER_WINDOW = 8/ 60 s);MAX_AGENT_CALL_HOPS = 20.Docs-only;
pnpm lintandpnpm format:checkpass.🤖 Generated with Claude Code