---
name: sync-curated-content
description: Pull new newsletter mail from the +newsletter Gmail alias, triage for high-signal content, and land it in <your-agent-config> # sanitized Runs Tue+Fri alongside sync-claude-sessions. Invoke when Andrew says "sync newsletters", "ingest newsletters", or "run curated-content sync".
---

# sync-curated-content — newsletter ingestion pipeline

Pulls mail that arrived at `<email> # sanitized` since the last run, extracts only the high-signal claims, and appends them to the knowledge base. Less is more: 2 high-signal items beat 20 mediocre ones.

## What it does

1. **Read the watermark.** Load `<your-agent-config> # sanitized`. On first run the file will not exist — treat `lastInternalDate` as `0` and `lastThreadId` as `null`. The watermark records the last thread date (Unix ms) and ID processed so the query stays incremental.

2. **Search Gmail for new threads.** Call `mcp__claude_ai_Gmail__search_threads` with query:

   ```
   to:<email> # sanitized after:<YYYY/MM/DD>
   ```

   Derive `after:` from `lastInternalDate` (convert epoch ms → `YYYY/MM/DD`, subtract one day as buffer). If the `newsletter` Gmail label exists, also accept threads carrying that label. Page through results if needed; collect all thread IDs not yet seen (`threadId !== lastThreadId`).

3. **Fetch and triage each thread.** For each thread ID, call `mcp__claude_ai_Gmail__get_thread` to retrieve full body. Then apply the triage criteria below. Mark the thread INGEST or DROP. Never re-fetch a thread you already processed in this run.

4. **Ingest high-signal threads via add-new-resource.** For each INGEST thread, DELEGATE to the
   `add-new-resource` skill — do NOT hand-write `raw/` or `wiki.md` here, so the KB keeps ONE format.
   Pass add-new-resource the thread body as pasted text plus a title of the form `<Subject> (<Source>)`.
   It stores the verbatim raw file, extracts the key claims, updates `wiki.md`, and emits any
   NEED-SIGNOFF flags. Collect its result for the summary count. (The `type` it records is `newsletter`.)

5. **Update the watermark.** After all threads are processed, write back `<your-agent-config> # sanitized` with the `lastInternalDate` and `lastThreadId` of the most recent thread seen (whether ingested or dropped — the watermark tracks what was examined, not what was kept).

6. **Print the summary.**

   ```
   sync-curated-content: N pulled, M ingested, K dropped-as-low-signal
   ```

   If N = 0, print `sync-curated-content: no new threads since <last-run-date>` and stop.

## Setup (one-time)

Create a Gmail filter so newsletters are easy to query:

1. Open Gmail → Settings → See all settings → Filters and Blocked Addresses → Create new filter.
2. In the **To** field enter: `<email> # sanitized`
3. Click **Create filter**, then check **Apply the label** and select or create a label named `newsletter`.
4. Optionally check **Skip the Inbox** to keep newsletters out of the main inbox.
5. Click **Create filter**.

The `search_threads` query in step 2 above works with or without this label — but having it lets you spot newsletters quickly in Gmail and makes the query faster.

## Watermark schema

```json
{
  "lastInternalDate": 17<phone> # sanitized,
  "lastThreadId": "18f9a3c21d44e001",
  "lastRunISO": "2026-06-28T09:00:00Z"
}
```

- `lastInternalDate`: Unix milliseconds of the newest thread examined in the last run. Used to build the `after:` date in the next query.
- `lastThreadId`: The thread ID of that same thread. Used as a de-dupe anchor.
- `lastRunISO`: Human-readable ISO timestamp for logging. Not used in query logic.

On first run (file missing or empty) set `lastInternalDate = 0`, which resolves to `after:1970/01/01` — Gmail will return results bounded only by the `to:` clause, typically the last 12 months. Triage normally; ingest only high-signal items.

## Triage criteria

**INGEST if the thread contains any of:**
- Concrete tool or model release (name, capability, availability, pricing)
- Research finding with a quantitative result or falsifiable claim
- Technique or pattern description that can be applied directly
- Strategic insight about an industry shift or a platform change
- Design system, API, or SDK change with breaking or additive substance

**DROP if the thread is primarily:**
- Marketing or promotional copy (discounts, "join our community", event invites)
- Restatements of widely-known news with no new angle
- Link roundups with no synthesis or commentary
- Job postings, sponsored content, or affiliate pitches
- Content you have already ingested from another source this run

When uncertain: bias toward DROP. One strong item is worth more than five weak ones.

## KB ingestion format

Raw file (`<your-agent-config> # sanitized<slug>.md`):

```markdown
# <Title>
**Source:** <Newsletter name>
**Date:** YYYY-MM-DD
**Thread ID:** <Gmail thread ID>

## Key claims
- <claim>
- <claim>

## Notes
<optional: one sentence of context if needed>
```

Wiki entry (appended to `<your-agent-config> # sanitized`):

```markdown
## YYYY-MM-DD — <Title> (<Source>)
- <claim>
- <claim>
Raw: `<your-agent-config> # sanitized<slug>.md`
```

Keep claims tight — one sentence each. Strip HTML, tracking links, and unsubscribe footers before writing.

## When this skill is invoked

- Andrew says "sync newsletters", "pull newsletters", "ingest newsletters", or "run curated-content sync".
- The Tue+Fri data-ingestion routine fires this skill alongside `sync-claude-sessions`.
- If invoked mid-week outside the routine, still run the full pipeline — the watermark ensures no duplicates.

## Hard constraints

- **Never subscribe Andrew to anything.** Do not call any send, subscribe, or signup endpoint.
- **Never send mail.** Do not call `mcp__claude_ai_Gmail__create_draft` or any send tool.
- **Never modify existing kb entries.** Append only; never rewrite `wiki.md` lines from prior runs.
- **Less is more.** If the entire run yields zero INGEST items, that is a correct and acceptable result — do not lower the bar to hit a count.
- **Read-only Gmail label operations only.** Applying the `newsletter` label to incoming threads for organization is acceptable. Deleting, archiving, or moving threads is not.

## Troubleshooting

- **No threads returned despite known newsletters** — verify the `after:` date is correct (check `lastInternalDate` in the watermark). Also confirm the Gmail MCP session is authenticated; if `search_threads` returns an auth error, ask Andrew to re-auth before retrying.
- **Watermark not updating** — the write happens at step 5, after all threads are processed. If the run was interrupted mid-triage, re-run; threads already examined will be re-fetched but the triage result should be the same. Duplicates in the wiki are possible but harmless — they will have the same raw file path.
- **`<your-agent-config> # sanitized` or `wiki.md` missing** — the kb scaffold must exist before this skill runs. Create it with `mkdir -p <your-agent-config> # sanitized && touch <your-agent-config> # sanitized`.
- **Thread body is garbled or empty** — some newsletters send HTML-only bodies. Strip tags and collapse whitespace before triage; if the result is still unreadable, DROP the item and note the source name in the summary.
- **Slug collision** — if two threads produce the same slug, append the last 4 chars of the thread ID to disambiguate: `tldr-ai-a3f1-18f9.md`.
