The permission ceremony

Cloud import, Google Meet. What it costs the researcher today, why it costs that, and the one choice actually worth putting on screen.

Drawn 17 Aug 2026, from the first live end-to-end run. Companion to docs/design-cloud-import.md.

The premise being tested. Import exists to be easier than downloading by hand. If it is not, it is worse than nothing — it is a second way to do the same job, with its own bugs. So the question is not "is this consent reasonable?" It is "how many clicks, against the manual route it replaces?"

1. Count them

One batch, twelve interviews. Every click, end to end.

StepWho asksClicks
Tick the recordings you wantBristlenose12
ImportBristlenose1
“Bristlenose” Wants to Use “accounts.google.com” to Sign In → ContinuemacOS1
Choose an account → your accountGoogle1
Bristlenose wants to access your Google Account → Allow accessGoogle1
Select the same twelve files again, in Google’s PickerGoogle12
InsertGoogle1
Total29

The manual route it replaces is about the same. Open Drive, then per file: click, download. Twelve files ≈ 24–30 clicks. We have not made this easier. And that is one batch — the fixed five recur on every subsequent import, because none of the grant survives the window closing.

The worst line is the sixth. The researcher picks the recordings in our list, and then picks the identical set again in Google’s Picker. That is not a consent step they are reading; it is the same decision, taken twice, in two different list UIs. Everything else on this table is defensible. That line is not.

2. Why it is like that

ScopeWhat the researcher is shownGoogle tierWhat it costs us
drive.file
today
“See, edit, create, and delete only the specific Google Drive files you use with this app” non-sensitive Nothing. No verification, no audit.
But the grant binds to file IDs — so a new file always means a new Picker.
drive.meet.readonly “View Drive files created or edited by Google Meet” restricted Google OAuth verification, certainly. A CASA security assessment — developer-paid, annual — only if the third-party-server clause bites. See below; unresolved.
drive.readonly “View and download all your Drive files” restricted Whatever the row above costs, exactly.

That last row is why there is no three-way choice. drive.meet.readonly and drive.readonly sit in the same restricted tier, so whatever verification and assessment burden one carries, the other carries identically — but one says “files Google Meet made” and the other says “all your Drive”. Paying the same toll for the worse sentence would be indefensible. If we ever go restricted, we go Meet-only. “All your Drive” is off the table permanently, not deferred. This conclusion does not depend on the open question below — it holds whichever way that lands.

The open question: does the assessment apply to us at all?

Google’s condition is “requests access to restricted data and has the ability to access data from or through a third-party server”. Bristlenose downloads Drive → the researcher’s own Mac; there is no Bristlenose server in that path, which reads like exemption. What muddies it is the pipeline’s LLM call — whether transcript text derived from a restricted-scope file counts as that data transiting a third-party server is an assessor’s call, not one to settle from a doc. If it does apply: the developer engages a Google-empanelled assessor and pays them directly (Google is not party to the fee), commonly $500–$4,500 for an app of this complexity, repeated every 12 months. Verification itself is required either way.

And it cannot be folded into the first sign-in. Google refuses a Picker authorization that carries any other scope — the calendar scope this same app already holds included — so the media grant is structurally a second round trip. It also binds to specific file IDs, which do not exist until the researcher has a list to pick from. Asking at sign-in is not a thing we are choosing not to do; it is not available.

3. Do the free thing first

Three defects, all ours, none needing a scope change or a penny of audit.

FixWhereEffect
Seed the Picker with the whole listing, not just the ticked rows requestMediaGrant One grant covers everything in the window. The double-selection collapses to a single “Allow access”.
Skip the Picker when every wanted file is already granted requestMediaGrant — it never consults grantedFileIDs Re-importing, or a second batch inside one listing, asks nothing at all.
Persist the grant across window opens openLive builds a fresh source each time and drops it Tomorrow’s import of the same study asks nothing.
Stop forcing the consent screen prompt=consent, hardcoded Google may skip the screen entirely for a grant it already holds. Verify against a live account — this one is a claim, not a measurement.
JourneyFirst importEvery one after
Today2917
After the free fixes1713
With drive.meet.readonly1613

Which is the finding. Once the double-selection is gone, the restricted scope buys one click. The ceremony is not really the consent — it is that we make the researcher choose the same files twice. Fix that and the scope question gets much less urgent, and can be decided on its merits rather than under duress.

4. Decided: we stay on drive.file

Correction, and it matters: there IS a gate — just the free one. An earlier draft of this page said that on drive.file there was no Google gate at all, reading only the Drive half. Import does not use drive.file alone. The listing needs calendar.events.readonly and the Meet conference-records scope, and both are sensitive. Google’s rule is verification for sensitive or restricted — so verification applies to Bristlenose today, whatever we do about Drive.

