Module: Campaigns & Target Lifecycle (Phase 1)
Ties the whole flow together: a campaign defines what to discover and how to spend; a target is a prospect company that belongs to 0..1 campaign and whose lifecycle state moves through the downstream stages.
Responsibilities
- Campaign CRUD: name, product(s), postal codes + industry/branch, budget + warning threshold, daily/per-run discovery limits, assignment rule, status.
- Attach discovered targets to a campaign when they pass the AssignmentRule; support multi-match and re-assignment of previously "unassigned" targets.
- Own the target lifecycle state machine (the spine every downstream module transitions).
- Managed by the Supervisor role (who also manages connect variants, shifts, and releases).
Campaign definition
| Field | Purpose |
|---|---|
| postal codes + industry/branch | discovery inputs (fan out one discovery job per code) |
| budget + warning threshold | governs the budget guard (pre-check before every paid call) |
| daily_limit / per_run_limit | discovery throttles for the scheduled research trigger |
| AssignmentRule | the rule deciding which targets attach to this campaign (see below) |
| product(s) | what's being sold (feeds outreach + closing context) |
| status | draft / running / paused / done |
Assignment (the AssignmentRule)
A campaign has one AssignmentRule (Campaign 1─<1 AssignmentRule). New targets auto-route
into a campaign using either:
- a threshold rule — a numeric benchmark cutoff (e.g. attach targets scoring ≥ N), or
- an LLM prompt rule — natural-language matching (e.g. "only companies founded this year in the care sector"), evaluated by Claude against the target profile with a schema-validated response.
The rule runs after discovery + enrichment + benchmark. It decides which targets attach to this
campaign vs. stay unassigned (Target ─>(0..1) Campaign). The engine handles multi-match
(a target qualifying for several campaigns) and re-assignment of previously unassigned targets
as new campaigns appear. A scheduled job rotates unassigned targets so they get re-evaluated.
Target lifecycle state machines
The target lifecycle is an explicit state machine in the domain layer — string-backed enums
plus guarded transitions (illegal transitions raise; there is no free-form status field). Each
stage's module owns its own transitions, and every transition writes a PipelineEvent and an
immutable audit_logs record (who / old→new / IP / user-agent), which feeds every downstream KPI
dashboard without extra bookkeeping.
Connect pipeline (8 stages):
Targets
→ Not reached (follow-up)
→ Reached (gatekeeper blocked)
→ Reached (appointment interest)
→ Appointment scheduled
→ No interest (follow-up)
→ Finally rejected
→ Do not contact
Bandit success = Appointment scheduled; failure = not reached / finally rejected / no interest / do-not-contact. Gatekeeper and appointment-interest are intermediate states that fire no bandit event.
Closing pipeline (8 stages):
New target (validation pre-stage)
→ Appointment scheduled
→ Follow-up
→ First meeting
→ Follow-up meeting
→ Sold (end)
→ Not sold (end)
→ Unqualified (end)
Appointments are round-robin assigned to Closing-Agents, who see only their assigned targets.
Key services
CreateCampaign/UpdateCampaign.RunCampaign— dispatch discovery jobs on theresearchqueue, respecting limits + budget.ApplyAssignmentRule— evaluate the threshold/LLM rule and attach qualifying targets (setting the campaign link), handling multi-match and re-assignment.- Lifecycle transition handlers (one per transition), each authorized and audited.
- Business logic lives in services, never in controllers or React components.
Acceptance
- A Supervisor can create a campaign with postal codes, industry/branch, budget, and an AssignmentRule (threshold or LLM prompt).
- Running it discovers + enriches + scores, and only rule-passing targets attach to the campaign; the rest stay unassigned and are eligible for other campaigns / re-assignment.
- A target's state can only move along legal transitions in the connect and closing pipelines; each
transition writes a
PipelineEventand an immutable audit record. - Tests cover campaign CRUD, assignment-rule application (threshold + LLM), multi-match/re-assignment, and the state machine's legal/illegal transitions (Jest/Vitest; external calls faked).
Screens (see 04-design-workflow)
- Campaigns — list + create form (Next.js App Router + React 19 + shadcn/ui).
- Targets — benchmark-sorted target list with breakdown (shared with Discovery/Benchmark).