DEV Community

Harshil Srivastav
Harshil Srivastav

Posted on AI-assisted

Building a Switch courtroom where AI can negotiate but can't consent

A creative lead wants someone's portrait in a promo. That person wants to contribute, but doesn't want their face used. An AI can suggest a compromise. Who gets to say yes?

I'm Harshil, a B.Tech AI/ML student in Delhi. My teammates Daksh, Kartik and I built Delegates Courtroom for Flip the Switch (Track 2: Best Personal or Creative Workflow). Two AI advocates negotiate a storyboard, and the humans keep control of their own briefs and the final approval.

The rule we built around: agents negotiate, humans sign.

What Switch does, in plain words

Switch Rooms gives people and agents one shared place to work. Agents join with their own identities, get requests, read the room's context and reply like teammates.

The shared room log is the part that matters. Instead of copying one assistant's answer into another assistant's prompt, both of them work from the same messages.

Switch Console is the desktop app for setting things up and running agents. Switch can also bridge a room to a chat app. We used Mattermost, so people send their decisions as chat messages.

One thing to know before you read our repo: our advocates are external workers registered with Switch, not Console-managed model sessions. I used Console while learning Switch, but the project itself runs on a pinned Docker stack and a local polling adapter.

Why a courtroom?

Delegates is for collaborators who disagree about one deliverable without disagreeing about the whole project.

Our scenario is a four-panel, 30-second promo called SIGNAL. The lead's brief requires Daksh's portrait. The creator's brief forbids it and requires the closing line Made together. On our terms. and the credit Visuals: Daksh. Neither advocate can rewrite those terms on its own.

The courtroom UI makes proposals, objections, owner questions and signatures visible. The illustrated judges are just presentation. They are not extra models deciding whose wishes count.

Three Switch identities do the work:

  • cvc-lead-advocate proposes the board.
  • cvc-creator-advocate reviews that exact revision and its hash against both briefs.
  • cvc-clerk runs deterministic Node.js rules. It is not a model.

Both advocates use Codex CLI through one machine's existing login, each with its own Switch credentials.

In the recorded rehearsal, the lead proposed a board that conflicted with the creator's brief. The creator's advocate caught it and asked for an amendment. The court paused for an owner decision, then the agents produced a revised board that passed our encoded checks.

Here's the honest part. Those advocate turns really went through Switch and Codex, but the owner decisions (adopting briefs, accepting the amendment) were synthetic fixtures in an isolated database. The original rehearsal produced zero human approvals. The browser replay adds labelled synthetic signatures so you can try the downloads. So this shows the mechanism works. It does not show that the two of us agreed to anything. The output is an HTML storyboard and a text script, not a rendered video.

How I set it up

I ran Switch Console in WSL using the Linux .deb.

For the project runtime we committed the upstream Compose file. It pins Switch core, gateway and setup to 0.31.0, with Mattermost 11.9.0, and infra/NOTICE.md records where it came from. You need Node.js 22.13 or newer. There are no npm dependencies.

git clone --depth 1 --branch main https://github.com/harshil-sri/delegates.git
cd delegates
npm run init
Enter fullscreen mode Exit fullscreen mode

npm run init creates an ignored .runtime/stack.env with generated local credentials. Keep that folder private.

The background-worker helpers are written for Windows PowerShell, not a WSL shell:

.\scripts\start-stack.ps1
npm run setup
npm run setup:engine
.\scripts\start-workers.ps1
npm run status
Enter fullscreen mode Exit fullscreen mode

These are the real commands from the repo. When we verified the README on a fresh clone, init and the Compose config check passed. The PowerShell helpers were only read through, not rerun on Linux.

Room setup registers the three identities and creates a private room called creative-veto-spike through the Mattermost bridge. The room's starting instructions include:

Respond only to explicit named requests.
Do not claim human consent.
Do not delegate further agent turns yourself.
Enter fullscreen mode Exit fullscreen mode

The files in agents/ add role-specific rules on top. The saved briefs are the source of truth, and chat messages can't override them.

Before starting workers, check your Codex sign-in:

codex login status
Enter fullscreen mode Exit fullscreen mode

Our adapter runs Codex in read-only, ephemeral sessions with JSON response schemas. Every call uses the allowance on whatever account is signed in.

Then start the frontend:

npm run dev
Enter fullscreen mode Exit fullscreen mode

Open http://127.0.0.1:4173/. The frontend proxies read-only API requests to the clerk on port 18802. Mattermost is on 18865 and the Switch dashboard on 18801.

For a first sanity check, send @cvc-clerk status C01 in the channel. That asks the deterministic clerk, so no model runs.

What I'd tell the next beginner

Give participants identities, not just names

A local model response can't advance our case. The advocate has to post through Switch, and the listener has to read that actual message back under the expected identity.

Tying human accounts to owners took extra work. The pinned HTTP directory didn't expose the native-account mapping, so trusted setup uses a scoped, read-only SQL query instead of guessing who a sender is from a username.

Make waiting a valid state

When an amendment needs its owner, the models stop. A run allows at most two lead proposals and one schema repair per turn. If a provider fails, the run stops. It never swaps in a canned answer. We also filter passive-agent notices, so admin chatter doesn't trigger another model call.

Separate infrastructure trouble from app bugs

Docker failed on our machine because of an inaccessible, zero-byte dockerInference socket. I fixed it by renaming the stale runtime directory and didn't prune any volumes. That was a fix for my machine, so don't copy it blindly.

Passing tests and real consent are different things

A fresh-clone check passed 113 tests, the syntax checks and the static build. None of that means real owners approved a board. The offline receipt checks file hashes and internal consistency, and it is not cryptographic proof of who a person is. I'd rather keep that gap visible than make the demo sound more finished than it is.

Building by voice with Wispr Flow

During this hackathon I dictated my prompts, agent instructions, the README and the docs with Wispr Flow, including the instructions about protected briefs and owner-only decisions. That was our Code with Wispr workflow. Delegates itself has no speech feature.

My lifetime Wispr Flow stats are a 57-day streak, 131,956 words dictated, 126 words per minute and 23 apps. Those are lifetime numbers, not measurements from this build, and I'm not claiming a measured time saving.

What's next

First I want to record the whole workflow with both real owners reviewing and approving the same bundle. After that, independently owned advocate workers and private chambers. Right now both workers share one provider login and the briefs are shared in the room.

My favourite thing about this demo is that the most important action is sometimes stopping. The agent's clever alternative is useful, but permission belongs to the person.

Links

Top comments (0)