On Monday, your team picks Postgres. It takes twenty messages and one good argument about reporting. Everyone agrees. Everyone moves on.
On Wednesday, someone asks your AI agent: "Set up the user store in MongoDB."
The agent says "Sure!" and does it. It never saw Monday's argument. It only sees the latest message, and the latest message asked for MongoDB.
Teams forget decisions because chat buries them. Agents forget because they never saw them. I built Thusfar for a hackathon to fix both problems at once.
Two agents, one room
Thusfar is a Switch room with two agents that have different jobs.
The scribe writes down what the team decides: what they chose, why, and who decided. The builder does the real work, but it reads that log first. If your request breaks an active decision, it stops and asks a human to override the decision.
The name comes from "thus far". The log holds what the team has settled so far, and a human can change it at any time.
What a decision looks like
Every entry in the log has the same shape:
D-001 | ACTIVE
Decision: Use Postgres as the primary database, not MongoDB.
Why: The team knows it, and reporting needs relational joins.
Decided by: David, 2026-11-06
Supersedes: none
A decision is either ACTIVE or SUPERSEDED. When a team changes its mind, the old entry stays in the log and its first line points to the entry that replaced it, such as D-001 | SUPERSEDED by D-005. IDs run in order and nobody reuses them. You can read the log like a history, and it shows how the team's thinking changed.
You talk to the scribe in three commands:
@thusfar-scribe record: <decision>. Why: <reason>
@thusfar-scribe list
@thusfar-scribe supersede D-nnn with: <decision>. Why: <reason>
If you forget the reason, the scribe asks once. A decision with no reason is a rule nobody can argue with later, so it insists.
The builder pushes back
Here is the demo scenario, rebuilt from memory of the flow (the real transcripts are in the repo):
You: @thusfar-builder add a users collection in MongoDB
Builder: Before I start, D-001 is active: "Use Postgres as the primary database, not MongoDB." The reason was that the team knows it and reporting needs joins. David decided this on 2026-11-06. Your request conflicts with it. A human needs to override D-001 before I go ahead.
You: ok sure
Builder: I can't treat that as an override. Do you want to supersede D-001? Say so, give a reason, and the scribe will record it.
The builder turns down a vague "ok sure". Only a clear human override, recorded by the scribe, lets the work go ahead. After that, the builder cites the new decision and builds.
The builder also does not object to everything. If your task matches a decision, it goes ahead and cites it. If no decision covers the task, it says "none relevant" and goes ahead. When it can't tell, it says so and asks.
Why only the scribe writes
The log is a Switch room document, and Switch lets only the agent that created a document change it. I made that a design rule (ADR 0001) instead of working around it.
One writer means one author for the log. The builder cannot edit a decision to make its own task legal, and the scribe never writes code. The two jobs stay apart.
Room documents have a second benefit. Every agent in the room can read one, even right after !reset wipes its context. My sixth test checks this: reset the builder, ask what the team decided about a topic, and it quotes the right entry.
No server, no database, no service code
Thusfar is one YAML template. You import it into Switch Console, select Create all, and both agents and the room appear, with four seed decisions already in the log.
Each part of the project maps to a Switch feature. Rooms give the humans and both agents one shared history. Room documents hold the log. Room instructions give both agents the same rules about the decision format and what counts as an override. The Mattermost bridge lets people join from an ordinary chat app. Without Switch, I would have had to build a chat server, a document store, and an agent runner. With it, I wrote a template and a set of instructions.
Testing an agent room
An agent that "usually" behaves is a bug waiting for a deadline, so I wrote six scenarios and ran each one three times through a real room. The test script posts as a human and checks only the replies a human would see.
| # | Human says | Expected |
| 1 | Asks for work that matches a decision | Goes ahead and cites it |
| 2 | Asks for work that breaks a decision | Stops and cites ID, reason, and decider |
| 3 | Overrides a decision | Scribe supersedes; builder goes ahead |
| 4 | Records a decision with no reason | Scribe asks once for it |
| 5 | Asks for work no decision covers | Says "none relevant" and goes ahead |
| 6 | Asks what was decided, after !reset | Quotes the right decision |
All 18 runs passed, but one needed a rerun, and that story is the most useful part of the project.
Scenario 3, run 2 failed on the first full pass. I read the transcript and the agents had done everything right. The scribe recorded the override, and the builder posted its migration file afterward. My test had stopped reading the room while the builder was still working. The bug was in the test, so I fixed the wait and reran that one run. It passed.
If you test agents, expect some failures to belong to your test and not your agents. Read the transcript before you blame the model. Every run's transcript and posted files are in results/runs/.
What it can't do yet
I tested Thusfar on Fedora Linux only. The template uses no platform-specific feature, so macOS and Windows should work, but I haven't checked. The builder is also only as good as the log: a decision nobody recorded does not exist for it. The scribe makes recording cheap, but a human still has to say the words.
Try it
You need Switch Console, Docker, and Claude Code. Then:
- Clone the repo.
- In Switch Console, import
thusfar.template.yaml.
- Select Use, keep the defaults, and select Create all.
- Open the
thusfar-decisions channel and wait for both agents to say hello.
The code is on GitHub under the MIT license, and the 2 min 50 s demo shows the MongoDB challenge from start to finish.
One question for the comments: what is the first decision your team made that someone, or something, later undid by accident? Write it as a decision entry. You may find it has been missing from your log all along.