agentleFS
metratonmetraton/gaia/skills/gmail-policy/SKILL.md

gmail-policy

Skill4 starsChanged 4 months agoMITView on GitHub

Use when managing Gmail messages, labels, or email workflows via gws CLI or Gmail MCP tools

The file

We split this skill into the rules it gives an agent

12 rules. About 0 are common, 8 are distinctive, and 4 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈8

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 4

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    Am I in a proactive triage context?: If I was just reviewing the inbox, I have permission to generate drafts ahead of being asked, then …

    line 19
  2. Distinctive:

    If you're uncertain, ask once.

    line 43
  3. Distinctive:

    Read inbox, identify threads that clearly need a response

    line 50
  4. Distinctive:

    If draftable -- draft it. Store the draft in Gmail.

    line 52
  5. Distinctive:

    Do not generate drafts proactively outside triage context.

    line 57
  6. Very specific:

    When you do create a draft, verify it with gws gmail users drafts list and report the draft ID and snippet to the user.

    line 87
  7. Distinctive:

    Before asking the user for a datum (RUT, dirección, cuenta bancaria, etc.), check these sources in order:

    line 91
  8. Distinctive:

    Only ask the user for data not found in any source

    line 96
  9. Distinctive:

    When you find data in another thread, cite the source: "Tu RUT lo saqué de un correo de Colmena del 2024-03." This builds trust and shows …

    line 98
  10. Very specific:

    Use +reply for quick operational replies where formatting does not matter.

    line 179
  11. Very specific:

    When an email contains a specific date/time deadline (bill due date, event, appointment): create a calendar event AND label the email …

    line 222
  12. Very specific:

    Use gmail.modify scope (read + label + move, no delete).

    line 240

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Gmail Policy

Reading User Intent Before Acting

The most common mistake is treating every email-related request as an execution command. Before touching a single API, ask: is the user giving me context, or is the user giving me a command?

This is a reasoning step, not a checklist. Run it silently before every response.

The Four Questions

  1. Context or command? Is the user describing a situation, or directing an action?
  2. If command -- explicit or ambiguous? Explicit means the verb leaves no doubt (send, dile que sí y envíaselo). Ambiguous means the verb could be draft or send.
  3. Reversible or sensitive? A simple scheduling reply is reversible. A lease acceptance, financial form, or commitment with a third party is sensitive -- draft first unless the user explicitly says send.
  4. Am I in a proactive triage context? If I was just reviewing the inbox, I have permission to generate drafts ahead of being asked, then present them.

Intent Classification Table

