<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Sonu Singh</title>
    <description>The latest articles on DEV Community by Sonu Singh (@sonu08).</description>
    <link>https://dev.to/sonu08</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4230536%2Ff490b53b-4811-4155-a231-c7057779f640.png</url>
      <title>DEV Community: Sonu Singh</title>
      <link>https://dev.to/sonu08</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/sonu08"/>
    <language>en</language>
    <item>
      <title>I built a month-end close room where AI agents investigate and only a human can approve (with Switch and Discord)</title>
      <dc:creator>Sonu Singh</dc:creator>
      <pubDate>Fri, 06 Nov 2026 13:41:07 +0000</pubDate>
      <link>https://dev.to/sonu08/i-built-a-month-end-close-room-where-ai-agents-investigate-and-only-a-human-can-approve-with-ce9</link>
      <guid>https://dev.to/sonu08/i-built-a-month-end-close-room-where-ai-agents-investigate-and-only-a-human-can-approve-with-ce9</guid>
      <description>&lt;p&gt;Month-end close review is slow. Variances, missing commentary and bad data get chased across spreadsheets and chat threads, and the people who approve things spend half their time just finding out what's waiting on them.&lt;/p&gt;

&lt;p&gt;For the &lt;a href="https://www.wemakedevs.org/hackathons/switch" rel="noopener noreferrer"&gt;Flip the Switch hackathon&lt;/a&gt; I built &lt;strong&gt;ClosePilot&lt;/strong&gt;, a finance close-control room that runs inside Discord through &lt;a href="https://docs.switchagents.ai" rel="noopener noreferrer"&gt;Switch&lt;/a&gt;. AI agents validate the numbers, open cases, investigate them and recommend what to do. &lt;strong&gt;Only a human can approve, reject or close a case.&lt;/strong&gt; The agents can't do it even if you ask them nicely, and I'll show you why.&lt;/p&gt;

&lt;p&gt;This post walks through what I built, how I set it up, and the things that tripped me up, so your first Switch build goes faster than mine.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Demo video (under 3 minutes):&lt;/strong&gt; &lt;a href="https://youtu.be/8zgcbYq1Vhs" rel="noopener noreferrer"&gt;youtu.be/8zgcbYq1Vhs&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Code:&lt;/strong&gt; &lt;a href="https://github.com/Sonu08/closepilot" rel="noopener noreferrer"&gt;github.com/Sonu08/closepilot&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;All data is synthetic, for a fictional company called &lt;em&gt;Northstar Logistics Group&lt;/em&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What it does, in one picture
&lt;/h2&gt;

&lt;p&gt;One Switch template creates four linked Discord channels:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Room&lt;/th&gt;
&lt;th&gt;Who's in it&lt;/th&gt;
&lt;th&gt;What happens&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;#demo-oct-de&lt;/code&gt;, &lt;code&gt;-pl&lt;/code&gt;, &lt;code&gt;-us&lt;/code&gt; (one per entity)&lt;/td&gt;
&lt;td&gt;me, &lt;code&gt;control-analyst&lt;/code&gt;, &lt;code&gt;exception-investigator&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;The analyst checks that entity's numbers, opens cases and hands them to the investigator&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;#demo-oct-review&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;me, &lt;code&gt;close-coordinator&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;The coordinator keeps a live status board of every case and who it's waiting on&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fc1wqh59rgup8i9iyms7k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fc1wqh59rgup8i9iyms7k.png" alt=" " width="800" height="421"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The story of one case, EX-003, shows the whole flow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The &lt;strong&gt;Control Analyst&lt;/strong&gt; runs seven deterministic controls. EX-003 is 13,200.00 EUR over forecast (&lt;strong&gt;13.89%&lt;/strong&gt;, above the 10% threshold) with no commentary, so it opens a case.&lt;/li&gt;
&lt;li&gt;It hands the case to the &lt;strong&gt;Exception Investigator&lt;/strong&gt; with a real &lt;code&gt;@mention&lt;/code&gt;, agent to agent.&lt;/li&gt;
&lt;li&gt;The investigator says it needs commentary and evidence before it can recommend anything.&lt;/li&gt;
&lt;li&gt;I ask the investigator to just approve it. &lt;strong&gt;It refuses.&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;I add commentary and an evidence reference from my own terminal.&lt;/li&gt;
&lt;li&gt;The investigator now recommends &lt;strong&gt;APPROVE&lt;/strong&gt;, and says the decision is mine.&lt;/li&gt;
&lt;li&gt;I approve, close and export an audit-ready decision record.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The design rule that shaped everything
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Agents calculate, investigate and recommend. Humans decide.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Two consequences:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Python does every calculation.&lt;/strong&gt; The agents never compute a variance or a percentage. They run a small CLI that prints JSON, and they report what it says. Ask an LLM to work out a variance percentage and sooner or later it will round something creatively; ask Python and you get the same answer every time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The approval gate lives in code, not in a prompt.&lt;/strong&gt; A prompt that says "never approve" is a suggestion. ClosePilot's workflow refuses a decision from any agent actor, and the agents' CLI simply has no &lt;code&gt;decide&lt;/code&gt; command.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 1: run Switch locally
&lt;/h2&gt;

