Every software company loses days at the same hand-off. A customer writes "I think I was overcharged." Support can't tell if it's a bug, so they paste it into an engineering channel. Engineering asks for the order details again, someone eventually reproduces it, fixes it, gets it reviewed and merges it, and nobody tells support, so the customer never hears back.

The work takes minutes. The waiting between people takes days.

For the Flip the Switch Hackathon I built Support-to-Ship Bridge: three AI agents and the people on two teams close that loop together in team chat, connected by Switch. People only make the two decisions that matter: merge the fix and send the customer reply.

Everything in the video is a real, unscripted run. This post explains how it's built, step by step, including the mistakes I made as a first-time Switch user, so you can skip them.

What is Switch, in one paragraph

Switch is open-source infrastructure that puts AI agents into the chat app your team already uses (Slack, Teams, Discord, Telegram or Mattermost) as members of rooms. A room is a channel with a briefing that every agent reads, documents, roles and links to other rooms. People and agents talk with @mentions. When an agent needs permission for something, it asks in the room, and a person approves or declines. You run it with Switch Console, a desktop app that can start a local Switch server and a bundled Mattermost for you, so you don't need Slack admin rights to try it.

What I built

#shoplite-support room #shoplite-engineering room ────────────────────── ────────────────────────── 1. Support person pastes a ticket 2. shoplite-support agent reproduces it against the pricing policy ──escalate──▶ 3. shoplite-fixer: branch, failing regression test, smallest fix 4. shoplite-reviewer: checks the diff against the policy → APPROVED 5. Engineer: Switch "Permission needed" for git merge → Allow once 6. shoplite-support drafts the reply ◀──fixed + merged── with the exact refund 7. Support person: Switch "Permission needed" to send → Allow once 

The "product" is a tiny checkout pricing service with two planted bugs: tax charged before the coupon, and free shipping that starts above $50 instead of at $50. The tests pass at the start because they don't cover either bug, so the agents have to find them.

A real run (ticket T-1042): a customer used SAVE20 on a $120 jacket and was charged $8.70 tax.

  • Support agent: "Actual: tax $8.70, total $104.70 (tax charged on the pre-discount $120). Policy: $96 × 7.25% = $6.96, total $102.96. This is a bug. Escalating."
  • Fixer: wrote a regression test that failed (8.7 vs 6.96), made a one-line fix, 7/7 tests pass.
  • Reviewer: approved. Without being asked, it also flagged the second planted bug in the shipping rule.
  • Me: clicked Allow once on the merge, then Allow once on the reply with a $1.74 refund.

How to build it (first-time Switch user edition)

1. Install the pieces

  • Docker Desktop. Switch Console uses it for the bundled Mattermost.
  • Switch Console, from the releases page.
  • An agent CLI, signed in. I used Claude Code: run claude, then /login.

In Switch Console: Get started → On this computer → Start. Wait for "Local server is running".

⚠️ Link your chat account. Go to Home → Messaging apps → Mattermost → Link → This is me. Without this, your agents won't answer your messages. To open Mattermost in the browser, use ⋮ → Sign-in details… on the same row.

⚠️ Switch Console reads your PATH once, at startup. If you install or sign in to an agent CLI while it's open, quit and reopen the Console.

2. Give the agents a shared workspace

All three agents work in one git repo, so the fixer's branch is visible to the reviewer. A setup script copies the demo app into ~/switch-demo/shoplite and runs git init. Running it again resets everything before a demo.

3. Create the agents

