Skip to content

Google Workspace read access — Drive, Gmail, Calendar, Contacts

This document captures the essential vocabulary, the real use cases, the choice of the API over an rclone mount, and the configuration guide with a strict security model. It is not an ADR — it documents how to access and why. Architecture decisions remain in ADR-0001 and ADR-0004.

“The vault” is the Google Workspace space. The “Personal Knowledge Vault” of the architecture is not something to build: it is the personal Google space — GDrive (the PARA structure: Projects, Areas, Resources, Archive) and twenty years of history, plus Gmail, Google Calendar and contacts. Nothing to migrate, nothing to synchronize. The vault already exists; Hermes needs the ability to access it in read mode.

API mode (Google Workspace skill) Mount mode (rclone, Drive Bridge filesystem)
Finding a document drive search full-text → ID → docs get ls / grep in the tree
Native Google documents (.gdoc, .gsheet) Read natively, structured On-the-fly export (.md/.csv), latency, “empty” files without a special option
Gmail / Calendar / Contacts Accessible via the API (read) Not accessible — rclone only mounts Drive
Disk footprint Zero (no local files) Bounded VFS cache (1-2 GB) but FUSE to operate (systemd, monitoring)
Search Google full-text (excellent), including in emails Local browse + grep (Drive only)

The dominant use case is “find me this document, this email, this deadline — read it, synthesize it.”

# Use case Drive Gmail Calendar Contacts Example
1 Find ✅ ✅ — — “what did I write about X?”
2 Paperwork / administrative ✅ ✅ ✅ — lease dossier: lease + template (Drive), exchanges with the landlord (Gmail), deadlines/visit (Calendar)
3 Dossier tracking ✅ ✅ ✅ — “where is my dossier X?” — cross-reference recent emails + documents + appointments
4 Deadline watch — ✅ ✅ — “what happens next week?”, missing documents
5 Templates / Resources ✅ — — — find a model, create a new one from it
6 Project synthesis ✅ ✅ — — feed a personal project with notes + emails
7 Writing (phase 2) ✅ — — — draft a post backed by notes
8 Contacts — — — ✅ find a contact, an address for a letter
9 Routines ✅ ✅ ✅ — weekly review, morning brief (cron reads docs + unread emails + agenda)
10 Filing (phase 2) ✅ — — — empty the notes-inbox and file into PARA

Concrete example (Adeo Dev Summit notes) :

  • API: drive search "Adeo Dev Summit" → ID → docs get ID → read the content.
  • Mount (alternative): ls /mnt/gdrive/Resources/ → read_file .../adeo-dev-summit.md.

The “paperwork” case in detail (the most demanding, it justifies access to all three):

  1. Hermes searches Drive for the previous lease, the template, the supporting documents.
  2. Hermes searches Gmail for the email thread with the landlord (exchanges, evidence, attachments).
  3. Hermes checks the Calendar for deadlines (notice period, visit, move-in date).
  4. Hermes produces an unified synthesis of the dossier — and, in phase 2 (writing), copies the template, fills a draft and provides the review link.

These are personal data. The scope is drawn for the least privilege possible.

This is not a promise (“Hermes won’t write”): it is a technical limitation imposed by Google. The OAuth token that Hermes holds only contains certain rights, listed in its scopes. If the skill tries an action outside this list, Google answers 403 Forbidden — impossible to bypass.

What the token can do (read only) — summary

Section titled “What the token can do (read only) — summary”
Service Scope granted What it allows Command example
Drive drive.readonly Catalogue: search, list, metadata, download/export binary files (pdf, images, md) drive search "lease", drive download DOC_ID
Docs documents.readonly Read the content of native Google Docs (.gdoc) docs get DOC_ID
Sheets spreadsheets.readonly Read the cells of native Google Sheets (.gsheet) sheets get SHEET_ID "Sheet1!A1:D10"
Gmail gmail.readonly Search + read emails (body, sender, date, attachments) gmail search "is:unread", gmail get MESSAGE_ID
Calendar calendar.readonly Read events (dates, summaries, participants) calendar list
Contacts contacts.readonly Read contact details (names, emails, phones) contacts list --max 20

gmail.readonly allows reading emails; calendar.readonly reading the agenda; contacts.readonly reading contacts. Without these scopes, files are visible but their content remains inaccessible.

