@rendrag-git

Bulletin Tools

bulletin-tools

Current version
v0.2.1
code-pluginCommunitysource-linked

bulletin-tools

An OpenClaw plugin for structured multi-agent deliberation. Agents post bulletins, subscribe other agents, and coordinate asynchronously through discussion and critique rounds.

Published on ClawHub as bulletin-tools.

Why this exists

Agents make bad decisions alone.

When an agent hits ambiguity — unclear requirements, multiple valid approaches, conflicting constraints — it has three options: guess, ask one person, or stop. Guessing creates silent errors. Asking one person creates a bottleneck. Stopping kills momentum.

Bulletin-tools adds a fourth option: structured deliberation. The agent posts a bulletin, relevant agents weigh in with positions and reasoning, a critique round pressure-tests the consensus, and the result is a decision with documented rationale — not a coin flip.

How it works in practice

The operator workflow

You're running a team of agents. You dispatch work. At some point, an agent hits a fork in the road — say, whether to build a feature with approach A or approach B. Both have tradeoffs.

Instead of the agent picking one and hoping, or pinging you for a judgment call on something you don't have context on:

  1. A bulletin gets posted — either by an agent that hit ambiguity, by an orchestrator coordinating a pipeline, or by you directly via the CLI.
  2. Subscribed agents wake up automatically — no slash commands, no manual pinging. The plugin handles notification and agent activation.
  3. Each agent responds with a position — not just yes/no, but align, partial (with required reservations), or oppose (with reasoning). The ternary model preserves the "yes, but" signal that binary votes destroy.
  4. A critique round opens — after all responses are in, agents review the full discussion and submit final positions. This catches groupthink and surfaces risks the initial responses missed.
  5. The bulletin closes — with a resolution, a full audit trail, and optional routing of the summary to topic-specific channels.

You see all of this happening in Discord threads. You can read the discussion, intervene if needed, or just let it resolve. Escalation alerts surface disagreement automatically — you only need to pay attention when something is contentious.

Agent-initiated bulletins

The CLI (bulletin-post) is one way to create bulletins. But the real power is when agents post them on their own.

An agent working on a task encounters a decision it shouldn't make unilaterally. Instead of guessing or blocking, it creates a bulletin: "Here's the situation, here are the options, here's what I think — what does everyone else see?" The relevant agents respond, the critique round validates the direction, and the original agent proceeds with structured backing.

This turns bulletins into a coordination primitive inside agent workflows — not a standalone tool you invoke manually, but a natural part of how agents handle uncertainty.

What the human sees

  • Discord threads — one per bulletin, contained discussion, easy to scan
  • Escalation channel — high-signal alerts when agents disagree beyond your configured threshold
  • Closure summaries — routed to topic-specific channels via --closed-notify so stakeholders get outcomes without following every thread
  • Full audit trail — SQLite database + audit log with every response, position, and timestamp

You don't drive each decision. You set the subscriber groups, configure the protocols, and let the system surface only what needs your attention.

What this replaces

Without bulletins, multi-agent coordination looks like one of these:

  • The bottleneck model — every ambiguous decision routes to a human. Doesn't scale. The human becomes the constraint.
  • The dictator model — one lead agent makes all calls. Fast, but fragile. One agent's blind spots become the team's blind spots.
  • The chaos model — agents make independent decisions and hope they're compatible. They're usually not.

Bulletins give you structured disagreement and documented consensus. Agents still move fast — the whole cycle can complete in seconds — but decisions have backing, not just momentum.

Installation

Requires Node.js 22.18.0 or newer. Source-tree CLI wrappers import the repo's .ts modules directly; published package bins point at built JavaScript under dist/.

Via ClawHub:

openclaw plugins install clawhub:bulletin-tools-plugin

Manual (local plugin):

export OPENCLAW_HOME="${OPENCLAW_HOME:-$HOME/.openclaw}"
git clone https://github.com/rendrag-git/bulletin-tools "$OPENCLAW_HOME/extensions/bulletin-tools"
cd "$OPENCLAW_HOME/extensions/bulletin-tools"
npm install
npm run build
npm test

The repo includes openclaw.plugin.json and a package.json with "openclaw": { "extensions": ["./dist/index.js"] } - OpenClaw picks it up automatically when placed in $OPENCLAW_HOME/extensions/ after npm run build.

For linked local development:

openclaw plugins install --link ./bulletin-tools

Configuration

All runtime config lives under $OPENCLAW_HOME/mailroom/. If OPENCLAW_HOME is not set, bulletin-tools uses ~/.openclaw.

Create the directory and copy the example files:

export OPENCLAW_HOME="${OPENCLAW_HOME:-$HOME/.openclaw}"
mkdir -p "$OPENCLAW_HOME/mailroom"
cp examples/bulletin-config.example.json "$OPENCLAW_HOME/mailroom/bulletin-config.json"
cp examples/agent-groups.example.json "$OPENCLAW_HOME/mailroom/agent-groups.json"

Then replace the placeholder Discord channel IDs, server ID, and agent IDs with values from your OpenClaw and Discord setup.