This table is the single source of truth for how an email-related utterance maps to an action. Every process that reacts to a user trigger (including gmail-triage's "Check My Mail") resolves the intent here first, then runs its process. The user-utterance column stays in the user's own words (Spanish); the interpretation and action are the policy.

User utteranceReal intentCorrect action
"necesito analizar un correo y enviar unos correos importantes"Context -- describing a plan, not executing itSend nothing; wait for the specific command
"chequea mis correos y ve si hay algo importante"Review + initiative grantedRead inbox, triage, generate drafts for threads that merit a reply, present the list to the user
"dile que aceptamos y envíaselo"Explicit send commandCreate and send directly (one T3 cycle, not draft->send)
"mándale un correo a X diciéndole Y"AmbiguousAsk: draft to review, or direct send?
"respóndele a Assetplan aceptando"Ambiguous, leans to draftDefault to draft when the content involves personal data, commercial decisions, or forms
"dile que llego a las 5pm"Simple command, reversible contentDirect send is fine, no draft needed
"prepara una respuesta para X"Explicit draftCreate draft and report

The "review + initiative granted" row is the interpretation gmail-triage depends on: a general review trigger ("chequea mi mail", "¿algo nuevo?") means review with initiative, not a bare listing. gmail-triage owns the PROCESS that follows; the meaning of the trigger is defined here.

The Anti-Drift Rule

There is no fixed pipeline where every send goes through draft→approve→send. That workflow exists as a safety net for sensitive cases, not as the default for every email. When the user says "envíaselo", they mean send -- one T3 approval, one action, done.

The question is not "should I always draft first?" The question is: what did the user actually ask for, and how reversible is this action?

If you're uncertain, ask once. Do not silently choose draft when the user said send.

Proactive Draft Generation (Triage Context)

During a triage or inbox review session ("chequea mis correos", "ve si hay algo importante"), the user grants implicit permission for proactive drafts. You do not need to ask for approval before creating each one.

Pattern:

  1. Read inbox, identify threads that clearly need a response
  2. For each, assess: does the reply require user input I don't have, or can I draft a reasonable response from context?
  3. If draftable -- draft it. Store the draft in Gmail. Note the draft ID.
  4. At the end of the review, present the complete list: "Generé 3 drafts: [subject 1], [subject 2], [subject 3]. ¿Quieres revisarlos?"

The user reviews and approves individual drafts before sending. The generation step does not require one-by-one confirmation -- the presentation step does.

Do not generate drafts proactively outside triage context. If the user opens a conversation about a single email, default to their explicit instruction.

Autonomous Action Boundary

This is the security rule that decides which email operations may run without asking and which must always be proposed. It governs every gmail-triage mode. Gmail operations never run unattended: triage always happens in a session with the user present, and a routine only reminds the user to start one (see "Making It a Routine" in gmail-triage). It lives here, in the policy layer, precisely because a process must not be the only home of the rule it obeys.

Permitted automatically (no per-item approval) -- moves that are mechanical AND reversible, where nothing is lost and the state can be flipped back:

  • Classifying a new email into _gaia/action or _gaia/waiting on a clear signal (addLabelIds).
  • The reversible state swaps: action -> waiting when the user replies, waiting -> action when a third party replies. Relabeling (removeLabelIds of the old state + addLabelIds of the new) is a swap, not a destruction.
  • Staging unprocessed mail into _gaia/pending.

Prohibited automatically (always a proposal, never executed alone) -- operations that are destructive OR a matter of criterion:

  • Moving to _gaia/trash, marking spam, unsubscribing, deleting.
  • Deferring to _gaia/someday -- a judgment call, not a mechanical classification.
  • Clearing a label to mark a thread done (here removeLabelIds destroys state).
  • Sending a message. (Proactive drafts for review are allowed in triage under the grant above.)

Unattended runs do not exist, so there is no report-instead-of-execute path: a prohibited operation is proposed to the user in the live session and runs only on their approval.

Sending: When Draft and When Direct

ScenarioDefault action
User says "envíaselo" / "mándalo" / "dile que sí y envíaselo"Send direct -- T3 approval for send, not for draft then send again
User says "prepara una respuesta" / "redacta"Draft
Reply contains PII (RUT, cuenta bancaria, dirección, DOB)Draft even if user said "mándale" -- confirm before send
Reply is a business commitment (arrendamiento, contrato, formulario)Draft unless user explicitly says send
Simple logistics (hora, confirmación de asistencia, "llegaré tarde")Direct send fine
Ambiguous command + first time with this recipientAsk once

When you do create a draft, verify it with gws gmail users drafts list and report the draft ID and snippet to the user. This closes the loop.

Multi-Source Data Completion

Before asking the user for a datum (RUT, dirección, cuenta bancaria, etc.), check these sources in order:

  1. Other Gmail threads (priority 1) -- search for related threads. A user's RUT might appear in a Colmena thread. A property address might appear in a previous landlord thread. Connecting emails is the preferred path.
  2. Local structured documents -- ~/Documents/personal/**/data.json, spreadsheets
  3. PDFs -- notarial documents (compraventa, hipoteca, tasación) carry DOB, nationality, m², civil status
  4. Only ask the user for data not found in any source

When you find data in another thread, cite the source: "Tu RUT lo saqué de un correo de Colmena del 2024-03." This builds trust and shows the search was real.

PII Hygiene

Any .eml or temporary file containing PII (RUT, cuenta bancaria, teléfono, DOB, dirección) must be deleted with rm after the draft is created. Verify deletion with Glob or ls. Report: "Archivo temporal eliminado."

Protection Lists

Some senders must never be swept to _gaia/trash in a bulk operation, even when the user says "manda todo a trash". These are the never-trash protection lists. Before executing any bulk trash, filter the id set against them and exclude matches, then tell the user what you held back.

Never-trash senders (hold back and flag):

CategoryExamples of protected senders
Banks / financialBci, Santander, BancoEstado, Falabella, Tenpo, Fintual, credit-card issuers
Government / taxSII, Registro Civil, ChileAtiende, municipalidad, notarías
Health / insuranceColmena, Cruz Blanca, Isapre, clinics, lab results
Legal / contractslandlords, property managers, notaries, anything with a contract or deadline
Direct human correspondencereal people in CATEGORY_PERSONAL, not automated senders

Rule: a bulk trash sweep operates on promotions, social, forums, and repetitive automated updates. A protected sender is excluded from the sweep even inside a matching category, and surfaced separately: "Mandé 1240 promos a trash. Retuve 3 de Bci y 1 del SII — ¿los revisamos?"

These lists are heuristics, not a hardcoded allowlist — extend them from what you learn about the user's real senders. When in doubt whether a sender is protected, exclude it and ask; a false hold-back costs one question, a false trash costs a lost bank statement.

Sweep Shield: never select a protected message in the first place

Filtering protected senders out after listing is fragile — the safer mechanism is to shape the sweep query so it never selects them. Two heuristics, applied to the query before any id is fetched (the concrete Gmail queries live in reference.md, "Sweep Shield"):

  • Domain shield (-from:<domain>) for transactional senders. Append a negative sender clause for each protected/transactional domain (banks, SII, health, notaries) to the sweep query, so their mail is excluded at search time. The shield is the mechanism; the never-trash lists above are the policy it enforces.
  • Keyword guard for mixed senders. Some senders emit BOTH promo blasts and real transactional mail from the same address — Ticketplus is the canonical case (marketing plus real ticket / purchase confirmations). A blanket -from: would either protect the promos too or trash the real ticket. Guard by keyword instead: sweep the sender's promos but hold back any message matching a purchase keyword (compra, pago, entrada, comprobante, boleta, orden, factura).

Security Tier Classification

OperationTierNotes
gws gmail users messages listT0Search/filter messages
gws gmail users messages getT0Read message content
gws gmail users labels listT0List available labels
gws gmail users labels getT0Read label details
gws gmail +searchT0Macro search (syntactic sugar over list)
gws gmail users messages modify --addLabelIdsT0Add any _gaia/* label (non-destructive)
gws gmail users messages modify --removeLabelIdsT2Changes message visibility
gws gmail users messages batchModify --json '{"addLabelIds":[...]}'T0Bulk add _gaia/* label to up to 1000 ids (non-destructive)
gws gmail users messages batchModify --json '{"removeLabelIds":[...]}'T2Bulk visibility change over up to 1000 ids — confirm count + destination first
gws gmail users messages modify (action→waiting after send)T2Auto-transition after user reply -- logged, no approval
gws gmail users drafts createT3Creates draft on user's behalf
gws gmail users drafts listT0Verify draft was created
gws gmail +reply --message-id --bodyT3Sends reply on user's behalf
gws gmail users messages send --paramsT3Sends/replies via raw RFC 2822
gws gmail users labels createT3Creates new label

Blocked Operations

Permanently denied by the hook -- gmail.modify OAuth scope excludes delete at the API level.

OperationReason
gws gmail users messages deletePermanent, unrecoverable
gws gmail users messages trashMoves to trash (use _gaia/trash label instead)
gws gmail users messages purgePermanent purge
gws gmail users drafts deleteDraft deletion

Macro Prefix Handling

gws CLI exposes convenience macros prefixed with + (e.g. +reply, +send, +search). The hook strips the leading + before the verb taxonomy lookup inside hooks/modules/security/mutative_verbs.py::detect_mutative_command, so each macro classifies like its base verb:

  • gws gmail +reply → token reply → match in MUTATIVE_VERBS → T3 block
  • gws gmail +send → token send → match in MUTATIVE_VERBS → T3 block
  • gws gmail +search → token search → match in READ_ONLY_VERBS → safe

Fix applied 2026-04-17 in hooks/modules/security/mutative_verbs.py after a +reply invocation slipped through as "safe by elimination" during a Gmail session.

Sending Replies

When to use +reply vs send --params

Use caseCommandProsCons
Simple plaintext replygws gmail +reply --message-id <id> --body "<text>"Simple, handles threading headers automaticallyPlaintext only, no HTML, no collapsed quote, no signature
HTML reply with signature + collapsed quotegws gmail users messages send --params '{"userId":"me","threadId":"<tid>","raw":"<base64url>"}'Full control over MIME, looks native in GmailMust construct RFC 2822 manually and base64url-encode

Use +reply for quick operational replies where formatting does not matter. Use send --params when the recipient will see the mail in a mail client and visual quality matters.

For the correct gws gmail users drafts create schema, RFC 2822 template, base64url encoding pipeline, and other technical patterns -- see reference.md in this skill directory.

Label Convention

Workflow Labels (Layer 0 -- _gaia/*)

LabelPurposeLifecycle
_gaia/actionI need to do something (respond, pay, read)Clears when user acts → moves to waiting or removed
_gaia/waitingI already acted, waiting for the other partyClears when other party responds → back to action or removed
_gaia/somedayInteresting but no urgency (promos, articles, ideas)Resurfaces in weekly review, user clears manually
_gaia/pendingStaging area during mass triageEmpties during triage sessions
_gaia/trashSoft deleteAccumulates, user reviews

No _gaia/* label = processed/done. No extra label needed.

State Transitions

inbox ──→ action   (user or AI: I need to act)
inbox ──→ waiting  (AI detects user already replied in thread)
inbox ──→ someday  (user defers, no urgency)
inbox ──→ trash    (not wanted)
inbox ──→ pending  (mass triage staging)

action  ──→ waiting  (user replied/acted → auto T1 transition)
action  ──→ done     (handled, no follow-up → remove label)
action  ──→ someday  (user defers)

waiting ──→ action  (other party replied → needs user attention)
waiting ──→ done    (resolved → remove label)

someday ──→ action  (user decides to act)
someday ──→ trash   (not worth it)
someday ──→ done    (reviewed, no action needed → remove label)

pending ──→ {action, waiting, someday, trash, done}  (triage output)

Calendar Rule

When an email contains a specific date/time deadline (bill due date, event, appointment): create a calendar event AND label the email _gaia/action. The calendar is the time-trigger; the label is the state-tracker.

Content Labels (Layer 1)

CategoryLabels
FinanceFinance/Bank, Finance/Transfers, Finance/Insurance
JobsJobs/Alerts, Jobs/Academic
ShoppingShopping/Promos, Shopping/Orders
MusicMusic/Nucleo, Music/DJ
SocialSocial/LinkedIn, Social/Facebook
ServicesServices/Subscriptions, Services/Utilities
TechTech/Programming, Tech/SalesForce
PersonalPersonal/Notes, Personal/Travel, Personal/Downloads
Legacy_gaia/legacy -- retired: Buzz!!, Isercon, WaReS, +1, multi-forward, GDrive, PokerStar

OAuth Scope

Use gmail.modify scope (read + label + move, no delete). Full access scope (https://mail.google.com/) is blocked -- it includes delete permissions that bypass both hook and label controls.

Related Skills

  • gmail-triage -- interactive triage workflow
  • gws-setup -- CLI installation and authentication

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.