Forbidden Why
Send/edit/delete emails gmail.send / gmail.modify scopes not granted — gmail send fails with 403
Create/edit/delete events calendar (full) scope not granted — calendar create fails with 403
Write in Drive (create, copy, edit, move, delete, share, upload) drive.readonly scope — drive upload / create-folder / delete / share fail with 403
Modify a Docs or a Sheets documents.readonly / spreadsheets.readonly scopes — docs create / append, sheets update fail

In short: the token is a “on-site consultation only” pass for the library (Drive/Docs/Sheets), the mailbox (Gmail read), the agenda and the address book — with zero pen: no writing, no sending, no deletion. It is Google that imposes this limit on every call, not a rule of good conduct.

SOUL rule: if content read from the vault turns out to be sensitive (tax, medical, legal, banking), Hermes warns before sending it to an external LLM. The token grants access; the content read goes to the LLM provider — these are two different things. Gmail reading is now included in this scope: emails (often more sensitive than notes) follow the same rule.

Permission crescendo — read-only first, targeted write on demand

Section titled “Permission crescendo — read-only first, targeted write on demand”

Read-only is the default posture: Hermes searches, reads, answers questions, and prepares material; every decision — and every send — stays with François. Write capabilities are added deliberately, one notch at a time, only when a real use case justifies them, never speculatively. Two mechanisms enforce this staircase:

  • The consent screen is the gate. Google only grants the scopes François explicitly approves during the browser authorization (Step 5). Widening a scope always triggers a fresh consent flow — nothing writes before François clicks.
  • Least privilege stays the norm. Each notch is granted for one validated use case and revoked when the use case disappears. Read-only remains the baseline.

Synthesis: stages, use cases, and config mapping

Section titled “Synthesis: stages, use cases, and config mapping”
Stage Use case (when it becomes real) Hermes can… Scope(s) to add Config change Validation gate
1. Read-only (current) Questions, dossier synthesis, deadlines, routines Search/read Drive, Docs, Sheets, Gmail, Calendar, Contacts; synthesize — (the 6 readonly scopes) None — Step 4 as-is François answers; no write capability exists
2. Email drafts (next, on request) “Prepare the answer, I’ll send it” Scan a thread, write a draft into Gmail Drafts, notify — never send gmail.compose Widen the SCOPES block in setup.py and google_api.py, then re-authorize (Step 5 flow) François opens the draft in Gmail, edits, presses send
3. Doc/sheet drafts (on request) Lease-renewal letter from template, weekly report sheet Copy a template, fill a draft Doc/Sheet from notes documents, spreadsheets + drive (write) Same widen + re-authorize François reviews the link and content before use
Never (without a real, validated need) Send directly, delete, share, change events — gmail.send, gmail.modify, full calendar, drive write Permanently out of scope —

gmail.compose is the narrowest scope that covers Gmail Drafts. Honest caveat: Google’s description of this scope also includes sending — so in stage 2 the “François sends” guarantee is not a Google-imposed 403 the way read-only is. It rests on two local mechanisms:

  1. Agent rule: Hermes creates drafts, never sends — the SOUL boundary extended to drafting.
  2. Hard local block (optional hardening): the same SCOPES patch that makes read-only possible can make sending impossible at the CLI level — google_api.py can be patched so gmail send exits with an explicit error even after gmail.compose is granted.
Section titled “Widening a notch — the runbook (one notch = one consent)”
  1. Declare the use case (validated need, not speculation).
  2. Add the scope to the SCOPES block in both scripts (Step-4 patch mechanics — the skill is frozen, the patch survives updates).
  3. Re-run the authorization: $GSETUP --revoke, then --auth-url (the consent screen lists the new scope — this is where François decides), then --auth-code.
  4. Verify: $GSETUP --check shows the new scope; one real API call confirms it works.
  5. Revocation is symmetric: remove the scope from the SCOPES block and re-authorize, or revoke entirely at https://myaccount.google.com/permissions.

Total ~15 minutes once, almost all on the Google Cloud side, then a browser authorization.

  1. https://console.cloud.google.com/projectselector2/home/dashboard → New project → name: hermes-workspace (or similar).
  2. Note the project ID (useful for step 3, test user).

API Library → enable exactly 6 APIs (no more):

  • Google Drive API
  • Google Docs API
  • Google Sheets API
  • Gmail API
  • Google Calendar API
  • People API (for contacts)

