← ClaudeAtlas

07-campaign-sheetlisted

Turn a terminal records.jsonl into an owner-readable campaign sheet plus a HubSpot-import-shaped CSV - the chain's exit door. Use when a run is ready to hand to an owner: "build the campaign sheet", "export these for HubSpot", "who do I contact first", or as the final step of run-first-campaign. Reads whatever stage produced the records (qualified, signal-ranked, or fully resolved) and degrades gracefully on missing fields - no email column until 06 has run. Nothing sends and nothing downstream reads its output: the sheet and CSV are the human deliverable, approved on screen. To add verified emails first, run 06-resolution-email-person; to rank, 05-signal-builder.
Zevenue/headless-gtm · ★ 26 · AI & Automation · score 77
Install: claude install-skill Zevenue/headless-gtm
# Campaign sheet (07) The last step in the chain. Everything upstream produced `records.jsonl`; this turns those records into what an owner actually uses - a ranked "who to contact first and why" sheet, plus a CSV shaped for a one-click HubSpot import. It **adds no fields to the chain and nothing reads its output.** The CSV is a derived, human-facing export (`headless-gtm-shared/CONVENTIONS.md`) - the canonical record stays `records.jsonl`, and re-running any upstream skill never depends on this sheet. ## What it reads A terminal `records.jsonl` from any stage. The script tolerates every shape and degrades instead of failing: - **Company name** from `company` (or `name`), **site** from `website` (or `https://` + `domain`), **phone / city** from wherever the discovery step put them. - **Ranking** from 05's `signals[]` - the highest-score signal per record decides call order; its `signal_type`, `signal_sentence`, and `source_url` become the "why now" line, and its `approach` (or `fallback_approach`) the recommended angle. Records with no signals stay in the sheet, ranked last. - **Contacts** from 06's `person_name` / `matched_title` / `email` / `verification_status`. When 06 has not run, the contact columns are dropped entirely rather than shipped empty. - Rows with no website ride through on their listing fields - the sheet does not drop them (a registry row with `domain` empty is still a real target). ## What it writes `runs/<run-id>/` (via `common.runs