&lt;p&gt;I used &lt;strong&gt;Switch Console&lt;/strong&gt; on Windows with a Switch server running on my own machine. Two things I learned the hard way:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Start Docker Desktop before the Switch server.&lt;/strong&gt; The bundled Mattermost runs in Docker; if Docker isn't up, it doesn't start.&lt;/li&gt;
&lt;li&gt;If you ever reset the server, your machine may not show up as an agent location again until you &lt;strong&gt;open the New agent form&lt;/strong&gt; (Your Agents → &lt;strong&gt;+&lt;/strong&gt;, Run location: &lt;em&gt;This computer&lt;/em&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 2: give each agent a tiny, boring job
&lt;/h2&gt;

&lt;p&gt;Each agent is a Claude Code agent with its own folder:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;switch/agents/control-analyst/
  CLAUDE.md                # the brief: what to run, how to report, what it must never do
  .claude/settings.json    # allows exactly one command, the ClosePilot wrapper
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The wrapper works out the agent's identity &lt;strong&gt;from its working directory&lt;/strong&gt;, not from an argument, so the investigator can't claim to be someone else. The settings file allows only that wrapper and denies file edits and web access.&lt;/p&gt;

&lt;p&gt;One surprise: in my setup the agents' shell was Git Bash, &lt;code&gt;python&lt;/code&gt; was the Windows Store stub, and my conda environment wasn't on &lt;code&gt;PATH&lt;/code&gt;. So the wrapper reads the interpreter's absolute path from a small git-ignored file instead of relying on &lt;code&gt;PATH&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Be honest with yourself about what the permission file is: a second layer, not a sandbox. The real guarantee has to come from the application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: one group template for all the rooms
&lt;/h2&gt;

&lt;p&gt;Switch &lt;strong&gt;group templates&lt;/strong&gt; can create several rooms, the links between them and a per-room kickoff message in one go. Here's the shape of mine (trimmed):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
&lt;span class="na"&gt;params&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;closepilot_home&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;label&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;ClosePilot checkout&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;pattern&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;([A-Za-z]:)?/.+"&lt;/span&gt; &lt;span class="pi"&gt;}&lt;/span&gt;
  &lt;span class="na"&gt;room_prefix&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;     &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;label&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;Room name prefix&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;default&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;closepilot-oct&lt;/span&gt; &lt;span class="pi"&gt;}&lt;/span&gt;
&lt;span class="na"&gt;agents&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;control-analyst&lt;/span&gt;
    &lt;span class="na"&gt;allow_existing&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;          &lt;span class="c1"&gt;# reuse the agent on later runs&lt;/span&gt;
    &lt;span class="na"&gt;addressing&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;owner-agents&lt;/span&gt;      &lt;span class="c1"&gt;# lets another agent's @mention wake it&lt;/span&gt;
    &lt;span class="na"&gt;directory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{closepilot_home}/switch/agents/control-analyst"&lt;/span&gt;
  &lt;span class="c1"&gt;# exception-investigator and close-coordinator look the same&lt;/span&gt;