Do NOT enable other APIs in phase 1. No scope, no attack surface.

Section titled “Step 3 — Create the OAuth client + consent screen”
  1. Consent screen (APIs & Services → OAuth consent screen):

    • User type: External (personal Gmail account — “Internal” is reserved for Workspace spaces).
    • App name: Hermes Personal Assistant; support email: a reachable address.
    • Scopes: leave empty for now (added via the code in step 4).
    • Test users: add the Gmail address of the concerned account here (essential for the authorization to pass).

    What to fill / what to leave empty (Testing mode):

    Field Action
    App name Required — Hermes Personal Assistant
    Support email Required — a reachable address
    Developer contact email Required — same address
    App logo Optional — purely cosmetic (shown on the consent screen), no verification
    App domain / Authorized domains Skip — only relevant for web apps hosting OAuth redirect URIs on that domain. Entering a domain triggers an ownership verification (Search Console / DNS TXT), pointless for a Desktop client (redirects go to localhost)
    Privacy policy / Terms of service Skip — required only if the app is submitted for Google verification, never for Testing or a plain Production publish

    Error “Your app’s OAuth configuration is incomplete”: if the console shows it when creating the OAuth client, the consent screen (branding) has not been completed and saved yet. Return to the branding page, set at least the app name + support email, Save, and retry. The warning does not block the JSON download — but the browser authorization in step 5 will fail without a complete consent screen and the account in test users. Fix the branding before authorizing, not after.

  2. Credentials → Create credentials → OAuth 2.0 Client ID → Type: Desktop application → Create.

  3. Download the JSON file (client_secret_....json) — never send it in a chat; place it on the machine (e.g. ~/Downloads/) and provide its path.

    • The JSON never expires: it is the app’s identity (client ID + secret), downloaded once. Only the token (step 5) has a lifetime, and only in Testing mode.
  4. Publishing status: gmail.readonly is a sensitive scope at Google. In Testing mode, the refresh token expires after 7 days. Weekly re-authorization is a ~1-minute flow (step 5 again — the JSON is not re-downloaded), acceptable for validation. Once phase 1 is validated, switch the app to Production (“Publish app”): the refresh token becomes durable.

    Testing vs Production:

    Testing (validation) Production, unverified (phase 1 validated)
    Consent flow (browser auth, step 5) Required — and repeated weekly (token expires after 7 days) Required — but once (token becomes durable)
    Refresh token Expires after 7 days Durable (until revoked or ~6 months unused)
    Test users list Required (account must be added) Dropped — any account could consent in principle
    Users Hard cap of 100 test users Hard cap of 100 total users until verified
    Extra branding needed to publish — None — “Publish app” is a single click
    “Unverified app” warning — Cosmetic screen at consent time
    Google verification (logo, domain, privacy policy, security audit) Not needed Not needed for personal use — only required to remove the unverified warning for a public audience
    • Production ≠ public: publishing does not list or index the app anywhere; access still requires an explicit consent on the consent screen. In practice the 100-user cap on unverified apps with sensitive scopes limits it anyway. Publish once phase 1 is validated — nothing to decide today.

Step 4 — Restrict the scopes in the 2 scripts

Section titled “Step 4 — Restrict the scopes in the 2 scripts”

The Hermes skill requests by default LARGE scopes (gmail.send, gmail.modify, full calendar, full drive, spreadsheets, documents). For read-only, patch both files with the read-only list:

~/.hermes/skills/productivity/google-workspace/scripts/setup.py — replace the SCOPES block (lines ~47):

SCOPES = [
"https://www.googleapis.com/auth/drive.readonly",
"https://www.googleapis.com/auth/documents.readonly",
"https://www.googleapis.com/auth/spreadsheets.readonly",
"https://www.googleapis.com/auth/gmail.readonly",
"https://www.googleapis.com/auth/calendar.readonly",
"https://www.googleapis.com/auth/contacts.readonly",
]

~/.hermes/skills/productivity/google-workspace/scripts/google_api.py — same block (lines ~45):

SCOPES = [
"https://www.googleapis.com/auth/drive.readonly",
"https://www.googleapis.com/auth/documents.readonly",
"https://www.googleapis.com/auth/spreadsheets.readonly",
"https://www.googleapis.com/auth/gmail.readonly",
"https://www.googleapis.com/auth/calendar.readonly",
"https://www.googleapis.com/auth/contacts.readonly",
]

