Inspiration

We kept having the same conversation twice.

A meeting would end, everyone would leave with a clear sense of what had been agreed, and a week later nobody could reconstruct it. Not the decision, not who owned the follow-up, not why we had ruled out the other option. The work was real; the record was not.

So we did what everyone does — we tried the AI notetaker apps. They work. But the shape of them was wrong for us in three ways:

  • They are per-person. Each colleague installs their own extension and pays their own subscription. The record of a company meeting ends up belonging to whoever happened to be running a bot that day.
  • Coverage is accidental. The meetings that most need minuting are the ones where nobody remembered to start anything.
  • The conversation leaves. Our numbers, our customers, our unfinished decisions — all of it sitting with a third party we had never chosen for that.

What we actually wanted was infrastructure, not an app: something that belongs to the organisation, covers every meeting by default, and keeps the record inside our own Google Cloud project.

We had talked about it for months. This hackathon is the reason it exists — thank you to the organisers for creating the deadline that turned it into a real system. It now runs in production and it is what our company relies on.

What it does

Atrium attends your meetings so that nobody has to remember to.

It watches calendar events across the whole organisation. The moment one appears with a Meet link, it puts its own notetaker on the invitation — in the same minute the meeting is created, days before it starts. There is nothing to install and nothing to click.

When people actually arrive, Atrium arrives too. Its notetaker joins as a participant, announces in the chat that the room is being minuted — that promise is configurable, and it is enforced and audited — and reads the live captions as people speak.

Afterwards, a pipeline of specialist agents takes over:

  • Extracts the record, drawing three distinctions that carry all of its value: discussed is not decided, might is not will, and only what still stood at the end of the meeting counts as a decision.
  • Files it into the organisation's Drive library.
  • Turns commitments into tasks on the owner's own list — but never for a commitment the transcript was not confident about.
  • Sends the record to everyone who was actually in the room, verified against the directory, never to an address a model invented.
  • Remembers what is worth carrying forward into the next meeting.

And then the part we use most: every meeting we have ever recorded is queryable. An organisation-wide memory we can simply ask — what did we decide about that supplier, what did we promise that customer, what came out of last month's review. Answered only from meetings actually recorded, with the meetings named so you can check.

Alongside meetings, it triages inbound mail into the organisation's own categories, labels it in the recipient's Gmail, and can draft replies. It has no send operation on the tool endpoint for any agent to call.

How we built it

Atrium runs on Google's Gemini Enterprise Agent Platform, with ADK and Gemini doing the reasoning.

Nine specialist agents, not one. Scout decides scope. Sentinel decides consent and takes no business action at all. Attendant gets the meeting captured. Herald briefs from memory. Scribe turns the transcript into a record. Archivist files. Registrar remembers. Steward turns commitments into work. Courier handles mail. They run as an ADK pipeline inside Agent Runtime, one session per meeting, resumed rather than restarted.

Least privilege, and provable. Each agent runs under its own Agent Identity with its own delegated Workspace scopes. A gateway maps every operation to the agents permitted to call it, and the tool endpoint refuses the rest. The clearest expression of this is the Scribe: the agent that reads your transcript holds zero tools and no Workspace credential at all. It can reach nothing.

Governance you can inspect rather than take on trust. The console's delegation page does not read configuration to tell you what is authorised — it mints a real token for every agent and reports what actually comes back. Model Armor sits on four boundaries: into a model, out of a model, into a tool, and before anything is written down. Agent Registry and Memory Bank are surfaced directly, so an administrator can see what the fleet is and what it remembers.

Being told, not polling. Calendar events.watch pushes changes to the ingress, which acts within seconds. The Meet Workspace Events API publishes participant-joined events to Pub/Sub, so the notetaker is dispatched when a human is actually in the room. Per-meeting Cloud Tasks timers handle everything else; a scheduled sweep exists only as a backstop for a missed notification.

The rest of the plumbing. Cloud Run for the ingress and the tool endpoint; a Cloud Run job running Playwright, Chrome and Xvfb for the notetaker itself; Firestore for runs and records; Cloud Storage for transcripts and recordings.

Challenges we ran into

A calendar does not know when a meeting starts. It knows when one was scheduled, which is a different fact and frequently a wrong one — people join late, run over, or never come. Every fix we made while guessing from the clock was just another guess. Subscribing to Meet's own join events replaced guessing with being told.