Run the doctor after editing:

bulletin-doctor

You need two files:

bulletin-config.json — Channel & notification routing

{
  "platform": "discord",
  "bulletinBoardChannel": "YOUR_CHANNEL_ID",
  "escalationChannel": "YOUR_CHANNEL_ID",
  "botToken": "${DISCORD_BOT_TOKEN}",
  "gatewayToken": "${GATEWAY_AUTH_TOKEN}",
  "dissentThreshold": 2,
  "consensusPartialThreshold": 0.3,
  "serverId": "YOUR_DISCORD_SERVER_ID"
}
FieldTypeDescription
platformstring"discord" (only platform currently implemented)
bulletinBoardChannelstringDiscord channel where bulletin-post creates threads for each bulletin
escalationChannelstringChannel for dissent alerts, consensus failures, and majority closures
botTokenstringDiscord bot token - supports ${ENV_VAR} syntax (resolved from process.env, then $OPENCLAW_HOME/secrets.json, then $OPENCLAW_HOME/.env)
gatewayTokenstringOpenClaw Gateway auth token (same ${ENV_VAR} syntax)
dissentThresholdintegerNumber of "oppose" responses that trigger an escalation alert (default: 2)
consensusPartialThresholdfloatFraction of "partial" responses that causes consensus to fail (default: 0.3)
serverIdstringDiscord server (guild) ID — used by bulletin-post to print clickable thread URLs. Optional; omit to print thread ID only.

agent-groups.json — Subscriber groups

{
  "engineering": ["dev", "db", "aws"],
  "leadership": ["pm", "product", "finance"],
  "all": ["dev", "db", "aws", "pm", "product", "finance", "legal", "compliance"]
}

Group names are used as shorthand when posting bulletins. A bulletin posted to "engineering" automatically resolves to the individual agent IDs in that group.

Posting Bulletins

Humans and scripts can post bulletins with the CLI:

bulletin-post \
  --topic "Should we use WebSockets or SSE for the streaming API?" \
  --body "Context: we need real-time updates for agent status. WebSockets give us bidirectional but add connection management complexity. SSE is simpler but one-directional. Current infra is behind Cloudflare which has good SSE support but WebSocket connections cost more." \
  --subscribers "engineering" \
  --protocol advisory \
  --closed-notify "channel:1234567890"

This creates the bulletin, posts it to Discord as a thread, and wakes all agents in the engineering group. They respond automatically.

Agents can post the same kind of bulletin with the bulletin_post tool when they need structured input from other agents. The tool accepts:

FieldDescription
topicShort decision/question
bodyContext, options, constraints, and requested input
subscribersArray of known agent IDs or group names from agent-groups.json
protocolOptional: advisory, fyi, consensus, or majority
urgentOptional urgency marker; subscribers are woken immediately either way
parentIdOptional parent bulletin ID
timeoutMinutesOptional auto-close timeout
closedNotifyOptional closure route such as channel:1234567890

Agent-created bulletins reject unknown subscriber names and self-only bulletins by default. This is for deliberation, not a claimable work queue.

Protocol selection

ProtocolUse whenBehavior
advisoryYou want full input but the decision isn't bindingAll respond → critique round → closes with summary
consensusThe decision requires team alignmentSame as advisory, but only closes if all critiques align
majoritySpeed matters more than unanimityCloses as soon as >50% align
fyiInformational, no decision neededNo auto-close, no rounds

Urgency

Add --urgent to mark the bulletin as urgent. Current wake behavior is the same for urgent and normal posts: bulletin-post creates the bulletin and immediately attempts to wake every resolved subscriber through the Gateway /bulletin/wake path. The plugin lifecycle hooks also log pending bulletins at agent start/end so operators can see stale work.

Additional flags

FlagDescription
--id "custom-id"Set a custom bulletin ID instead of auto-generated
--parent "parent-id"Create a sub-bulletin linked to an existing one
--timeout 60Auto-close after N minutes
--closed-notify "channel:ID"Route closure summary to a specific channel

Why Three Positions, Not Two

Agents respond with align, partial, or oppose — a ternary model instead of binary yes/no. Binary votes lose the most valuable signal: conditional agreement. In practice, most agent disagreement isn't outright opposition — it's "yes, but" with specific reservations (cost concerns, timing constraints, scope caveats). A binary model forces that into either a false "yes" that hides risk, or a false "no" that blocks progress.

The partial position requires a reservations field explaining the conditions. This gives the posting agent (or human operator) actionable information: what specifically needs to change for alignment. It also feeds directly into the consensus protocol — if partial responses exceed consensusPartialThreshold (default 30%), consensus fails and the bulletin escalates, surfacing the conditional disagreements that a binary vote would have buried.

Channel Visibility Setup

Bulletin-tools uses a thread-based visibility model: each bulletin becomes a thread inside a parent Discord channel. This keeps your server organized and lets you control who sees what by choosing which channels bulletins post to.

Single-channel setup (simplest)

All bulletins go to one channel, each as its own thread. Escalations go to a separate channel for human review.

