What it does
Closes the postcard prospecting loop in three directions:
1. Backwards — mines email + transcripts for postcards that ALREADY went out, so the system knows exactly who has been mailed
2. Sideways — syncs SoT (master workbook Lead Details tab) ↔ HubSpot on postcard send-history (single source of truth)
3. Forwards — produces a ready-to-email Excel file of the next mailing round + composes the handoff email to Jasmine + Rachel
Files
- Skill:
~/.claude/scheduled-tasks/postcard-ledger/SKILL.md(prompt the scheduler invokes) - Spec:
~/.claude/scheduled-tasks/postcard-ledger/SPEC.md(canonical design — edit here) - Helper:
~/Library/Application Support/SkyRun/postcard_ledger.py(Python: status / ingest / build_next_round) - Output dir:
~/Desktop/SkyRun/Postcard Rounds/Next_Postcard_Round_<DATE>.xlsx
Cron
0 10 1 — Mondays at 10am MDT. After Joseph's standing 11:30 Rachel 1:1 and after the morning brief; gives Joseph midweek window to review the file before sending to Jasmine for print.
Plus: SkyRun QB can trigger postcard-ledger on demand any time.
SoT ledger columns (added to Lead Details tab)
| Column | Type | Purpose |
|---|---|---|
POSTCARD ROUNDS | comma-separated YYYY-MM-DD | append-only history of every postcard send |
LAST POSTCARD | YYYY-MM-DD | most-recent send (computed) |
POSTCARD COUNT | int | total send count (computed) |
NEXT ROUND ELIGIBLE | bool | computed during build_next_round |
Next-round criteria (defaults; configurable via skill args)
- Score gate: ≥ 50 (HOT/WARM/SWITCH; NURTURE/LOW excluded)
- Cooldown: no postcard in last 60 days
- Mailing address: required (no nulls)
- DNC gate: email + phone-last-10 + name-token check
- Cap: 150 recipients per round (UPDATED 2026-04-30 — was 75; Joseph killed the 75-recipient round on 4/30 with feedback "need more before we re-up". Don't surface a round to Jasmine + Rachel unless it has at least 100 fresh-eligible recipients meeting all gates.)
- Minimum-to-fire: 100 recipients. If fewer than 100 are fresh-eligible at fire time, skip the handoff — log "round_skipped: insufficient_volume" + heartbeat YELLOW. Joseph hand-launches when fresh enrichment + cooldown rotations push the eligible pool back over the threshold.
- Sort: SWITCH first → HOT → WARM, score-desc within tier
Outputs every fire
- Updated SoT workbook (Lead Details tab — ledger columns populated with new sends)
- HS update proposals →
pending_hs_updates.jsonl(one per new send event; Joseph 👍 to write) Next_Postcard_Round_<DATE>.xlsxin Postcard Rounds dir- Handoff email draft to Jasmine + Rachel →
pending_drafts.jsonl(Joseph 👍 to send) - Batch entry →
pending_postcard_batches.jsonl(PWA shows the round) - Heartbeat at
~/Library/Application Support/SkyRun/health/ - Audit summary at
~/Desktop/SkyRun/audit/<DATE>/ - ntfy push:
📬 Postcard ledger — N new sends recorded, M ready for next round
Hard rules (per Office toolchain + DNC + drafting standards)
- Microsoft Office toolchain only — openpyxl OK, never soffice/LibreOffice
- DNC check before adding any candidate
- HS writes are proposals — never auto-write
- Email drafts only — never auto-send
- No commission/pricing references in cold-prospect copy
- No Vermont references
Manual operation
bash
Status — what's the ledger know right now?
python3 "$HOME/Library/Application Support/SkyRun/postcard_ledger.py" status
Ingest manually (events.json with confirmed sends)
python3 "$HOME/Library/Application Support/SkyRun/postcard_ledger.py" ingest --events /path/to/events.json
Build next round
python3 "$HOME/Library/Application Support/SkyRun/postcard_ledger.py" build_next_round --cap 75 --min-score 50 --cooldown-days 60
Differs from postcard-updater (existing skill)
postcard-updater— adds NEW prospects to the Postcard Targets tab as the candidate pool grows. Concerned with workbook freshness on the input side.postcard-ledger(this) — knows what's been sent, syncs SoT ↔ HS, produces the output round file. Concerned with the loop's tail end.
The two skills are complementary; both can run without conflict.
Tuning notes
After 4 weekly rounds:
- Approve rate (% of file Joseph 👍's vs. dismisses) — target >90%
- Response rate per round (calls + emails citing the postcard) — establishes baseline conversion
- Tier-specific cooldown? (SWITCH may want shorter, NURTURE longer)
- Geo-batching? (Winter Park-only one round, mixed the next, for A/B testing)
Phase 2 (when adam-bd activates first market)
Same pattern scaffolds into ~/.claude/scheduled-tasks/adam-bd-postcard-ledger/ with operator-namespaced paths and per-market workbook references. Bootstrap-package skills-skeleton/_DISABLED/ gets a postcard-ledger template.
Origin
Built 2026-04-27 in response to Joseph's framing: "the system to know and understand by way of mining data from email tracks with jasmine and rachel and any other person involved in that conversation in addition to an additional scan for any other correspondence related to postcards. SoT and hubspot should always be in sync and while keeping up with what leads have been sent postcards, that list should be consistently be getting updated with new postcard recipients in a file that I can easily send to Jasmine and Rachel for the next round of sends." — built immediately, closes Gap A from the prospecting-loop walkthrough.