The Day 46 Confidential Transcripts Reached Main

Oct 16, 2026 · 6 min read

The Day 46 Confidential Transcripts Reached Main
_ □ ×

Two days after we shipped a four-layer confidentiality gate for meeting transcripts, a documentation pull request merged 46 transcripts that gate would have held. The gate itself worked fine. It just wasn’t standing anywhere the pull request had to pass.

This is the story of how that happened, what was in those files (in categories only, for reasons that will be obvious), and what I’d tell anyone who is about to point an AI at their company’s meetings.

Why transcripts are the dangerous part of a company brain

Our company brain is a git repo that Claude Code reads and writes. Emails, docs, call notes and meeting transcripts all live in folders per client, and a search index sits on top so anyone on the team can ask a question and get an answer with sources. Right now it holds 727 transcripts.

Transcripts are the richest source in there and the riskiest by a wide margin. An email is written to be read by someone else. A meeting is people talking, and people say things out loud they would never type: a raise that hasn’t been announced, a health issue mentioned in the five minutes before the agenda starts, a password read off a screen so a colleague can log in. A transcription bot captures all of it with equal enthusiasm.

The old job that pulled transcripts into the repo had none of this in mind. It also clipped every transcript at 6,000 characters, which is about a tenth of an hour-long call, and when it couldn’t reach its source it logged a warning and reported “nothing to sync” forever. It never crashed. It was confidently and silently wrong, and that turned out to be the pattern I had to design against.

The gate we built

In late September we rebuilt the ingest as an hourly cloud job, with four layers between a recording and the repo:

  1. Attendee routing. A meeting with zero external attendees is held, no model call involved. Internal identity comes from the team roster, never from email domains, because personal Gmail addresses show up in about twenty of our contact records and routing on them would spray one client’s call across every folder with a Gmail contact.
  2. A word-list scan. Deterministic, bilingual, standard library only. It reports a category, a line number and a rule. It never stores the matched text.
  3. An LLM classifier that can only hold. It holds on sensitive content, and it also holds on uncertainty, low confidence, timeouts, API errors, malformed output, refusals and truncation. There is no path where the model releases something the earlier layers flagged.
  4. A CI re-scan on every pull request that adds a transcript, whoever adds it. This is the one layer that doesn’t depend on a model behaving correctly.

The most useful thing I learned building it: seven defects turned up just from running the code, and five had the same shape. An empty roster, a bad config value, a result object created with no arguments. Each one quietly produced a CLEAN verdict. In a pipeline that’s supposed to fail closed, a defensive default is a bug unless it defaults to HOLD.

We also wrote the leak runbook before go-live, while the failure was still hypothetical. That felt a bit paranoid at the time.

How 46 transcripts got around it

The ingest job wasn’t the problem. The job was in shadow mode, committing nothing.

The leak came through the side door. Transcripts had also been committed by hand into client folders on a working branch, outside the job, and they rode along in a big docs pull request with scoping notes and reference pages. Our repo has a docs lane where documentation-only PRs auto-merge. That one auto-merged about one second after it opened.

The CI re-scan (layer 4) existed. It had a path filter, and it wasn’t a required check. Auto-merge doesn’t wait for checks that aren’t required. So the merge went through before the scan had even started.

When we ran the scan locally afterwards, it held all 46 files. The flags fell into three categories: staff compensation and employment details, private personal information, and four transcripts containing what looked like credentials.

What we did, and what we couldn’t undo

We removed the files from main fifteen minutes after the merge. They stayed on disk and on the working branch, so nothing was lost for the people who were entitled to it.

And they’re still in git history. This is the part people underestimate. git rm removes a file going forward. Every earlier commit still contains it, GitHub serves the old objects by SHA, and the pull request page has its own copy of the diff. Actually purging it means a history rewrite and a force-push against a protected branch (which breaks every open branch cut from that range), plus a support request to GitHub for the cached objects and the PR page. And if your search index already ingested the files, it keeps answering questions from them until you reconcile it too.

The repo is private and the audience was the team, which is why this was an incident and not a disaster. But “only the team can see it” is exactly the problem when the content is a colleague’s compensation.

The fixes

Five days later the gate became a required status check on every PR. We dropped the path filter, because a required check that only runs on some paths never reports on the others and blocks every PR forever. On a PR with no transcripts it runs a self-test and passes in seconds. The override is a transcript-reviewed label, because a human applying a label is an auditable act and a commit message isn’t.

Then we closed the side door. Day-end no longer writes transcripts to paths it derives from meeting titles. Everything goes through one filing script that runs the same content scan, files clean transcripts and parks held ones in a gitignored folder. Never into the tracked tree, either way.

What the gate costs

I want to be honest about this part. The ingest ledger has 110 rows so far. 104 held, 5 errored, 1 cleared, and that one was in shadow mode. The hourly commits read “0 would file, 2 held” over and over.

Most holds are routing, not content: 41 meetings had no attendee list, 29 were internal only, 16 had no matching client folder, 9 matched more than one client. Those are cases where the system can’t prove where a transcript belongs, so it refuses to guess.

That’s annoying. It means people still file a lot of meetings by hand, through the gated script. I’ll take it. A held transcript costs someone a few minutes of filing. A misfiled one puts one client’s call in another client’s folder, which our runbook treats as the same incident as a leak, because it is.

The other cost is invisible loss. A held transcript looks identical to a meeting that never happened. So day-end now reads the ledger and lists every held meeting in its window, by category code only. If you build a gate, build the report of what it stopped.

Leaks that aren’t files

Transcripts are the loud case. The quieter one is the brain carrying knowledge from one client into another client’s work.

Every lesson in our learnings store has a client_safe flag. A learning from one client never shows up in another client’s materials unless it has been generalized first. And before anything leaves the company (proposals, decks, and every drafted email), a leak-check pass reads it as the recipient would, looking for seven categories: other clients’ identities, internal economics, legal identifiers, credentials, stale commercial language, negotiating position, and things a third party told us in confidence.

None of this is exotic. It’s the same discipline a careful person applies before forwarding an email. The difference is that a company brain forwards things at machine speed, so the care has to be built into the path instead of remembered.

What I’d tell you before you connect your meetings

Put the check where the content has to pass, and make it required. A safety layer that runs next to the path is a report, and you’ll read it after the merge.

Default to holding on anything ambiguous, errors and timeouts included. Then build the list of what got held so a human can clear it.

Write the cleanup runbook before you need it, and include git history and your search index in it.

This is one reason the company brain I describe here spends so much of its code on governance. If you want one for your team with the access rules built in from day one, that’s what I build.

More Thoughts

Before Automating Something, Ask Whether It Should Exist

Oct 16, 2026 · 6 min read

The first verdict our build gate ever gave was DON'T, for a vacation reminder job. It got the right answer by asking the …

Read more

Eight Small Ways Our Tools Lied to the Brain

Oct 16, 2026 · 5 min read

The tools feeding our company brain broke it more often than the model did: transcription, email, notes and auth kept …

Read more

Our AI Created 1,042 Tasks in Four Months

Oct 16, 2026 · 6 min read

Between June and September our company brain created 1,042 tasks from our email and meetings. Most of them were noise. …

Read more

Our AI Drafts Every Email and Sends None of Them

Oct 16, 2026 · 5 min read

In four months our company brain surfaced 626 email threads where we owed a reply, and it can draft any of them. It …

Read more