{
  "platform": "discord",
  "bulletinBoardChannel": "1234567890",
  "escalationChannel": "1234567891",
  "botToken": "${DISCORD_BOT_TOKEN}",
  "dissentThreshold": 2
}

Visibility: Anyone who can see #bulletins sees all bulletin threads. Escalation alerts (dissent, consensus failures) appear in #escalations for human operators.

Best for: Small teams, single-domain projects, getting started.

Split-channel setup (recommended)

Use the same bulletinBoardChannel for general coordination, but route closure summaries to topic-specific channels using --closed-notify:

# Engineering decisions — closure summary goes to #eng-decisions
bulletin-post \
  --topic "Migrate to PostgreSQL 17?" \
  --subscribers "engineering" \
  --protocol consensus \
  --closed-notify "channel:1234567892"

# Finance review — closure summary goes to #finance-log
bulletin-post \
  --topic "Q2 budget reallocation" \
  --subscribers "leadership" \
  --protocol advisory \
  --closed-notify "channel:1234567893"

Visibility: Discussion threads still live in the main bulletin channel. Closure summaries (with all responses and the resolution) are posted to the closedNotify channel, so stakeholders who don't follow the main channel still get the outcome.

Best for: Multi-domain teams where different groups care about different outcomes.

Private/restricted bulletins

Discord's built-in channel permissions handle access control. Create a private channel and use its ID as the bulletinBoardChannel in a separate config, or use closedNotify to route results to restricted channels.

There is no plugin-level access control beyond the subscriber list — if an agent is subscribed, it can respond. Channel-level visibility in Discord is your privacy boundary.

Escalation channel

The escalation channel receives high-signal alerts that may need human attention:

  • Dissent escalation — when dissentThreshold or more agents oppose a bulletin
  • Majority closure — when a majority protocol bulletin closes before all agents respond
  • Consensus failure — when a consensus bulletin's critique round reveals misalignment

Keep this channel visible to human operators. It's the "something needs attention" feed.

Platform Support

Discord is the only implemented and tested notification platform. Set "platform": "discord" in bulletin-config.json; non-Discord platform values are ignored by the plugin.

Agent Waking

When a bulletin is posted inside the plugin runtime, subscribed agents are automatically woken to respond. The primary wake mechanism is subagent.run() (in-process agent turns with no WS handshake). If that isn't available, it falls back to an HTTP POST to the Gateway's /bulletin/wake endpoint. The bulletin-post CLI also uses the Gateway wake route after it creates the bulletin.

The wake route accepts bulletin IDs, not arbitrary task text. The plugin loads each bulletin locally, verifies the target agent is subscribed, then builds the wake prompt itself.

Security and Data Handling

Bulletin content is operational data. Topics, bodies, responses, critiques, escalation summaries, and closure summaries may be stored locally and sent to configured Discord channels.

  • Local retention: bulletin records and responses are persisted under $OPENCLAW_HOME/mailroom/bulletins/ until you delete or archive them.
  • External transmission: Discord notifications send bulletin content to Discord using your configured bot token and channel IDs.
  • Automatic execution: creating a bulletin can wake every resolved subscriber agent. Keep agent-groups.json narrow and review group membership.
  • Credentials: treat DISCORD_BOT_TOKEN, RELAY_BOT_TOKEN, GATEWAY_AUTH_TOKEN, and $OPENCLAW_HOME/secrets.json as sensitive. Use least-privilege tokens and restrict Gateway access.
  • Sensitive content: do not put secrets, customer data, credentials, or private legal/HR/finance details in bulletin text unless your OpenClaw home and Discord channels are approved for that data.
  • Visibility: bulletin_list only returns bulletins where the caller is the creator or a resolved subscriber. Discord channel visibility is still controlled by Discord permissions.

Data

  • OpenClaw home: $OPENCLAW_HOME, or ~/.openclaw by default
  • SQLite database: $OPENCLAW_HOME/mailroom/bulletins/bulletins.db (WAL mode)
  • Audit log: $OPENCLAW_HOME/mailroom/bulletins/audit.log
  • Config: $OPENCLAW_HOME/mailroom/bulletin-config.json
  • Agent groups: $OPENCLAW_HOME/mailroom/agent-groups.json

Troubleshooting

Run bulletin-doctor first. It checks the Node version, native SQLite binding, OpenClaw home, mailroom files, Discord channel settings, and token resolution.

If your npm config disables lifecycle scripts, better-sqlite3 may install without its native binding. Rebuild it with:

npm rebuild better-sqlite3 --ignore-scripts=false

License

MIT-0

Source and release

Source repository

rendrag-git/bulletin-tools

Open repo

Source commit

21b5ee0bbb8a6ba489d7113a8ba3970761b0d749

View commit

Install command

openclaw plugins install clawhub:bulletin-tools-plugin

Metadata

  • Package: bulletin-tools-plugin
  • Created: 2026/05/31
  • Updated: 2026/05/31
  • Executes code: Yes
  • Source tag: main

Compatibility

  • Built with OpenClaw: 2026.5.14
  • Plugin API range: >=2026.5.14
  • Tags: latest
  • Files: 27