Why both? setup.py builds the authorization URL with the scopes, google_api.py uses them on refresh. If only one is patched, the refresh may fail (invalid_scope) or the consent screen may re-request unwanted rights. No secret is touched — it is only the list of requested permissions, reduced to the essential.

If a write operation becomes necessary (phase 2: copy a template, create a draft, prepare an email as draft), the scopes will need to be widened to drive + documents + spreadsheets + gmail.compose (drafts only) and a new authorization will be required — never full gmail.send/calendar without a real and validated need. Each widening is one notch of the permission crescendo (see above), gated by a fresh consent.

Why this patch is essential (and what happens if this step is skipped):

The OAuth token only has the scopes that were requested at authorization time. The authorization URL is built by setup.py from its SCOPES list:

  • Without patch: the URL requests the wide scopes (gmail.send, gmail.modify, calendar, full drive…) → the Google consent screen displays them → if the authorization is granted, the token can do everything (write, send emails, create events). No 403, the skill has the rights.
  • With patch: the URL only requests the 6 readonly scopes → the token can never write → Google answers 403 Forbidden at the slightest write attempt.

The Google consent screen does not allow unchecking individual scopes. The patch is therefore THE mechanism that makes the token read-only, not an extra safety belt. Without it, the only barrier is the skill’s behavioral rule (“do not write without confirmation”) — a good intention, not a technical guarantee.

What happens at the next Hermes update? Nothing: local modifications are preserved.

Hermes synchronizes its bundled skills (hermes update) via a hash manifest (~/.hermes/skills/.bundled_manifest):

Case Sync behavior
Unmodified skill + new version available Update applied
Locally modified skill (hash ≠ origin) Update ignored — modification preserved
Skill deleted locally Respected, not re-added

Consequence: by patching the two scripts, the google-workspace skill is “frozen” at its current version — future updates of this skill will no longer be applied automatically. This is the intended behavior: the patch will be re-applied manually if a later version brings a useful fix.

To check at any time:

Terminal window
cat ~/.hermes/skills/.bundled_manifest | grep google-workspace # the hash changes after the patch

Working directory: none required. setup.py resolves all paths from its own location and $HERMES_HOME (default ~/.hermes) — run the commands from any folder. Shell variable: GSETUP must be defined in the same shell where you run $GSETUP ... — a new terminal session starts empty. If you see --auth-url: command not found, the variable is missing (redefine it). The commands below match the installed setup.py at the time of writing. The skill is frozen locally (Step 4), so these flags are stable; if the skill is ever updated upstream, verify with $GSETUP --help before following the exact flags.

The scripts need the Google client libraries, but the VPS system python3 is PEP-668 locked (no pip). Create a dedicated venv (uv is installed) — once per machine:

Terminal window
uv venv ~/.hermes/venvs/gws
uv pip install --python ~/.hermes/venvs/gws/bin/python \
"google-api-python-client==2.194.0" "google-auth==2.55.1" \
"google-auth-oauthlib==1.3.1" "google-auth-httplib2==0.3.1" \
"httplib2==0.32.0" "pyasn1==0.6.4"

All $GSETUP / $GAPI commands below use this venv’s python. If a command fails with No module named pip or externally-managed-environment, you used system python3 — retry with the venv python.

The OAuth client is an identity (client ID + secret), not a token — it never expires. The JSON is its container. Two ways to get it:

  • Download (recommended) — Google Cloud Console → APIs & Services → Credentials → OAuth 2.0 Client IDs → the client → Download JSON. The file bundles the client ID, the client secret and the fixed endpoint fields.
  • Rebuild from client ID + secret — the JSON is a plain file; Google reads client_id and client_secret from it (the other fields have standard defaults). With the two values, write this exact structure — client type Desktop application → key installed:
{
"installed": {
"client_id": "....apps.googleusercontent.com",
"project_id": "hermes-workspace",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_secret": "....",
"redirect_uris": ["http://localhost"]
}
}

