Our AI agent cannot approve its own posts. Half its commits are fixes.

Our AI agent reads internal Slack and proposes posts, but it can never approve its own. How we built that guardrail and watched each control fail.
Case study // Azorel, built by us

Our AI agent cannot approve its own posts. Half its commits are fixes.

Giving an agent a way to post is easy. Making sure it can never approve its own post is the work.

Azorel is our chat and operations agent. We recently gave it a controlled way to read internal Slack channels and propose posts. The design question was not what it can do, but what it can never do alone.

0posts the agent can approve itself
4Slack tools on the Slack-only key, exactly
5build tasks, each with its own adversarial review
1,064 of 2,110commits start with fix
Part of the BetterQA team on a lakeside jetty
Part of the BetterQA team in Cluj-Napoca.

Propose, never approve

Every Slack post the agent tries to make becomes an approval card in the main channel and on an approvals page, with plain approve and reject buttons. Nothing is posted at that moment.

On approval, a dedicated executor checks the channel again and posts the text exactly as it was stored. What the person approved is what is sent, and it never takes the normal tool path where the agent could have changed it. A card that cannot be delivered is retired, and the caller is told that nothing was queued. Normal human-attestation rules apply, so the agent can never approve its own card.

Read only what is internal, and fail closed

The read tools refuse a channel if any one of six flags is anything other than the boolean false: shared, externally shared, pending external share, organisation-wide shared, direct message or group message. Missing or odd values are refused, not guessed. Direct-message ids are refused outright. Message text is fenced, the history limit is capped and results are redacted.

We checked this against a real public channel and a real private channel: all six flags came back as booleans.

Two keys that cannot be the same

The Slack tools live behind their own key that unlocks those tools and nothing else. The service refuses to start if the full key and the Slack key are the same value. A session created under one key and reused under another is refused. The full-access tool list no longer shows Slack tools at all, and the Slack-only key lists exactly four.

How we tested a control

Each of five build tasks had its own adversarial review. The mutation tests then switched off each piece in turn: the scope handoff, the internal-channel gate, the check at approval, the dedicated executor, the retirement of undelivered cards. Each broke named tests. A control nobody has seen fail is a hope.

And half of its commits are fixes

Since March, 1,064 of the agent's 2,110 commits have subjects starting with "fix", against 526 starting with "feat". That is an honest picture of building an agent: most of the work is correcting it. Plan for it. Budget for the fixes, not only the features.

What to take to your own team

  • List what the agent must never do alone, and enforce it where the action happens.
  • Check at the consequence. Re-check the channel at approval, not only at proposal.
  • Watch the guard fail. Switch each part off and see a test go red.

Letting an agent act in your systems?

We test what it can do alone and what it can never do alone. The first month is a trial.

Talk to our team
Share the Post: