Inspiration
Knowledge work reorganised itself around the meeting, and the tooling around that moment is worse than most people admit. Summaries tell you what was discussed, not what you now owe, to whom, by when, or what has to happen first. And every transcript tool will happily list "Alex to prepare the rollout checklist" — whether Alex agreed, refused, or said nothing at all.
That last part is the whole problem. A list that cannot tell a promise from a suggestion is a list nobody trusts, and an untrusted list is worse than none, because it displaces the manual system people would otherwise keep. So they track it by hand: a sticky note, a doc they stopped updating, mostly memory. That works until the week they have thirty items, and then the thing they drop is, reliably, the thing someone else was waiting on.
Everything we built follows from one distinction: an assignment is not a commitment. When Maya says "Sarah, can you send the launch report by Friday?", nothing has been promised yet. Work doesn't get lost in meetings — it gets lost after them.
What it does
Weave is a commitment ledger that lives inside Google Workspace.
- Captures every commitment from every meeting you attend, automatically. No bot joins the call; each person's own Meet transcript subscription fires when their transcript is ready.
- Verifies each one against the transcript. Five statuses — accepted, declined, deferred, reassigned, unresolved — and only accepted and reassigned become work. Silence is always unresolved, and an accepted item is structurally required to carry the turn index where the acceptance happened.
- Enriches privately. Each owner's items are enriched in a session that runs as that owner, over only their prior commitments, meeting summaries, Drive files and open Tasks — nothing anyone else can't see, because nothing anyone else has is reachable from there.
- Remembers across meetings. The same deliverable raised on the 12th, restated on the 19th and carried again on the 28th is one commitment with a three-week history, not three tasks in three tools.
- Answers what to do first, with a reason: "Confirm the rollback plan — it's 3 days overdue and three commitments are queued behind it."
It all lands in one Google Chat direct message: the install that onboards you, the card after each meeting with a "Mark done" button, and the copilot you ask questions of. Weave never writes into Drive, Tasks or Calendar — it tells you what you owe, and the doing stays where you already do it.
How we built it
One GCP project, and a pipeline that is a security boundary before it is an engineering one.
Meet → Workspace Events → Pub/Sub → Cloud Run → Vertex AI Agent Engine → Firestore → Chat. Ingestion claims each conference id with a Firestore lease, screens the transcript through Model Armor, and hands it to a two-phase ADK pipeline.
- Phase 1 — extraction reads the whole transcript with exactly two tools:
resolve_speaker(matched against trusted Meet attendee state, graded confidence) andinfer_deadline. It holds no context tool at all, so the agent that sees everyone's words cannot reach anyone's files. - Between the phases, three pure Python functions re-ground every identity against the real attendee list, drop everything nobody accepted, and resolve a principal — failing closed on no email, low confidence, or an owner who wasn't in the meeting.
- Phase 2 — enrichment runs once per owner in a fresh session, as that owner, with
only their items in state. Its one tool searches four registered sources; every
Firestore query carries
visible_to array_contains <email>inside the query, and the ACL field shares an index with the vector field so the prefilter survives vector search. - The echo gate SHA-256 fingerprints everything the model hands back against the exact inputs, keeps the originals, and discards the model's copies.
Agent Engine holds no domain-wide delegation, ever. When enrichment needs a Drive file, it calls a broker endpoint on ingestion — the only identity with delegation — which performs the read as the owner and refuses anyone not onboarded.
Reconciliation folds new mentions into an owner-scoped commitment graph with UUIDv5 ids, and prioritisation is a pure function in shared code — urgency groups plus a deterministic attention score — so a Chat card and a copilot sentence can never disagree. A scheduled Cloud Run job keeps every user's Meet subscription alive. Everything is Terraform, and 265 hermetic tests run with no network, no cloud credentials and no model calls.
Challenges we ran into
Card buttons that died silently. A Pub/Sub-connected Chat app has no channel to answer an interaction on — clicks never reached our service and users saw a red "unable to process your request". The fix was the App URL connection with Authentication Audience set to the HTTP endpoint URL, so the bearer is a Google-signed OIDC token we verify with the same code path as Pub/Sub push. Chat also speaks two envelope dialects depending on how the app is registered, so we parse both and log which one actually arrived.
A dependency graph with no edges. Reconciliation judged each mention on its tidied description — and a stated precondition ("I can't send it until the vendor numbers land") gets paraphrased out of a tidy description every time. We now assemble a judgement text that includes the spoken source text, and the edges appeared.
Degradations that look exactly like success. Three independent failures — the judgement model failing, the vector index not being ready, embeddings not surviving the read — all produce one commitment per mention, which is precisely what a healthy first run looks like. Same shape with embeddings: a document with no vector is invisible to search, but the lexical fallback keeps search working, so the gap hides. We had to rewrite our own verification steps to check the right thing.
Identity that the platform will only give you one way. Meet's Events API rejects
an email and the literal me as a subscription target with
TARGET_RESOURCE_ACCESS_DENIED — it wants the numeric Cloud Identity id. And a
conference record is visible only to its participants, so reads have to impersonate
the user whose subscription produced the event, not one fixed service account.
Platform trivia that each cost a debugging cycle. Cloud Run 401s push at the edge
unless custom_audiences is declared. The root logger defaults to WARNING under
uvicorn, so our INFO logs vanished. Model Armor ignores IAM basic roles entirely and
is regional-only. The org's domain-restricted-sharing policy blocks granting
pubsub.publisher to Google's own Meet publisher. And zsh reads a bare
$MODEL:generateContent as a history modifier and silently mangles the URL into a 404.
Accomplishments that we're proud of
- Isolation is structural, not instructed. No prompt asks the model to stay in its lane — phase 2 is a separate session that has never been shown anyone else's data, and a named test pins it.
- A promise is not an assignment, enforced by a validator. An accepted item without its acceptance turn is a validation error, not a style violation. That one rule is what makes the output worth acting on without re-reading the transcript.
- Refusals are reported as refusals. An identity failure that renders as an empty list is the most dangerous possible outcome — the platform looks healthy and the user concludes they have no work. Weave says which of the two happened.
- Priority you can argue with. Ordering is arithmetic over stated facts, written once and read by every surface, and every recommendation says why.
- Read-only by construction. There is no write tool anywhere in the agent surface — not disabled, absent — so the worst case of prompt injection is a bad suggestion on a card a human reads.
- 265 hermetic tests across 29 files, no network, no cloud, no model calls, with every safety property pinned by a named test.
- Nobody installs anything. No bot in the call, no dashboard to check. A user adds a Chat app and their subscription is provisioned for them.
What we learned
If a rule can only be expressed in a prompt string, it is not a rule yet. A prompt is guidance that degrades under adversarial input; a validator holds for every input, including ones nobody imagined.
Absent beats disabled. A capability that doesn't exist cannot be re-enabled by a mistake — which is why "no write path" is a stronger claim than "writes are turned off".
Put the access rule inside the query. A post-filter is one forgotten branch away from a leak; a query predicate cannot be forgotten, because a query without it returns nothing rather than everything.
Fewer true edges beat more plausible ones. A guessed dependency corrupts exactly the answer the graph exists to give, and does it invisibly — the ordering still looks confident.
Verify the thing that distinguishes success from failure, not the thing that's easy to observe. "Commitments appeared" is not a successful backfill; "at least one commitment has mentions from different meetings" is.
Design for the tenant you actually have. Keeping transcript retrieval behind a seam with fixture and live implementations meant the entire deployed pipeline was exercisable end to end while Workspace approvals were still pending.
What's next for Weave
Per-user OAuth consent. Move delegated access from one central identity to consent each person grants at onboarding and can withdraw themselves. The seam already exists: the search principal carries a credential reference and the broker resolves credentials per subject.
Anticipatory, self-healing discovery. Anchor to the calendar so Weave knows which meetings are coming, who was invited and what the agenda says — sharpening extraction, pre-staging context, and turning no transcript into a known state rather than silence. Add a second Drive-side signal and a watermark sweep, and every gap closes itself on the next pass.
Gemini Enterprise as a second surface. The copilot is already a separate Agent Engine deployment taking its principal from the platform, so this is registration and verification work rather than code — and it fails closed either way: an unmapped identity yields a useless agent, never a leaky one.
An open ecosystem of context sources. Adding a provider is one subclass and one config entry. The same interface opens onto Jira, Confluence, GitHub, Linear, Notion and Slack, so a commitment arrives with the ticket it belongs to and the page that specifies it — still filtered to what its owner can already see.
Live mode. With the explicit consent of everyone present, Weave can confirm a commitment while the room is still there: "Alex, is the rollout checklist yours?" The single largest source of dropped work is the item nobody answered, and in the room that costs one sentence to fix.