Get the file onto the VPS, then either let the script place it (recommended, step 5.2 #1) or copy it manually yourself:

Terminal window
# Manual copy — the script expects EXACTLY this name (underscores) and path:
# a differently-named file (e.g. google-client-secret.json) is ignored.
cp ~/Downloads/client_secret.json ~/.hermes/google_client_secret.json
chmod 600 ~/.hermes/google_client_secret.json

Never paste the client secret in a chat or a terminal transcript — the file stays on disk.

⚠️ Client type check: the OAuth client must be a Desktop application (installed key). A Web application client (web key) would refuse the http://localhost:1 redirect (redirect_uri_mismatch). If the wrong type was created, recreate the client as Desktop in the console.

Terminal window
# Define once per shell (does NOT persist between sessions). Python = the venv from 5.0.
GSETUP="$HOME/.hermes/venvs/gws/bin/python $HOME/.hermes/skills/productivity/google-workspace/scripts/setup.py"
# 1. Store the client secret (copies the file into ~/.hermes/google_client_secret.json)
$GSETUP --client-secret ~/Downloads/client_secret.json
# → OK: Client secret saved to /home/hermes/.hermes/google_client_secret.json
# (skip #1 if you already placed the file manually — the script will still find it)
# 2. Build the authorization URL (printed alone, ready to copy)
$GSETUP --auth-url
# → copy the printed URL, open it on the machine logged into the concerned Google
# account (e.g. M1Max), approve the 6 readonly scopes on the consent screen
# → the browser ends on "can't reach this site" at http://localhost:1 — NORMAL:
# Google redirects to the loopback address with the code in the URL
# → copy the FULL ADDRESS from the address bar (the one containing code=...)
# 3. Exchange the code (accepts the full redirect URL or the bare code)
$GSETUP --auth-code "http://localhost:1/?code=....&scope=....&state=...."
# → OK: Authenticated. Token saved to /home/hermes/.hermes/google_token.json
# 4. Verify the token locally — scopes must cover the 6 readonly
$GSETUP --check
# → AUTHENTICATED (or AUTHENTICATED (partial) if a scope is missing — at this stage,
# if (partial) lists anything other than the 6 readonly scopes, it is a problem to fix)
# 5. Verify with a real API call (Calendar) — catches disabled client/account issues
$GSETUP --check-live
# → LIVE_CHECK_OK: Real API call succeeded.

What each command does (read the script’s output with this in mind):

Persistent setup (optional) — GSETUP / GAPI die at the end of the shell session. To have them always available, append to ~/.bashrc (paths are stable: venv + frozen skill):

Terminal window
# Hermes Agent — Google Workspace skill CLI shortcuts (venv python: system python3 is PEP-668 locked)
export GSETUP="$HOME/.hermes/venvs/gws/bin/python $HOME/.hermes/skills/productivity/google-workspace/scripts/setup.py"
export GAPI="$HOME/.hermes/venvs/gws/bin/python $HOME/.hermes/skills/productivity/google-workspace/scripts/google_api.py"
Command Effect
--client-secret PATH Copies the JSON into ~/.hermes/google_client_secret.json. No network call — the JSON never expires.
--auth-url Builds the consent URL from the SCOPES list in setup.py (Step 4) — this is where the read-only restriction becomes concrete: the consent screen can only offer the 6 readonly scopes.
--auth-code Exchanges the single-use code for a refresh token (durable) + access token, stored in ~/.hermes/google_token.json.
--check / --check-live Local token inspection / one real Calendar API call.

Troubleshooting

Symptom Cause / fix
error: unrecognized arguments: --format json The installed setup.py has no --format flag (the skill’s own SKILL.md still shows it). Just drop it — the output is already machine-readable.
--auth-url: command not found GSETUP is empty in this shell — define it first (the variable does not persist between sessions).
No module named pip / externally-managed-environment You used system python3 — retry with the venv python from 5.0.
ERROR: No client secret stored. Step 1 skipped or wrong path, or the manual copy used the wrong filename (~/.hermes/google_client_secret.json, underscores).
Consent screen shows “OAuth configuration incomplete” Step 3 branding not saved, or the account missing from Test users.
redirect_uri_mismatch at authorization OAuth client is type Web application, not Desktop — recreate as Desktop.
ERROR: OAuth state mismatch. Stale pending session (google_oauth_pending.json). Run --auth-url again for a fresh one.
TOKEN_REVOKED / invalid_grant at --check Code already used / expired, or Testing-mode token expired (7 days). Re-run --auth-url + --auth-code — the client secret is not re-downloaded.
AUTHENTICATED (partial) naming a non-readonly scope Wrong scopes granted at consent. $GSETUP --revoke, then redo the flow.

Once authenticated, Hermes can:

Terminal window
# Same venv python as GSETUP (5.0) — system python3 fails with 'No module named pip'
GAPI="$HOME/.hermes/venvs/gws/bin/python $HOME/.hermes/skills/productivity/google-workspace/scripts/google_api.py"
# Drive search
$GAPI drive search "Adeo Dev Summit" --max 5
# Read a native Google Doc
$GAPI docs get DOC_ID
# Read a Sheets range
$GAPI sheets get SHEET_ID "Sheet1!A1:D10"
# Search + read emails
$GAPI gmail search "is:unread" --max 10
$GAPI gmail get MESSAGE_ID
# Agenda (next week)
$GAPI calendar list
# Contacts
$GAPI contacts list --max 20
# One-off export of a doc to .md / .pdf
$GAPI drive download DOC_ID --export-mime text/plain --output ~/doc.md
  • Never run the authorization with the default unpatched scopes: gmail.send, gmail.modify, full calendar, full drive give write and send capabilities to a token that does not need them.
  • Never send the client_secret.json or the token in a chat, a ticket, a public repo. The refresh token is a permanent access key to the Workspace (Drive + emails + agenda + contacts).
  • Never commit ~/.hermes/google_token.json or google_client_secret.json in a repo (even private). They stay in ~/.hermes/ (outside the repo), protected by the folder permissions (mode 0600/0700).
  • Backup: the daily backup (backup.sh → hermes-backup repo) never collects google_token.json or google_client_secret.json — no secret reaches the GitHub repo. A lost VPS therefore costs a re-authorization (Step 5 again, ~5 min); the client secret is the safe recovery point (kept on the local machine, and downloadable again from the Google console). Note: the running backup.sh performs no encryption — ADR-0008’s age-encrypted pipeline describes the design, not the implemented script. The token stays out of the backup until encryption exists; plaintext would defeat the purpose.
  • Dashboard: hermes.ducatillon.net must never display these files (rule already in place: no .env, no secrets in the UI).
  • Revocation: two levels — $GSETUP --revoke (removes the local token) and https://myaccount.google.com/permissions (revokes access on the Google side, definitive). When in doubt, do both. The client secret can also be regenerated in the Google console.
  • Rotation: if the VPS is compromised or if doubt arises, revoke on the Google side + regenerate the client secret + re-authorize.
  • No writing in phase 1: the skill can “upload”, “send”, “create”, etc. — these commands will fail with the readonly scopes: this is the intended guardrail.
  • No filesystem view: free tree exploration (“show me what’s there”) is not possible in phase 1. If this need becomes real → phase 2: read-only rclone mount with the same OAuth client (inject the client_id/secret into the rclone remote to have only one authorization to manage).
  • Google search ≠ local search: drive search / gmail search are full-text on the Google side (very good), but do not cover everything a local grep would (binary file content, OCR…).
  • Testing refresh token = 7 days: as long as the OAuth app is in Testing mode, weekly re-authorization (same flow as step 5, ~1 minute — the client_secret.json is not re-downloaded, it never expires; only the token does). Switching to Production (even unverified) makes the refresh token durable — the recommended path once phase 1 is validated.
  • Sensitive scopes (Gmail): gmail.readonly is classified as sensitive by Google. In unverified production, an “unverified app” warning is displayed and a 100-user cap applies — no impact for strictly personal use.
  • drive.file vs drive.readonly: the choice is drive.readonly because drive.file only covers files created/opened by the application — unusable to read 20 years of PARA history.
  • Rate limits: Google quotas are comfortable for targeted search/read. Listing/downloading the whole Drive is forbidden (no “mirror” usage).
  • Hermes local skill: ~/.hermes/skills/productivity/google-workspace/ (official doc: https://hermes-agent.nousresearch.com/docs/user-guide/skills/google-workspace)
  • Architecture skill: google-workspace-agent-integration (agent ↔ Workspace integration pattern, writing phases)
  • ADR-0001 (Drive = SSOT) · ADR-0004 (Drive docs / GitHub code) · ADR-0007 (plaintext secrets + OS hardening)