Being notified, and then polling anyway. We were pleased with the calendar push until we measured it: the notification arrived in one second, and then nothing happened for another fifty-one, because discovery opened a record and left it for the next sweep tick. Discovery now queues the work immediately.

Leaving a visible action to a model. Adding Atrium to the guest list was a tool the Attendant could call and was never told to. It invited itself to one meeting and not to the next identical one. There is no judgement in that decision and the result is visible to everyone on the invitation — it belongs in code, not in a prompt.

The failures that do not look like failures. This was the real difficulty, and it cost us the most time:

  • The transcript arrived, was assigned to the run, and the run was saved — and only its length was persisted. Every later read saw an empty transcript, so the meeting was re-attended in a loop and would never have been minuted. Every step reported success.
  • create_task() was called with an argument it did not accept. Every commitment failed, each failure was caught and logged per task, and the meeting still completed successfully. Minutes were filed and the work quietly became nobody's.
  • The gateway permitted the invite operation to the Attendant only, while discovery ran as the conductor. The call was refused, a warning was logged, and the guest list stayed exactly as empty as before.
  • The Scout is told that a meeting is in scope only if "the organiser is covered by the organisation's capture policy" — and the brief handed to it contained no policy at all. Asked to check a rule it had never been shown, it correctly refused and said so. The agent was right; our prompt was incomplete.

One instant, two spellings. The calendar returns whatever UTC offset an event was written in, so the same moment arrived as 08:55+00:00 from one read and 09:55+01:00 from another. We hashed that string to build the run id, so a single occurrence opened as two runs — and a second run puts a second notetaker into a room that already has one, in front of the participants. One stand-up accumulated six. It also corrupted the consent record: the later notetaker's report overwrote the earlier one's, so meetings that had been told they were being minuted were recorded as not having been.

Logs that stopped meaning anything. Re-subscribing to a Meet space answers 409 ALREADY_EXISTS — the state we wanted — and the HTTP layer logged every one of them as a warning. Over a thousand a night, all of them the system working correctly, in a log whose only value is that a warning means something.

Accomplishments that we're proud of

It is live, in production, running a real company's meetings. Not a demo path. Real staff, real calendars, real minutes that people act on — which is also why several of the bugs above were found by reading a night of production logs rather than by a test.

Least privilege that survives contact with reality. The agent that reads our most sensitive material can reach nothing at all, and the console proves the whole delegation picture by asking Google for real tokens rather than by reciting a config file.

Consent treated as a promise, not a checkbox. The room is told, in the organisation's own words, and on the occasions when that promise was not kept the system said so loudly and put it on the meeting's record.

Coverage by default. Nobody installs anything, nobody pays a per-seat subscription, and nobody has to remember. A meeting created at 8:55 had Atrium on the guest list at 8:55 and Atrium in the room at 8:59.

A test suite that checks the chain, not the pieces. Our worst failures were all cases where every component worked and nothing was connected — a provider method fully written and called by nothing, a permission naming the wrong caller. The suite that grew out of that asserts the whole path, end to end.

What we learned

The dangerous bug is not the crash. It is the system that reports success while doing nothing. Almost everything that hurt us was caught, logged, handled — and wrong. We now treat "handled, and carried on" as a design smell, and we take it as a rule that a component which says nothing when it does nothing is indistinguishable from one that is not running.

An agent cannot apply a policy it has not been given — and a well-instructed one will tell you so honestly, which looks exactly like a bug in the agent. It was not.

Judgement belongs to the model; visible, deterministic actions do not. "Sometimes" is indistinguishable from "broken" to the person watching.

Governance is worth far more when it is demonstrable. A page that mints a real token is a different artefact from a page that reads a manifest, and that is the difference between claiming least privilege and showing it.

Measure before optimising, including the parts you are proud of. Our push notification was fast and our system was slow, and those two facts lived a full minute apart.

What's next for Atrium

  • Get the notetaker into the room faster. Dispatch to browser-in-the-room is currently around three minutes, most of it a 1.5 GB Chrome image that a Cloud Run job cannot keep warm. Shrinking it is the single biggest remaining win.
  • Handle how our meetings actually sound. Code-switching between languages mid-sentence, strong accents and people talking over each other are where captions degrade, and the record should say so rather than quietly thinning out.
  • Deeper recall. The organisation-wide memory answers from meetings today; we want it to reason across meetings, mail and filed documents together.

  • Give the fleet its own observability surface. Reasoning trails are already recorded per meeting, and we want them queryable across the whole deployment.

Built With

Share this project:

Updates