| name | customer-comms |
| description | Draft a customer-facing roundup email for a product area, covering announcement-worthy Released cards |
| disable-model-invocation | true |
Customer Comms
Draft a customer-facing roundup email for a product area, covering all
announcement-worthy Released cards. One email per product area. The email goes
through iterative drafting with human review before sending.
The goal is an approved draft, not a sent email. Every version goes through
approve_content. The user reads, edits, and copies from the modal. Iteration
is typical (2-4 rounds). Don't rush to "final."
Arguments
$ARGUMENTS is optional:
- (no args): Start from Phase 1 — identify all uncommunicated cards, group
by product area, present for the user to pick which area to draft.
{product-area}: Skip Phase 1 — go directly to Phase 2 for the named
product area.
Also Read
Before starting, read these references:
- Voice & Style Notes:
reference/customer-comms.md (workflow, voice patterns,
accumulated style flags)
- Style examples:
reference/email-examples.md (3 example emails showing
the actual voice)
- Comms tracker:
box/comms-tracker.md (what's been communicated already)
- Workspace Constants: Shortcut IDs,
workflow states, product area values
- API Quirks: Shortcut and Slack API
gotchas (search is GET not POST, JSON payloads, pagination)
- General Constraints: mutation cap, rate
limiting
- Remaining areas:
reference/customer-comms.md Remaining section lists known
backlog groupings and notes (thin areas, second waves, audience considerations)
Read the style examples every time. Don't draft from remembered patterns. The
examples are short and the voice is specific enough that skipping them produces
noticeably different output.
Constraints
- Mutation gate: A PreToolUse hook blocks all Slack and Shortcut mutations
through Bash. Route through
agenterminal.execute_approved or present the
command for the user to run. Note: the Shortcut search endpoint is POST, which
triggers the gate. Use shortcut-cards.py for card listing, or the GET
endpoint for individual story fetches.
- Human-in-the-loop: Every draft version goes through
approve_content.
The user reviews exact text. Summary descriptions of changes are not
substitutes for showing the full draft.
- Before Shortcut API calls: check
reference/tooling-logistics.md for
tested recipes. Don't re-derive payload shapes or endpoint paths.
- Feature accuracy: Headline feature descriptions must reference the
verified scope from Step 2a, not card descriptions. When card description and
shipped code disagree, shipped code wins. Step 2a makes this structural rather
than advisory — the verification record is produced before drafting begins.
- Feature flag gating: Before announcing any feature, check whether it's
behind a feature flag that limits visibility. Ungated features are announceable.
Features gated to new signups only (e.g.,
product_focused_nav) are not:
new users never knew it was missing, existing users can't see it.
Steps
1. Identify uncommunicated cards
Skip this step if $ARGUMENTS names a product area.
- Pull all Released cards:
python3 box/shortcut-cards.py --state Released --summary
- Cross-reference against
box/comms-tracker.md to find cards not yet
communicated.
- Filter out bugs, infrastructure, and minor fixes. Group the remainder by
product area.
- Checkpoint: present the uncommunicated cards grouped by product area.
The user picks which area to draft.
2. Read cards and prominence split
- Fetch full card descriptions from the Shortcut API. The
shortcut-cards.py
script returns description_length, not description text. Use the API
directly:
SHORTCUT_API_TOKEN=$(grep '^SHORTCUT_API_TOKEN=' .env | cut -d= -f2-)
curl -s -H "Shortcut-Token: $SHORTCUT_API_TOKEN" \
"https://api.app.shortcut.com/api/v3/stories/{id}"
- Read the actual card descriptions. Do not write feature descriptions
from titles alone. For user-facing behavior details, check the codebase
(
git show origin/main:path/to/file).
- Propose a prominence split: 3-5 headline features (bold sections with
benefit copy) and remaining cards as supporting bullets.
- Checkpoint: user confirms or adjusts the split.
Multi-email splits: When a product area has 2+ headline-worthy features,
split into separate emails, each anchored by one headline. Distribute
supporting and bullet cards between them by narrative theme. Proved
2026-04-27: Pin Scheduler carousel + product tagging were each
headline-worthy, split into two emails with distinct hooks.
2a. Scope verification (headline features)
The proxy chain from card description to customer copy loses information at
every layer. 3 of 8 features had descriptions that diverged from shipped code
in one session. This step makes verification structural: the draft references
verified scope, not card descriptions.
For each headline feature (the 3-5 cards from the prominence split):
-
Delegate a subagent per card (or 2-3 clustered by area) with:
- The card description (fetched in Step 2)
- Instructions to find linked PRs via card branches, read the PR diff
(actual changed files), and return a structured comparison:
"Card says [X]. PR actually shipped [Y]. Divergences: [Z]. Key files: [paths]."
- Use
model: "claude-sonnet-4-6". Include the card ID and title so
output is identifiable after collection.
-
Collect results. For any card where the subagent flags a divergence,
read the relevant file yourself via git show origin/main:path/to/file.
Subagent output is a pointer, not a finding — your own read confirms
whether the divergence is real and what the shipped behavior actually is.
When a delegate reports "not implemented," check the PR commit history
(git log --oneline --grep or git show on linked PRs) before accepting.
Delegates search the current codebase but can miss functionality added in
specific commits — especially post-publish handlers, SQS workers, or async
flows that don't live alongside the main feature code. Proved 2026-04-27:
delegate reported product tagging never reaches Pinterest; PR commit history
revealed an SQS handler that applies tags post-publish.
-
Produce a verification record per headline card:
SC-NNN: Card scope: [what card describes]
Shipped scope: [what code actually does]
Email will say: [accurate description for the draft]
Write the verification records to
/tmp/ff-$AGENTERMINAL_SESSION_ID/comms-verification.md. This file is
the input to Step 4 drafting — reference it, not card descriptions, when
writing feature copy.
-
Checkpoint: present the verification records to the user. Flag any
cards where shipped scope differs materially from card description. The
user confirms the "Email will say" framing before drafting begins.
Supporting-bullet cards (not headlines) get lighter treatment: card
description is usually sufficient for a one-line bullet. If a supporting card
has a title that feels ambiguous, spot-check the PR, but full verification is
not required.
3. Find a stat for the hook (optional)
Check box/posthog-events.md for known events in this product area. Query
PostHog for a compelling number that anchors the intro.
Good stats speak to user value (engagements received, time saved, adoption
count), not internal metrics.
If no good stat exists, use a narrative/scenario hook instead. Both approaches
work. See style examples for both patterns. Don't force a stat.
4. Draft
-
Re-read reference/email-examples.md immediately before writing —
not at session start, not between emails, but right before the draft call.
Write the draft following the voice and style notes in
reference/customer-comms.md. Key patterns:
- Story-driven or data-driven intro that earns the feature list
- Short sentences, fragments, conversational tone
- Features introduced after the hook, not before
- End with a question or CTA that invites reply
- Specific numbers over vague claims ("25,000 hours per week" not "a lot")
- Direct address ("So tell me," "Don't worry")
- One idea per line, lots of whitespace, spoken rhythm
-
Present the full draft via approve_content with
content_type: "email-draft", filename: "comms-{product-area}". The user
can read, edit, and copy from the modal. The saved file is compaction
insurance for multi-round iteration.
-
Iterate based on feedback. Each iteration goes through approve_content
again (same filename — the saved file gets overwritten with the latest
approved version).
5. Proofread and verify
Before presenting the final version:
- Every headline feature description must match the verified scope from
Step 2a (
comms-verification.md), not the card description. Re-read the
verification record if unsure.
- Verify UI labels against actual code for every feature named in the
email. Read the component that renders the button, toggle, or modal
title. Don't describe a feature using a label that doesn't appear in
the UI — users will look for it and not find it. Proved 2026-04-27:
email described a labeled "Add to Turbo" button; actual UI is an
unlabeled icon toggle.
- No em dashes in customer-facing copy. Use commas, periods, or colons.
- Turbo-specific: no "slot" in customer-facing copy (use "Turbo Pin" or
rephrase). This applies to Turbo queue slots specifically, not time slots
in SmartSchedule or other contexts.
- Pinterest-specific: "saving and repinning" are the same action. Use
"saving, clicking through, and commenting."
- Verify any stats cited are from actual PostHog queries in this session.
- Flag any cards that were in earlier drafts but dropped from the final,
so the user can confirm intentional omission.
Style flags (accumulated from prior sessions)
These are specific corrections from past drafting rounds. They apply every time.
- No "constantly" or other absolute/negative language.
- No "getting closer" framing. Reframe as positive progress ("making what you
like better").
- Factual accuracy on superlatives ("most-used" vs "most-requested"). Must be
verifiable.
- The intro earns the feature list by setting up why features matter, not by
restating the problem.
- Repeat key value language intentionally (e.g., "value" in hook and close).
- Close should invite action, not just promise to read replies.
6. Update tracker
After the user confirms the final version:
- Add an entry to
box/comms-tracker.md with each card, release date,
prominence (headline/bullet), and a short description.
- Cards intentionally omitted from the email are NOT added to the tracker.
They remain uncommunicated and will surface in the next diff.
- Features communicated that don't map to a specific SC card: use
— for
the card column.
- Update
reference/customer-comms.md to move the product area from
Remaining to Completed.