&lt;span class="na"&gt;rooms&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{room_prefix}-de"&lt;/span&gt;      &lt;span class="c1"&gt;# quote it, or the YAML is invalid&lt;/span&gt;
    &lt;span class="na"&gt;agents&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;control-analyst&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;exception-investigator&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;kickoff&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
      &lt;span class="s"&gt;@control-analyst check FC-2026-10-DE&lt;/span&gt;
  &lt;span class="c1"&gt;# -pl, -us ...&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{room_prefix}-review"&lt;/span&gt;
    &lt;span class="na"&gt;agents&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;close-coordinator&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;kickoff&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
      &lt;span class="s"&gt;@close-coordinator build the status board for FC-2026-10&lt;/span&gt;
&lt;span class="na"&gt;links&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;from&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{room_prefix}-de"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;to&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{room_prefix}-review"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;label&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;review-room&lt;/span&gt; &lt;span class="pi"&gt;}&lt;/span&gt;
  &lt;span class="c1"&gt;# ... and back again&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Things worth knowing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A kickoff must @mention an agent&lt;/strong&gt;, or nobody wakes up.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;allow_existing: true&lt;/code&gt;&lt;/strong&gt; lets you run the template again without inventing new agent names. Agent names can't be reused, so without it your second run fails.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A room-name prefix parameter&lt;/strong&gt; saves you on the second run too, because room names must be free.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Links are signposts, not permissions.&lt;/strong&gt; An agent sees a linked room's name and label, but it still has to be a member to work there.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 4: connect Discord
&lt;/h2&gt;

&lt;p&gt;Switch's &lt;a href="https://docs.switchagents.ai/switch-rooms/deploy/messaging-apps/discord" rel="noopener noreferrer"&gt;Discord guide&lt;/a&gt; is good; follow it exactly. The short version: create an application and bot in the Discord Developer Portal, turn on the &lt;strong&gt;Server Members&lt;/strong&gt; and &lt;strong&gt;Message Content&lt;/strong&gt; intents (without Message Content your agents receive empty messages), invite the bot with the listed permissions, then connect it in Switch Console with the bot token and your server ID.&lt;/p&gt;

&lt;p&gt;Then, when you use the template, pick your Discord connection as the &lt;strong&gt;Messaging app&lt;/strong&gt;. The form starts on whichever app the template tries first, so check this every time, or you'll create your rooms somewhere you weren't looking.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5: hand-offs are just mentions
&lt;/h2&gt;

&lt;p&gt;There's no task protocol to learn. With &lt;code&gt;addressing: owner-agents&lt;/code&gt;, the analyst posting &lt;code&gt;@exception-investigator You now own 5 cases…&lt;/code&gt; wakes the investigator in the same room.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffml5lm034s8q88cb3t5i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffml5lm034s8q88cb3t5i.png" alt=" " width="800" height="392"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Keep each agent in the room it was addressed in.&lt;/strong&gt; In a spike I had an agent post into a second room. It worked, but it moved that agent's session: its session in the other room stopped receiving messages. The next message addressed there simply started a fresh session, which answered fine. So I designed around it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;hand-offs happen by mention &lt;strong&gt;inside&lt;/strong&gt; a room;&lt;/li&gt;
&lt;li&gt;all case state lives in ClosePilot's log, &lt;strong&gt;not&lt;/strong&gt; in an agent's memory, so a fresh session loses nothing.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 6: a live status board as a room document
&lt;/h2&gt;

&lt;p&gt;Switch &lt;strong&gt;room documents&lt;/strong&gt; are perfect for a status board. The coordinator runs &lt;code&gt;status FC-2026-10&lt;/code&gt;, creates the document &lt;code&gt;close-status-board&lt;/code&gt; and posts a short summary.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0in4ztsxk2t8qraowh2y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0in4ztsxk2t8qraowh2y.png" alt=" " width="800" height="395"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Two rules I didn't expect:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Only the agent that created a room document can update it.&lt;/strong&gt; If your template seeds the document, a &lt;em&gt;person&lt;/em&gt; wrote it and no agent can update it later. Let the agent create its own.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Order matters.&lt;/strong&gt; All four kickoffs fire at once, so on my first run the coordinator built the board &lt;strong&gt;before&lt;/strong&gt; the analysts had opened any cases ("0 cases"). Asking it to refresh fixed it. Next time I'd have the analysts mention the coordinator when they finish.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I also found that &lt;strong&gt;roles are for agents only&lt;/strong&gt;. I first planned a "reviewer" role for the human. That doesn't exist: an agent takes a role, a person doesn't.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 7: the gate no agent can pass
&lt;/h2&gt;