What that costs is the point: sensitive-tier verification is review, written justification, a demo video, and weeks of latency — no security assessment, no assessor, no fee. It is time, not money. The drive.file decision still avoids the paid tier completely, which was the thing worth protecting; it does not avoid verification, because nothing that can list a Meet recording does.
The Drive half, accurately. Google’s verification page: “If your app utilizes only non-sensitive scopes, it is not mandatory for your app to complete the app verification process.” drive.file is non-sensitive, so it adds nothing to the burden the calendar and Meet scopes already carry. Choosing a restricted Drive scope would add the paid assessment on top. That is the whole delta, and it is the delta the decision below turns on.

This page reached that late, because the restricted-scope framing got carried past the decision that ruled restricted scopes out. Sections 2 and 3 are true and, for the chosen path, mostly beside the point. Kept because the next person will ask the same question and deserves the working, not the conclusion.

Google’s one real lever is the OAuth client, which it can restrict for policy violation. That is independent of Apple, who review the app and audit nobody’s Google-API compliance. Small surface on a non-sensitive scope; not nil, but not a process we have to pass. And GoogleOAuthConfig.resolve reads the client ID from UserDefaults before the Info.plist, so a customer whose admin objects to ours can point the app at one they registered themselves — an internal-only app being exempt from everything.
Settled 17 Aug 2026. Bristlenose will not buy a recurring security assessment to save a click. That pricing is a footnote on an enterprise line item and a material share of an indie developer’s income; the two are not the same decision wearing different numbers. So the restricted scopes are out — not deferred, out — unless the exemption question below comes back “no assessment required”, which costs nothing to ask.

Which makes section 3 the entire answer rather than a first step. The scope was never the problem. Three implementation gaps were, and none of them needs Google’s permission to fix.

What the steady state looks like once they are fixed. First import of a study: one grant, covering every recording in the listing. Every subsequent import of those recordings: nothing asked at all. New recordings appearing later: one grant, covering the new ones only. That is a perfectly good product — and it is reachable with a non-sensitive scope, no verification, and no annual anything.

The one number that decides whether that is true is whether a drive.file grant persists server-side at Google, so a stored refresh token keeps reading a previously-picked file indefinitely. If it does, the above holds. If it expires, the ceiling is lower and we should know that before promising it. Unmeasured — and the first thing to measure.

Parked: the choice, if the exemption question ever unblocks it

Settings ▸ Cloud Import. Not a first-run question — nobody can answer this before they have felt the cost. Drawn because it is cheap to keep, not because it is next.

Cloud Import

Google Drive access

How much of your Drive Bristlenose may read when it fetches recordings.

Only the files I pick

Bristlenose asks Google for permission each time you import recordings it has not seen before.

“See, edit, create, and delete only the specific Google Drive files you use with this app”

Anything Google Meet made

Ask once. Covers recordings, transcripts and meeting notes — and nothing else in your Drive.

“View Drive files created or edited by Google Meet”

Changing this signs you out of Google. You can revoke either at any time in your Google Account.

Each option quotes Google’s own consent sentence verbatim. The researcher is going to read that exact string on the next screen, and recognition is the whole mechanism — the same reason the MCP pane quotes macOS’s own dialog wording rather than paraphrasing it. Our label says what it means to them; the quote says what Google will say.

Why the narrow one stays the default

Not timidity — a Workspace admin can block restricted scopes outright, so the per-file path has to keep working on a client tenant whatever we prefer. A default that some researchers simply cannot use is not a default.

Why it is not a first-run question

“Which OAuth scope would you like?” is unanswerable before you have felt the cost of the narrow one. The honest place is Settings, discovered after the second or third import — and pointed at from the import window once we can see the researcher re-granting repeatedly.

5. What is actually next

GateCostsState
Does a drive.file grant persist server-side? Decides whether “ask once per recording, ever” is realnothing — one live testunmeasured — do this first
Whether prompt=consent is genuinely required by the one-pick flownothing — delete it and watchunmeasured — the code comment lumps it with trigger_onepick
The three free fixes land, and the journey is re-counted against a live accountengineering onlynot started
Ask Google whether the assessment applies to a client-only app at allnothing — an emailunresolved. The only thing that could revive the parked radio
CASA assessment, annually$500–$4,500/yr, developer-paiddeclined — not a cost this product carries
OAuth verification for a restricted scopetime + the abovenot pursued, consequent on the row above

Every live row costs nothing but attention. That is the useful outcome of this exercise: the expensive option turned out to be both unaffordable and nearly pointless — worth one click once the free work is done — and the free work was sitting there the whole time being mistaken for a stepping stone.

The two “unmeasured” rows are marked so on purpose. This feature has already had six reasoned-not-measured claims turn out wrong on contact with a real tenant. Neither belongs in a plan as an assumption, and both are answerable in an afternoon.