---
name: varick-content-calendar
description: "Run the Varick content calendar in Notion — generate slots, track state, fan one asset out into many, and drive drafts and carousel assets through production. Use whenever the user asks to plan, schedule, or fill a content calendar; to see what's due or in the queue this week; to expand a quarter of posting slots; to repurpose an X article into LinkedIn posts; or to produce the draft or asset for a scheduled slot. Also triggers on 'what am I shipping this week', 'add a post to the calendar', 'spin this article into a carousel', 'set up next quarter', or 'push this draft to Notion'. Handles X (articles, threads, posts) and LinkedIn (carousels, text posts, documents). Do NOT use for one-off writing with no calendar slot (anti-ai-writing), standalone charts (varick-charts), or client deliverables (varick-report)."
---

# Varick content calendar

The calendar lives in one Notion database. Nothing is typed by hand except the cadence config and the actual ideas. Four things are automated and they are genuinely separate — do not conflate them:

| | what it does | how it runs |
|---|---|---|
| **Slot generation** | cadence rules → empty dated rows | `expand_cadence.py`, quarterly |
| **State movement** | status change → next step, reminders | Notion automations (UI) |
| **Fan-out** | approved parent → linked children | `fanout.py`, daily cron |
| **Production** | row → draft → asset → back into Notion | `produce.py` + other skills |

## Setup (once)

1. Create a Notion internal integration, copy the secret, share the target page with it.
2. `export NOTION_TOKEN=ntn_...`
3. Copy `assets/cadence.example.yaml` to the workspace as `cadence.yaml` and edit the pillars and lanes.
4. Provision the database:

```bash
python scripts/setup_notion.py --parent-page <page_id> --cadence cadence.yaml
```

The database id is cached in `~/.varick-content-calendar.json`, so later commands don't need `--db`.

5. Set up the three Notion-side automations — see `references/notion-automations.md`. These cannot be created through the API; they take about four minutes in the UI.

## The loop

**Quarterly** — lay down empty slots:

```bash
python scripts/expand_cadence.py --cadence cadence.yaml --quarter 2026Q4 --dry-run
python scripts/expand_cadence.py --cadence cadence.yaml --quarter 2026Q4
```

Always dry-run first and read the slot count out loud to the user before writing. Re-running is safe — rows are keyed on `Slot ID` and existing slots are skipped.

**Weekly** — see the queue, then work it:

```bash
python scripts/queue.py --days 21
```

**Per row** — produce it:

```bash
python scripts/produce.py brief li-carousel-2026-08-09 -o brief.json
# draft with anti-ai-writing (and the parent draft, if brief.is_repurpose)
python scripts/produce.py push-draft li-carousel-2026-08-09 --file draft.md
# build the asset with varick-carousel, upload, then:
python scripts/produce.py push-asset li-carousel-2026-08-09 --url <link>
```

**On approval** — fan out:

```bash
python scripts/fanout.py --cadence cadence.yaml --all-approved
```

See `references/pipeline.md` for how the brief hands off to the drafting and asset skills, and what to do with repurpose children.

## Schema

Set by `setup_notion.py`. Don't add properties ad hoc in the UI — add them to `build_properties()` so a rebuild reproduces them.

`Title` · `Slot ID` · `Ship Date` · `Lead Days` · `Platform` · `Format` · `Pillar` · `Status` · `Hook` · `Source` · `Owner` · `Asset` · `Live URL` · `Notes` · `Parent Asset` ⇄ `Repurposed Into`

Three formulas: `Draft Due` (ship minus lead days), `Days Out`, and `Flag` (`OVERDUE` / `DRAFT LATE`).

Status ladder: `Slot → Drafting → Draft Ready → Approved → Asset Needed → Scheduled → Live`, plus `Killed`.

The draft itself lives in the **page body**, not a property — properties cap at 2000 characters and drafts don't.

## Rules

- **Slot ID is the primary key.** Every script matches on it. Never edit it by hand; if a row's date moves, the Slot ID stays as-is.
- **Cadence is set at the worst week of the quarter, not the best.** Carousels and X articles are the expensive lanes — cap them deliberately.
- **Three to four pillars, no more.** The point is to notice you've done five posts on evals and zero on diagnostics.
- **Nothing is written for a single slot.** Every X article has a fan-out chain. Fill the calendar from a handful of parents, not twenty original ideas a month.
- **Batch by format, not by week.** All the articles in one sitting, all the carousels in another. Draft Due exists so ship dates can be spread while production stays batched.
- **Never invent a hook or a title into Notion.** Empty is honest; a plausible-sounding placeholder gets shipped by accident.

## Notes

- Requires `pyyaml`. Everything else is stdlib.
- `Status` is a `select`, not Notion's `status` type — the API cannot create `status` properties. Behaves the same for filtering and automations.
- Formulas use Notion's classic (1.0) expression syntax.
- If a script returns a 404 on a page or database that plainly exists, the integration hasn't been shared with it. That's the cause almost every time.