&lt;p&gt;This is the part I'm proudest of. In &lt;code&gt;#demo-oct-de&lt;/code&gt; I typed &lt;code&gt;@exception-investigator approve EX-003&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvlkw5epfk1au5z8u3b36.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvlkw5epfk1au5z8u3b36.png" alt=" " width="800" height="395"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It refused, and it explained why with numbers ClosePilot calculated, not ones it made up. It refuses because it &lt;em&gt;can't&lt;/em&gt;, not only because it's polite:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;The agent CLI has no decide, close, commentary or attach-evidence command.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The workflow rejects those actions from any agent actor&lt;/strong&gt;, and a separate invariant blocks any agent event outside the four agent states.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The human's commands need a reviewer secret&lt;/strong&gt; that lives only in the human's shell. Agents run in sessions that never see it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Every human event is sealed&lt;/strong&gt; with an HMAC whose key comes from that secret, and the reviewer is enrolled once in a hash-chained audit log. A forged "human" line, or a replaced reviewer file, makes every reviewer command stop.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;After I added commentary and an evidence reference, the investigator recommended approval, and said plainly that ClosePilot hasn't opened the document behind the reference:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcsu7ebh06utqbd80numh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcsu7ebh06utqbd80numh.png" alt=" " width="800" height="398"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then the decision, close and export are mine:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkhoha0km1gkl83i1f4a4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkhoha0km1gkl83i1f4a4.png" alt=" " width="800" height="429"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd tell a first-time Switch user
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Do a throwaway spike first.&lt;/strong&gt; One agent and two rooms told me more in 20 minutes than an hour of planning. Delete it afterwards, or keep it as evidence.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Console can say "Nothing was created" when everything was created.&lt;/strong&gt; Template runs sometimes timed out in the Console while the server finished the job. Check &lt;em&gt;Your Agents&lt;/em&gt; and &lt;em&gt;Your Rooms&lt;/em&gt; before retrying, because a retry collides with the names you just created.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"Bridge is not running"?&lt;/strong&gt; Restart the server first. That's the first fix in &lt;a href="https://docs.switchagents.ai/switch-rooms/resources/troubleshooting#bridge-is-not-running" rel="noopener noreferrer"&gt;the troubleshooting guide&lt;/a&gt;, and it worked for me.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use a new room prefix for each run&lt;/strong&gt;, and &lt;code&gt;allow_existing: true&lt;/code&gt; for your agents.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Put state in your app, not in the chat.&lt;/strong&gt; Sessions come and go; a log doesn't.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Make the dangerous action impossible, not just discouraged.&lt;/strong&gt; Prompts are guidance. Code is the gate.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Be careful what you type in a live agent room.&lt;/strong&gt; Every mention wakes an agent. While recording my demo I re-ran a check in the wrong channel by accident; ClosePilot only &lt;em&gt;read&lt;/em&gt; the data, so nothing changed, but it's a good reason to keep writes behind the human's own commands.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  What's next
&lt;/h2&gt;

&lt;p&gt;Microsoft Teams (it needs a public HTTPS address, which my laptop doesn't have yet), a hand-off from the analysts to the coordinator so the board refreshes itself, and a proper identity provider instead of a local secret.&lt;/p&gt;

&lt;p&gt;If you're building on Switch, the code is on &lt;a href="https://github.com/Sonu08/closepilot" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;. The templates, agent briefs and permission files are all in &lt;code&gt;switch/&lt;/code&gt;. Questions welcome in the comments.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Built for Flip the Switch (WeMakeDevs). I built ClosePilot with help from Claude Code, and this post was drafted with AI assistance and checked by me. All data is synthetic.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>python</category>
      <category>discord</category>
    </item>
  </channel>
</rss>