Your Agents → + in Switch Console. For each agent: a name (it's addressed by this, e.g. @shoplite-fixer), a description, instructions, Run location: This computer, Provider: Claude Code, and the workspace as its directory.

The instructions do most of the work. Here's the core of the fixer's:

When @shoplite-support escalates a bug: 1. Say in one line what you are about to check. 2. Create a branch from main: git checkout -b fix/<ticket id>. 3. Write a failing regression test that encodes the policy line, run node --test to show it fails. 4. Make the smallest fix in src/ that makes it pass. Run node --test. 5. Commit, then hand off to @shoplite-reviewer: branch, root cause, diff stat, test summary. 6. When the reviewer approves, ask the room "Merge fix/<id> into main?" and wait for a clear yes. Never push, never rewrite history. Never report a result you did not run. 

Agents hand work to each other with a plain @mention. There's no separate task protocol to learn.

4. Create the rooms with a template

Switch templates are YAML. One document created both rooms, the link between them, a room document, roles and the kickoff messages:

version: 1 params: bridge: { type: bridge, default: [$first] } group: name: ShopLite support-to-ship rooms: - name: shoplite-support bridge: "{bridge}" agents: ["shoplite-support"] users: ["{$creator}"] instructions: | Support staff paste customer tickets here. @shoplite-support answers how-to questions and escalates pricing bugs to shoplite-engineering. Nothing is sent to a customer without a person approving it here. docs: - name: escalation-checklist content: ticket id, customer's words, repro command, actual vs expected, policy line kickoff: "@shoplite-support hi. Introduce yourself in one line." - name: shoplite-engineering bridge: "{bridge}" agents: ["shoplite-support", "shoplite-fixer", "shoplite-reviewer"] users: ["{$creator}"] roles: - { name: reviewer, instructions: Review fixes against the policy., exclusive: false } - { name: merge-captain, instructions: The one agent allowed to merge., exclusive: true } kickoff: "@shoplite-fixer @shoplite-reviewer hi. One line each." links: - { from: shoplite-support, to: shoplite-engineering, label: escalate pricing bugs } 

Templates → Import template, paste it, then Use → Create all. The kickoff matters: agents only start working when a message addresses them, so the kickoff makes the rooms come alive straight away.

5. Make the human approvals real, not just a polite prompt

This is my favourite part. Telling an agent "ask before merging" in its instructions is a request, not a guarantee. Instead, the workspace has a Claude Code permission file:

{ "permissions": { "ask": ["Bash(git merge:*)", "Bash(node scripts/send-reply.js:*)"], "deny": ["Bash(git push:*)", "Bash(git reset --hard:*)"] } } 

When the fixer runs git merge, Claude Code needs permission, and Switch posts it in the room: Permission needed · request A1, with Allow once / Allow for this session / Decline. A plain yes or no reply works too. Switch records who approved ("Chosen by @user from Mattermost"). The customer reply works the same way: the "send" step is a script on the ask list, so the support person's Allow once literally is the send button.

💡 The request is posted in a thread. If an agent seems stuck, open the "N replies" link under the latest message.

Gotchas I hit (so you don't have to)

  1. Your model must handle tool calls well. Switch agents reply by calling a post_message tool. Their final text isn't shown to anyone. I first tried a free model through OpenRouter, and the agent showed "Thinking…" and then nothing. The session log said "The model's tool call could not be parsed." Switching to a Claude model fixed it immediately.
  2. On Windows, create agents from "Your Agents", not from a template. In Switch Console 0.40.1 on Windows, the template form showed every provider as "Not supported here" (tooltip: "SDK sessions require a POSIX SSH execution host"), while Your Agents → + on the same machine worked fine. So I create the agents by hand and use a rooms-only template. On macOS or Linux, one template can create agents and rooms.
  3. Room creation can outlive the Console's timeout. Each Mattermost room took about 15 seconds. The Console said "Could not reach the server… Nothing was created", but both rooms had been created. Check Your Rooms before you retry, or you'll get duplicates.
  4. Want a Linux agent host on a Windows PC? WSL works as a Switch remote host, but the agent controller in WSL has to reach the local server, which listens only on 127.0.0.1. Enable networkingMode=mirrored in .wslconfig. Also make sure non-interactive SSH sessions can find a current Node.js and the claude CLI, because ~/.local/bin isn't on their PATH.

What I'd build next

  • Approval policies by role, so only the merge-captain holder or named engineers can approve a merge.
  • A help-desk connector (Zendesk or Intercom), so tickets arrive in the support room without anyone pasting them.
  • Scheduled check-ins: "@shoplite-support, list tickets waiting on a customer reply" every morning.

Try it

Everything is on GitHub: the demo app, agent instructions, both templates, setup scripts, and the script that edited the demo video.

👉 github.com/Shiva-Preetham/Flip-the-Switch-Hackathon

Built for the Flip the Switch Hackathon by WeMakeDevs × DevTools Academy × Switch. #HowISwitch