Reengagement Segmenter
Turn "the lapsed list" into distinct audiences with distinct reasons for silence, and treat the
reason as the segment. The design rests on one argument: win-back blasts fail because they treat
silence as one condition, when silence is the least informative signal a contact can send. A
deal stalled at security review, a user who never reached value, and a buyer who signed with the
incumbent all look identical in a recency report and need three different messages, or none.
The other half of the argument is the repo's standing one: the most valuable output of a
reengagement pass is often the list of people you decide not to contact, documented.
What this consumes
| Input |
From |
What it provides |
| The lapsed records |
CRM export, deal notes, product events, support history |
The evidence each blocker call rests on |
| ICP |
icp-research |
Disqualifiers (bad-fit lapse is leave-alone, not win-back), triggers for the why-now |
| Voice directory |
brand-voice-guide |
The register sequences are written in |
| What changed on your side |
The user |
Reengagement needs a why-now; "we miss you" is not one |
The last row is the load-bearing one. A reengagement sequence exists because something changed:
your product closed the gap that stalled them, their situation visibly shifted, or a trigger from
the ICP fired. No change, no sequence; that cohort waits.
The workflow
Five phases. Show the user the segment map (end of Phase 3) before any drafting.
Phase 1: Assemble the evidence, not just the list
Pull the lapsed records with whatever evidence exists per record: last meaningful activity,
lifecycle stage reached, stated reasons (closed-lost fields, cancellation surveys), and observed
behavior (support tickets, usage before silence, emails). First-party evidence is messy and
contradictory; that is expected and handled in Phase 3, not smoothed over.
Phase 2: Run the exclusions first
Before any blocker reasoning, apply the hard gates from references/blocker-taxonomy.md:
unsubscribed or do-not-contact, complaint history, active competitor contracts, ICP disqualifiers,
and the dormancy horizon. These records go to the leave-alone lane with reasons, which ships
in the deliverable. Reengaging someone who opted out is not a growth motion, it is a compliance
incident.
Phase 3: Assign blockers, honestly
Tag each surviving record with a blocker from the taxonomy, citing the evidence. Three rules:
- The evidence outranks the label. When the CRM says "price" but the thread shows a security
stall, the record is flagged as a conflict and resolved by judgment, with the resolution
written down.
scripts/segment_audience.py detects these conflicts mechanically.
- Unknown stays unknown. A record with no blocker evidence goes to the diagnostic segment,
whose sequence asks one honest question instead of assuming a reason.
- The script does the bookkeeping. Deterministic rules (exclusions, recency bands, tag
precedence, conflict detection) run mechanically; the judgment calls are yours and get recorded.
Phase 4: Plan one motion per segment
Each blocker has its own sequence archetype in the taxonomy: the never-activated get the value
they missed, the timing-stalled get a low-pressure trigger watch, the security-stalled get the
specific thing that changed, the diagnostic segment gets one question. Two universal rules:
reengagement sequences are shorter than cold ones (two to three touches), and every sequence
opens with the why-now change, not with an apology for the silence.
Phase 5: Draft, gate, package
Draft in the brand voice and run every sequence through outbound-engine's
sequence_gate.py: the freshness check applies to the why-now change, the slop list catches
"just checking in" (the canonical reengagement failure), and the voice Avoid list holds. Package
per references/output-template.md: the segment map with counts, the per-segment plans, the
conflict log, and the leave-alone lane with reasons.
Scripts
| Script |
What it does |
Network |
Keys |
scripts/segment_audience.py |
Deterministic segmentation: exclusions, recency bands, blocker tags, conflict flags, counts |
None |
None |
Failure modes to avoid (the quality bar)
- One blast to the whole list. The disease this skill treats.
- "Just checking in." The outbound gate catches it mechanically; do not write it.
- Guessing the blocker. No evidence means the diagnostic segment, not your best story.
- Reengaging the excluded. Unsubscribes and complaints are permanent exits from this motion.
- Apologizing for the silence. Open with what changed, not with the gap.
- A hidden leave-alone lane. The records you chose not to contact, with reasons, are output.
- Trusting the CRM label over the thread. Stated reasons are hypotheses; conflicts get logged.
Bundled references
references/blocker-taxonomy.md: the segments, the evidence each requires, the exclusion
gates, and the sequence archetype per segment. Read at Phases 2-4.
references/output-template.md: the audience.json schema the script consumes and the segment
report format. Read at Phases 3-5.
examples/cohere/: a dormant-pipeline pass in Cohere's voice on synthetic records (fictional
counterparties, labeled, with real contradictions), the segment map, and a gated sequence for
the security-stall segment.
1---2name: reengagement-segmenter3description: Segment a lapsed audience by the actual blocker that stalled each relationship, then plan a distinct reengagement motion per segment, including the segment that should be left alone. Works on dormant pipeline, lapsed customers, cold trial users, or an inactive list. Applies a blocker taxonomy (never activated, timing, lost to incumbent, price, trust damage, drifted, unknown), enforces hard exclusions first (unsubscribed, complaints, active competitor contracts), flags records whose stated reason contradicts their evidence, and drafts sequences through the outbound-engine gate. Use whenever the user says "win-back", "reengagement", "re-engage", "lapsed customers", "dormant accounts", "stalled deals", "gone quiet", "cold pipeline", "churned users", or wants to revive a list without blasting it. Consumes icp-research and brand-voice-guide; sequences inherit outbound-engine's no-send discipline.4---56# Reengagement Segmenter78Turn "the lapsed list" into distinct audiences with distinct reasons for silence, and treat the9reason as the segment. The design rests on one argument: **win-back blasts fail because they treat10silence as one condition, when silence is the least informative signal a contact can send.** A11deal stalled at security review, a user who never reached value, and a buyer who signed with the12incumbent all look identical in a recency report and need three different messages, or none.1314The other half of the argument is the repo's standing one: the most valuable output of a15reengagement pass is often the list of people you decide not to contact, documented.1617## What this consumes1819| Input | From | What it provides |20|---|---|---|21| The lapsed records | CRM export, deal notes, product events, support history | The evidence each blocker call rests on |22| ICP | `icp-research` | Disqualifiers (bad-fit lapse is leave-alone, not win-back), triggers for the why-now |23| Voice directory | `brand-voice-guide` | The register sequences are written in |24| What changed on your side | The user | Reengagement needs a why-now; "we miss you" is not one |2526The last row is the load-bearing one. A reengagement sequence exists because something changed:27your product closed the gap that stalled them, their situation visibly shifted, or a trigger from28the ICP fired. No change, no sequence; that cohort waits.2930## The workflow3132Five phases. Show the user the segment map (end of Phase 3) before any drafting.3334### Phase 1: Assemble the evidence, not just the list3536Pull the lapsed records with whatever evidence exists per record: last meaningful activity,37lifecycle stage reached, stated reasons (closed-lost fields, cancellation surveys), and observed38behavior (support tickets, usage before silence, emails). First-party evidence is messy and39contradictory; that is expected and handled in Phase 3, not smoothed over.4041### Phase 2: Run the exclusions first4243Before any blocker reasoning, apply the hard gates from `references/blocker-taxonomy.md`:44unsubscribed or do-not-contact, complaint history, active competitor contracts, ICP disqualifiers,45and the dormancy horizon. These records go to the **leave-alone lane with reasons**, which ships46in the deliverable. Reengaging someone who opted out is not a growth motion, it is a compliance47incident.4849### Phase 3: Assign blockers, honestly5051Tag each surviving record with a blocker from the taxonomy, citing the evidence. Three rules:5253- **The evidence outranks the label.** When the CRM says "price" but the thread shows a security54 stall, the record is flagged as a conflict and resolved by judgment, with the resolution55 written down. `scripts/segment_audience.py` detects these conflicts mechanically.56- **Unknown stays unknown.** A record with no blocker evidence goes to the diagnostic segment,57 whose sequence asks one honest question instead of assuming a reason.58- **The script does the bookkeeping.** Deterministic rules (exclusions, recency bands, tag59 precedence, conflict detection) run mechanically; the judgment calls are yours and get recorded.6061### Phase 4: Plan one motion per segment6263Each blocker has its own sequence archetype in the taxonomy: the never-activated get the value64they missed, the timing-stalled get a low-pressure trigger watch, the security-stalled get the65specific thing that changed, the diagnostic segment gets one question. Two universal rules:66reengagement sequences are **shorter than cold ones** (two to three touches), and every sequence67opens with the why-now change, not with an apology for the silence.6869### Phase 5: Draft, gate, package7071Draft in the brand voice and run every sequence through `outbound-engine`'s72`sequence_gate.py`: the freshness check applies to the why-now change, the slop list catches73"just checking in" (the canonical reengagement failure), and the voice Avoid list holds. Package74per `references/output-template.md`: the segment map with counts, the per-segment plans, the75conflict log, and the leave-alone lane with reasons.7677## Scripts7879| Script | What it does | Network | Keys |80|---|---|---|---|81| `scripts/segment_audience.py` | Deterministic segmentation: exclusions, recency bands, blocker tags, conflict flags, counts | None | None |8283## Failure modes to avoid (the quality bar)8485- **One blast to the whole list.** The disease this skill treats.86- **"Just checking in."** The outbound gate catches it mechanically; do not write it.87- **Guessing the blocker.** No evidence means the diagnostic segment, not your best story.88- **Reengaging the excluded.** Unsubscribes and complaints are permanent exits from this motion.89- **Apologizing for the silence.** Open with what changed, not with the gap.90- **A hidden leave-alone lane.** The records you chose not to contact, with reasons, are output.91- **Trusting the CRM label over the thread.** Stated reasons are hypotheses; conflicts get logged.9293## Bundled references9495- `references/blocker-taxonomy.md`: the segments, the evidence each requires, the exclusion96 gates, and the sequence archetype per segment. Read at Phases 2-4.97- `references/output-template.md`: the audience.json schema the script consumes and the segment98 report format. Read at Phases 3-5.99- `examples/cohere/`: a dormant-pipeline pass in Cohere's voice on synthetic records (fictional100 counterparties, labeled, with real contradictions), the segment map, and a gated sequence for101 the security-stall segment.