@sellable/mcp 0.1.80 → 0.1.82

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/agents/post-find-leads-filter-scout.md +19 -8
  2. package/agents/post-find-leads-message-scout.md +21 -6
  3. package/agents/registry.json +33 -18
  4. package/agents/source-scout-linkedin-engagement.md +10 -2
  5. package/agents/source-scout-prospeo-contact.md +9 -2
  6. package/agents/source-scout-sales-nav.md +10 -2
  7. package/dist/server.js +7 -1
  8. package/dist/tools/bootstrap.js +2 -2
  9. package/dist/tools/campaigns.d.ts +12 -0
  10. package/dist/tools/campaigns.js +40 -5
  11. package/dist/tools/context.d.ts +10 -4
  12. package/dist/tools/context.js +8 -4
  13. package/dist/tools/leads.d.ts +6 -3
  14. package/dist/tools/leads.js +39 -43
  15. package/dist/tools/navigation.d.ts +28 -1
  16. package/dist/tools/navigation.js +220 -10
  17. package/dist/tools/prompts.js +1 -1
  18. package/dist/tools/provider-preflight.js +2 -3
  19. package/dist/tools/readiness.d.ts +31 -6
  20. package/dist/tools/readiness.js +6 -1
  21. package/dist/tools/rubrics.d.ts +2 -0
  22. package/dist/tools/rubrics.js +2 -0
  23. package/package.json +1 -1
  24. package/skills/create-campaign/SKILL.md +24 -12
  25. package/skills/create-campaign-v2/SKILL.md +166 -114
  26. package/skills/create-campaign-v2/SOUL.md +19 -11
  27. package/skills/create-campaign-v2/core/auto-execute.yaml +14 -13
  28. package/skills/create-campaign-v2/core/flow.v2.json +964 -310
  29. package/skills/create-campaign-v2/references/approval-gate-framing.md +64 -32
  30. package/skills/create-campaign-v2/references/filter-leads.md +8 -0
  31. package/skills/create-campaign-v2/references/final-handoff-contract.md +55 -20
  32. package/skills/create-campaign-v2/references/lead-validation-preview.md +30 -17
  33. package/skills/create-campaign-v2/references/step-13-import-leads.md +62 -17
  34. package/skills/create-campaign-v2/references/validation-criteria.md +10 -5
  35. package/skills/create-campaign-v2/references/watch-link-handoff.md +72 -72
  36. package/skills/create-campaign-v2-tail/SKILL.md +123 -92
@@ -1,17 +1,25 @@
1
1
  # Approval Gate Framing
2
2
 
3
- This reference governs the commit gate in create-campaign-v2. Load this file
4
- before showing the user the approval packet at the end of Phase 84 and before
5
- minting a real `CampaignOffer`.
3
+ This reference is legacy/resume-only for create-campaign-v2. New shell-first
4
+ runs do not route through `approval-packet`, `commit-gate`, or `atomic-mint`.
5
+ Load this file only when resuming a compatibility run that already has those
6
+ debug artifacts.
7
+
8
+ CampaignOffer state is canonical; disk artifacts are a debug trail. All 14 disk
9
+ artifacts remain as debug outputs, but resume, gating, and handoff read campaign
10
+ state first. In new runs, the live campaign object appears after the short brief,
11
+ not after this approval packet.
6
12
 
7
13
  ## Purpose
8
14
 
9
- The approval gate is the **only** place in create-campaign-v2 where the user
10
- authorizes a DB mutation. Everything upstream (Phase 83 brief + Phase 84
11
- validation artifacts) is file-only. Everything downstream (atomic mint +
12
- autonomous tail) is live campaign state.
15
+ The approval gate used to be the place where the user authorized
16
+ spend/send-adjacent mutation. It is now compatibility-only. New runs import the
17
+ bounded 10-row review batch after source approval, save rubrics and the approved
18
+ message template after post-import review, and only then queue the cascade.
13
19
 
14
- No campaign row exists until the user picks `approve`.
20
+ If `campaign-shell.json` exists, a draft `CampaignOffer` row already exists but
21
+ must remain non-sendable. If no shell exists, no campaign row exists until the
22
+ user picks `approve`.
15
23
 
16
24
  ## Preconditions
17
25
 
@@ -60,8 +68,8 @@ This invariant is identical to the Phase 84 "sender/workspace mismatch
60
68
  From Wrong Workspace`. If the invariant fails:
61
69
 
62
70
  1. Do NOT show the commit gate.
63
- 2. Do NOT call `bootstrap_create_campaign`, `create_campaign`, or
64
- `save_rubrics`.
71
+ 2. Do NOT call `bootstrap_create_campaign`, `create_campaign`,
72
+ `update_campaign`, or `save_rubrics`.
65
73
  3. Surface an explicit workspace/sender-mismatch error + the detected
66
74
  current-workspace vs expected-workspace diagnostic.
67
75
  4. Leave the draft directory intact; a fresh session in the correct
@@ -204,6 +212,9 @@ If a customer/operator roleplay pass is run before the gate, write its critique
204
212
  to `customer-roleplay.md`. That critique is advisory only. A positive roleplay
205
213
  result is not approval and must not trigger `create_campaign`,
206
214
  `save_rubrics`, or any other DB mutation.
215
+ `customer-roleplay.md` can never authorize mint; only
216
+ `commit-gate-decision.md` authorizes DB mutation when it says
217
+ `Decision: approve`.
207
218
 
208
219
  After the user/operator chooses, write `commit-gate-decision.md` with exactly
209
220
  one of the six allowed choices plus a short reason. This file is the durable
@@ -248,6 +259,7 @@ abort
248
259
 
249
260
  Use the host-native structured question gate with these 6 options. Do not
250
261
  collapse, reorder, or add choices. Do not auto-select.
262
+ Do not collapse the six routes into one vague approval choice.
251
263
 
252
264
  If the host UI limits options per question, preserve all six routes with a
253
265
  two-question gate instead of mentioning implementation limits:
@@ -263,18 +275,26 @@ choice that activates the second question.
263
275
 
264
276
  ### `approve`
265
277
 
266
- User authorizes atomic mint. Proceed to the atomic-mint step:
278
+ User authorizes committing the draft shell or atomic mint. Proceed to the
279
+ atomic-mint step:
267
280
 
268
281
  0. **verify workspace/sender invariant** (see §Preconditions) — if the
269
282
  invariant fails, abort BEFORE `bootstrap_create_campaign`
270
283
  1. `bootstrap_create_campaign({ flowVersion: "v2" })`
271
- 2. `create_campaign({ campaignBrief, currentStep: "auto-execute-leads" })`
272
- capture `{ campaignId, watchUrl }` from response
273
- 3. `save_rubrics({ campaignOfferId, rubric })` using `rubric.json` (or derived
284
+ 2. If `campaign-shell.json` exists, call `create_campaign({ campaignId })` to
285
+ resume/recover `{ campaignId, watchUrl }`, then
286
+ `update_campaign({ campaignId, campaignBrief, leadSourceType,
287
+ leadSourceProvider, currentStep: "auto-execute-leads" })` with the final
288
+ approved brief and source decision.
289
+ 3. If no shell exists, call
290
+ `create_campaign({ campaignBrief, currentStep: "auto-execute-leads",
291
+ leadSourceType, leadSourceProvider })` and capture `{ campaignId, watchUrl }`
292
+ from the response.
293
+ 4. `save_rubrics({ campaignOfferId, rubric })` using `rubric.json` (or derived
274
294
  payload from `lead-filter.md`)
275
- 4. flip brief status to `committed`; move draft to `.committed/`
276
- 5. surface the watch link using `watch-link-handoff.md`
277
- 6. hand off to the autonomous tail (Plan 85-02)
295
+ 5. flip brief status to `committed`; move draft to `.committed/`
296
+ 6. surface the watch link using `watch-link-handoff.md`
297
+ 7. hand off to the autonomous tail (Plan 85-02)
278
298
 
279
299
  See `watch-link-handoff.md` for the exact link-surfacing protocol and the
280
300
  partial-mint recovery rules.
@@ -314,7 +334,8 @@ automatic rollback.
314
334
  ### `revise-brief`
315
335
 
316
336
  Route back to Phase 83 or Phase 84 depending on what the user wants to change.
317
- Do NOT mint a campaign. Do NOT print a watch link.
337
+ Do NOT commit/mint a campaign. If a draft shell already exists, keep it
338
+ non-sendable and update it only through the normal draft-shell sync points.
318
339
 
319
340
  Draft effect: preserve nothing by default; the brief itself is usually
320
341
  rewritten upstream. Delete all downstream artifacts so Phase 84 will re-run:
@@ -327,8 +348,9 @@ rewritten upstream. Delete all downstream artifacts so Phase 84 will re-run:
327
348
 
328
349
  ### `revise-leads`
329
350
 
330
- Route back to Phase 84 `find leads`. Do NOT mint a campaign. Do NOT print a
331
- watch link.
351
+ Route back to Phase 84 `find leads`. Do NOT commit/mint a campaign. If a draft
352
+ shell already exists, keep it non-sendable and replace only draft-shell source
353
+ state when the revised source is approved.
332
354
 
333
355
  Draft effect:
334
356
 
@@ -341,8 +363,9 @@ Draft effect:
341
363
 
342
364
  ### `revise-rubric`
343
365
 
344
- Route back to Phase 84 `filter leads`. Do NOT mint a campaign. Do NOT print a
345
- watch link.
366
+ Route back to Phase 84 `filter leads`. Do NOT commit/mint a campaign. If a draft
367
+ shell already exists, keep it non-sendable and update only saved draft rubrics
368
+ after the revised filter is confirmed.
346
369
 
347
370
  Draft effect:
348
371
 
@@ -355,8 +378,8 @@ Draft effect:
355
378
 
356
379
  ### `revise-messaging`
357
380
 
358
- Route back to Phase 84 `generate message`. Do NOT mint a campaign. Do NOT
359
- print a watch link.
381
+ Route back to Phase 84 `generate message`. Do NOT commit/mint a campaign. If a
382
+ draft shell already exists, keep it non-sendable.
360
383
 
361
384
  Draft effect:
362
385
 
@@ -369,25 +392,34 @@ Draft effect:
369
392
 
370
393
  ### `abort`
371
394
 
372
- User cancels the session. Do NOT mint a campaign. Do NOT print a watch link.
395
+ User cancels the session. Do NOT commit/mint a campaign and do NOT import,
396
+ queue, attach sequence, or start. If a draft shell exists, leave it paused/
397
+ non-sendable for operator cleanup instead of silently deleting a workspace row.
373
398
 
374
399
  Draft effect: move the draft directory to
375
400
  `.sellable/create-campaign-v2/drafts/{workspace-slug}/.aborted/{campaign-slug}-{timestamp}/`.
376
- No DB rows touched. No `CampaignOffer` created.
401
+ Legacy no-shell runs touch no DB rows and create no `CampaignOffer`.
402
+ No DB rows touched in legacy no-shell aborts; draft-shell aborts leave the
403
+ non-sendable shell for operator cleanup.
377
404
 
378
405
  ## Hard Rules
379
406
 
380
- - The user's `approve` choice is the **only** trigger for live DB mutation.
407
+ - The user's `approve` choice is the **only** trigger for lead import,
408
+ campaign-table cloning, workflow-table queuing, sequence attach, or start.
409
+ - A positive `customer-roleplay.md` critique is not approval and produces zero
410
+ launch mutation unless `commit-gate-decision.md` explicitly says
411
+ `Decision: approve`.
381
412
  - A positive `customer-roleplay.md` critique is not approval and produces zero
382
413
  DB mutation unless `commit-gate-decision.md` explicitly says
383
414
  `Decision: approve`.
384
- - The commit gate must not call `create_campaign`, `save_rubrics`,
385
- `import_leads`, `update_campaign`, or any other mutating tool before the
386
- user picks a choice.
415
+ - The commit gate must not call `create_campaign`, `update_campaign`,
416
+ `save_rubrics`, `import_leads`, or any other mutating tool before the user
417
+ picks a choice. Draft-shell writes happen in earlier allowed flow steps, not
418
+ inside the commit-gate question turn.
387
419
  - `import_leads` is **not** part of atomic mint. Full sourcing/import happens
388
420
  in Plan 85-02 Step 13 after mint.
389
- - Revision choices produce ZERO DB rows.
390
- - Aborting produces ZERO DB rows.
421
+ - Revision choices produce no spend/send-side effects.
422
+ - Aborting produces no spend/send-side effects.
391
423
  - A failed `approve` path (e.g. `create_campaign` errors, or `save_rubrics`
392
424
  fails after campaign creation) does NOT surface a watch link. See
393
425
  `watch-link-handoff.md` for the partial-mint recovery protocol.
@@ -1,5 +1,11 @@
1
1
  # Filter Leads
2
2
 
3
+ CampaignOffer state is canonical; disk artifacts are a debug trail. All 14 disk
4
+ artifacts remain as debug outputs, but resume, gating, and handoff read campaign
5
+ state first. Filter design may use the debug sample, but the active rubric save
6
+ is `save_rubrics({ campaignOfferId, leadScoringRubrics }) after the campaign
7
+ table exists`.
8
+
3
9
  The middle step is a lightweight lane audit and decision gate. It should add
4
10
  fidelity to the find-leads estimate, not re-run sourcing or produce a large
5
11
  scoring framework.
@@ -116,6 +122,8 @@ one client, a funded seed-stage team for another, multi-location practice size
116
122
  for another, or existing tool spend for another. Do not hard-code Sellable's
117
123
  exact budget, geography, or title rules into unrelated campaigns; hard-code the
118
124
  principle that passing leads must be worth the customer's time.
125
+ Do not hard-code Sellable's exact budget into the filter; translate affordability
126
+ into the client's native buying proxy.
119
127
 
120
128
  - right role / function: the person owns or directly influences the buyer
121
129
  problem; wrong-function leads fail even when they match the source topic
@@ -14,23 +14,43 @@ that decision — UI and Claude greenlight — and the skill must handle both
14
14
  cleanly, including the case where one channel has already started the
15
15
  campaign before the other arrives.
16
16
 
17
+ CampaignOffer state is canonical; disk artifacts are a debug trail. All 14 disk
18
+ artifacts remain as debug outputs, but resume, gating, and handoff read campaign
19
+ state first. At this point, `workflowTableId` is the campaign table, while
20
+ `selectedLeadListId` remains the source list that produced the review batch.
21
+
17
22
  ## Step 16 Setup
18
23
 
19
24
  On entry to `awaiting-user-greenlight`, the skill does the following in
20
25
  order (all before waiting for the user):
21
26
 
22
- 1. Attach the sequence to the campaign (`attach_sequence`).
23
- 2. Re-surface the `watchUrl` using the exact block in
27
+ 1. Call `get_campaign({ campaignId })` and inspect whether the campaign has at
28
+ least one attached sender and whether a sequence is already attached.
29
+ 2. Call `list_senders()` so the handoff can name available connected senders if
30
+ the campaign has none selected.
31
+ 3. If no sender is attached, surface the campaign Settings link
32
+ `/campaign-builder/{campaignId}/settings?mode=claude`, tell the user to attach
33
+ a sender there, and STOP. Do not attach sequence and do not start.
34
+ 4. If a sender is attached but no sequence is attached, surface the campaign
35
+ Sequence link `/campaign-builder/{campaignId}/sequence?mode=claude`, tell the
36
+ user to accept or adjust the recommended sequence there, and STOP. Do not
37
+ start. The Claude greenlight path may use
38
+ `attach_recommended_sequence({ campaignId })` as an explicit repair after the
39
+ user asks to start.
40
+ 5. Re-surface the `watchUrl` using the exact block in
24
41
  `references/watch-link-handoff.md`.
25
- 3. Surface the `handoff.orientation` string from `auto-execute.yaml`
42
+ 6. Surface the `handoff.orientation` string from `auto-execute.yaml`
26
43
  alongside the watch link so the user sees both the link and the
27
44
  "what to do next" sentence.
28
- 4. STOP. Do not call `start_campaign`. Do not advance `currentStep`.
45
+ 7. STOP. Do not call `start_campaign`. Do not advance `currentStep`.
29
46
 
30
47
  The final handoff must answer five customer questions in plain language:
31
48
 
32
49
  - where to inspect the campaign (`watchUrl`)
33
- - what is ready now (brief, filters/rubrics, review-batch leads, messages, sequence)
50
+ - what is ready now (brief, filters/rubrics, review-batch leads, messages, and
51
+ sequence when a sender is attached)
52
+ - whether the next UI step is Settings because a sender still needs to be
53
+ attached
34
54
  - what clicking Start does (approves/starts the campaign send path)
35
55
  - how to revise (reply with what to change, or use the campaign UI)
36
56
  - whether anything has started yet (nothing sends until the user starts)
@@ -58,10 +78,11 @@ the UI.
58
78
 
59
79
  ### UI path
60
80
 
61
- The user reviews messages in the watch link, bulk-approves messages in
62
- the workflow table UI (the existing "Approve all" button that wraps
63
- `POST /api/v3/workflow-tables/cells/approve-batch`), and clicks "Start
64
- Campaign" in the campaign-builder UI.
81
+ The user reviews messages in the watch link, attaches a sender in Settings when
82
+ needed, chooses/accepts the sequence, bulk-approves messages in the workflow
83
+ table UI (the existing "Approve all" button that wraps
84
+ `POST /api/v3/workflow-tables/cells/approve-batch`), and clicks "Start Campaign"
85
+ in the campaign-builder UI.
65
86
 
66
87
  No skill involvement. The skill remains idle in
67
88
  `awaiting-user-greenlight`. On the next resume, the skill observes
@@ -74,7 +95,13 @@ The user replies in chat with an affirmative intent: "yeah start",
74
95
  "looks good, start", "ship it", "go". The skill then performs, in
75
96
  order, atomically:
76
97
 
77
- 1. **Bulk-approve queued messages** via the existing endpoint
98
+ 1. **Fetch campaign state** with `get_campaign({ campaignId })` and short-circuit
99
+ if already running.
100
+ 2. **Validate sender + sequence state.** If no sender is attached, refuse start
101
+ and return the Settings link. If no sequence is attached but a sender is
102
+ attached, call `attach_recommended_sequence({ campaignId })` before approving
103
+ messages.
104
+ 3. **Bulk-approve queued messages** via the existing endpoint
78
105
  `POST /api/v3/workflow-tables/cells/approve-batch` with
79
106
  `{ tableId }`.
80
107
  - This is the SAME server action the UI "Approve all" button uses
@@ -86,18 +113,20 @@ order, atomically:
86
113
  - If no MCP tool wrapper for approve-batch is available, the skill
87
114
  invokes it via the same authenticated API client path that other
88
115
  v3 routes use.
89
- 2. **`start_campaign({ campaignId })`** flips the campaign into the
90
- running state. If this call fails, surface the error; do NOT leave
116
+ 4. **Verify start preconditions again**: `workflowTableId`, attached
117
+ `senderIds`, `sequenceTemplate`, and a validated greenlight/send
118
+ `currentStep` must all exist before start.
119
+ 5. **`start_campaign({ campaignId })`** — flips the campaign into the
120
+ running state and persists `CampaignOffer.currentStep = "running"` through
121
+ the backend start route. If this call fails, surface the error; do NOT leave
91
122
  the approvals in an "approved but not started" half-state silently
92
123
  — either retry or escalate with the explicit state.
93
- 3. **`update_campaign({ campaignId, currentStep: "running" })`** — moves
94
- the skill past `awaiting-user-greenlight` so resume lands in a
95
- "campaign live" confirmation state.
96
- 4. **Re-surface the watch link** plus a short "campaign is live"
124
+ 6. **Re-surface the watch link** plus a short "campaign is live"
97
125
  confirmation sentence.
98
126
 
99
- All three mutating steps (bulk-approve, start_campaign, update_campaign)
100
- must succeed together. If step 1 succeeds but step 2 fails, the skill
127
+ The mutating start steps (sequence repair when needed, bulk-approve, and
128
+ start_campaign) must succeed together. If approve-batch
129
+ succeeds but start_campaign fails, the skill
101
130
  surfaces the recoverable state and stops — it does NOT attempt to
102
131
  unapprove (that would be destructive) and it does NOT silently retry
103
132
  forever.
@@ -137,7 +166,7 @@ true:
137
166
  (e.g. upstream lead import failed post-mint and never recovered).
138
167
  - Workspace mismatch between skill context and campaign row.
139
168
  - Sender is missing or disabled.
140
- - `attach_sequence` never succeeded (no sequence bound to the
169
+ - `attach_recommended_sequence` never succeeded (no sequence bound to the
141
170
  campaign).
142
171
 
143
172
  A refusal is NOT an escalation in the ladder sense — it's a pre-check
@@ -163,6 +192,10 @@ does NOT silently proceed.
163
192
  - The autonomous tail NEVER calls `start_campaign` on its own.
164
193
  - Step 16 NEVER auto-advances past `awaiting-user-greenlight` without a
165
194
  user signal.
195
+ - Step 16 routes to Settings before sequence/start when no sender is attached.
196
+ - Step 16 routes to Sequence before start when no sequenceTemplate is attached.
197
+ - Prefer `attach_recommended_sequence`; use `attach_sequence` only for an
198
+ explicitly custom cadence.
166
199
  - Bulk-approve in the Claude greenlight path uses the EXISTING
167
200
  `POST /api/v3/workflow-tables/cells/approve-batch` endpoint. Do NOT
168
201
  invent a new tool / endpoint / mutation path.
@@ -170,7 +203,9 @@ does NOT silently proceed.
170
203
  confirmation, not a duplicate start.
171
204
  - Workspace/sender mismatch at greenlight time aborts the start with
172
205
  the Phase 84 invariant.
206
+ - The start route, not a follow-up `update_campaign`, persists
207
+ `currentStep: "running"`.
173
208
  - A pre-check refusal is NOT an escalation — it's a fixable state. The
174
209
  skill stays in `awaiting-user-greenlight`.
175
210
  - Never print the watch link in a half-started state (approved but not
176
- started, or started but `currentStep` not updated).
211
+ started, or a start response that did not return `currentStep: "running"`).
@@ -5,8 +5,16 @@ This reference defines the Phase 84 `find leads` output contract.
5
5
  ## Inputs
6
6
 
7
7
  - `brief.md`
8
+ - CampaignOffer campaignId
9
+ - `campaign-shell.json` debug copy when present
8
10
  - optional `lead-source-intake.json`
9
- - the existing `find-leads` capability in campaignless preview mode
11
+ - the existing `find-leads` capability in shell-first campaign-attached preview mode
12
+
13
+ CampaignOffer state is canonical; these files are debug outputs; resume and
14
+ gating read campaign state first. Campaignless preview mode is only allowed
15
+ before the campaign shell is minted; once a CampaignOffer exists, use its
16
+ campaignId as `campaignOfferId`.
17
+ campaignless preview mode is only allowed before the campaign shell is minted.
10
18
 
11
19
  ## Output Files
12
20
 
@@ -16,33 +24,38 @@ lead-sample.json
16
24
  lead-source-intake.json # only when the user intentionally supplied a source
17
25
  ```
18
26
 
27
+ Read `brief.md`, `lead-review.md`, and `lead-sample.json` as debug context;
28
+ the CampaignOffer is still the resume/gating source after mint.
29
+
19
30
  ## Tooling Rules
20
31
 
21
- Preview only:
32
+ Shell-first source preview:
22
33
 
23
- - use `find-leads` without `campaignOfferId`
34
+ - use `find-leads` with CampaignOffer campaignId as `campaignOfferId`
24
35
  - do not call `import_leads`
25
- - do not set `selectedLeadListId`
36
+ - do not import into the campaign table or set `workflowTableId`
37
+ - do not set `workflowTableId`
26
38
  - do not call `confirm_lead_list`
27
- - do not create workflow rows or campaign rows
39
+ - do not create workflow rows
40
+ - do not queue cells, attach a sequence, or start the campaign
28
41
  - do not call `save_domain_filters`
42
+ - only mutate campaign-attached source preview state in the narrow watch-mode
43
+ ways allowed by `flow.v2.json`
29
44
 
30
45
  Supplied-source preview:
31
46
 
32
47
  - `normal-discovery` is the default when the user does not already know the
33
48
  people/accounts.
34
- - `supplied-linkedin-profiles` uses `load_csv_linkedin_leads` before approval
35
- only as preview. Pre-approval calls must omit `confirmed: true`,
36
- `campaignOfferId`, `currentStep`, `leadListId`, `sourceLeadListId`, and
37
- provider-import parameters.
38
- - `supplied-domains` uses `load_csv_domains`. Pre-approval confirmation is
39
- allowed only to create a standalone `domainFilterId` and must omit
40
- `campaignOfferId` and `currentStep`. Use that `domainFilterId` for a
41
- campaignless account-constrained people sample.
42
- In other words, pre-approval domain CSV confirmation must omit
43
- `campaignOfferId` and `currentStep`.
44
- - `existing-lead-list` samples from the existing Sellable lead list before
45
- approval and does not clone/import it until after approval.
49
+ - `supplied-linkedin-profiles` uses `load_csv_linkedin_leads` before the
50
+ 10-lead import batch only as preview/source attachment. Pre-import calls
51
+ must omit provider-import parameters. Use `campaignOfferId` only to attach the
52
+ preview/list choice to the campaign UI.
53
+ - `supplied-domains` uses `load_csv_domains` to create or reuse a
54
+ `domainFilterId`, then runs campaign-associated Prospeo sampling with
55
+ `campaignOfferId`. Step 13 reuses the same `domainFilterId` for import.
56
+ - `existing-lead-list` samples from the existing Sellable lead list and does
57
+ not clone/import it until rubrics are saved and the approved message set is in
58
+ the campaign brief.
46
59
 
47
60
  `lead-source-intake.json` records:
48
61
 
@@ -1,16 +1,38 @@
1
1
  # Step 13 — Import Leads Contract
2
2
 
3
- This reference governs Step 13 (`auto-execute-leads`) of the Plan 85-02
4
- autonomous tail. Load before calling `import_leads` and on every resume
5
- into Step 13.
3
+ This reference governs Step 13 of the shell-first create-campaign-v2 flow. Load
4
+ before calling `import_leads` or `confirm_lead_list` and on every resume into
5
+ Step 13.
6
6
 
7
7
  ## Principle
8
8
 
9
- Bounded review-batch sourcing + import happens AFTER atomic mint, not during.
10
- The commit gate never calls `import_leads`; Step 13 is where the first real
11
- review batch is pulled into the workspace. Do not spend credits on the full
12
- source list until the user reviews the first batch and explicitly approves
13
- expansion.
9
+ CampaignOffer state is canonical; disk artifacts are a debug trail. All 14 disk
10
+ artifacts remain as debug outputs, but resume, gating, and handoff read campaign
11
+ state first.
12
+
13
+ Bounded review-batch sourcing + import happens after the existing campaign
14
+ shell has the selected source attached with `campaignOfferId`. It happens before
15
+ the post-import fit/message scouts, so those scouts can use the actual campaign
16
+ table sample.
17
+
18
+ Do not queue workflow cells, attach a sequence, or start before the later
19
+ rubric/message prerequisites are true:
20
+
21
+ 1. rubrics saved to the campaign
22
+ 2. approved message set synced into `campaignBrief`
23
+
24
+ Do not enrich, queue workflow cells, attach a sequence, or start before those
25
+ prerequisites are true.
26
+
27
+ Before `import_leads`, the approved source must already be campaign-attached.
28
+ The find-leads scouts receive `campaignOfferId` up front, so selected Signal
29
+ posts, provider searches, domain filters, or source lead-list IDs should already
30
+ live on the campaign. Reuse that state. If anything is missing, do not replay
31
+ the source inside Step 13; route back to find-leads so the source can be
32
+ attached with `campaignOfferId` and the campaign UI can stay in sync.
33
+
34
+ selectedLeadListId remains the source list; workflowTableId is the campaign
35
+ table. The first confirm/import must preserve both identities.
14
36
 
15
37
  ## Resolve Approved Source
16
38
 
@@ -21,11 +43,15 @@ source lead lists, searches, campaign table rows, or queued cells.
21
43
 
22
44
  Supported branches:
23
45
 
24
- - **No manifest / normal discovery** — use the existing provider import path.
46
+ - **No manifest / normal discovery** — reuse the approved provider source that
47
+ is already attached to this campaign, then import. Never call a bare
48
+ `import_leads({ campaignOfferId })` for Signal Discovery unless selected posts
49
+ already exist on the campaign.
25
50
  - **Supplied LinkedIn profile CSV** — confirm `load_csv_linkedin_leads` only
26
- after approval to batch/materialize the full uploaded CSV into a Sellable
27
- lead-list table. Persist the returned `leadListId`, then import the bounded
28
- review batch into the campaign table with
51
+ after rubrics and the approved message set are ready. Batch/materialize the
52
+ uploaded CSV into a Sellable lead-list table. Persist the returned
53
+ `leadListId`, then import the bounded review batch into the campaign table
54
+ with
29
55
  `confirm_lead_list({ sourceLeadListId: leadListId, targetLeadCount: importLimit })`.
30
56
  Do not call `import_leads` for this branch; the materialized lead list is the
31
57
  source.
@@ -41,6 +67,21 @@ Supported branches:
41
67
  `campaignOfferId`, provider prompt preflight, and `domainFilterId`, then
42
68
  import the review batch from that search.
43
69
 
70
+ ## Campaign-Attached Source Contract
71
+
72
+ Step 13 does not rediscover or replay source searches. It imports from the
73
+ campaign-attached state created during find-leads:
74
+
75
+ - Signal Discovery: selected posts must already exist on the campaign's
76
+ signal-discovery tabs.
77
+ - Sales Nav: the selected Sales Nav search/filter state must already be attached
78
+ to the campaign.
79
+ - Prospeo: the selected Prospeo search and any `domainFilterId` must already be
80
+ attached to the campaign.
81
+
82
+ If the required campaign-attached state is missing, stop before import and route
83
+ back to find-leads with the same `campaignOfferId`.
84
+
44
85
  If the source file changed after preview, or the existing list is missing or
45
86
  wrong-workspace, escalate before materialization. If a supplied-domain search
46
87
  returns zero or too few usable people, preserve `domainFilterId` and ask
@@ -54,13 +95,14 @@ Default case — no existing lead list bound to the campaign:
54
95
  ```text
55
96
  import_leads({
56
97
  campaignOfferId,
57
- targetLeadCount: <importLimit from auto-execute.yaml; default 25>
98
+ targetLeadCount: <importLimit from auto-execute.yaml; default 10>
58
99
  })
59
100
  ```
60
101
 
61
- Provider is inherited from the Phase 84 commit decision (not re-selected
62
- here). Response returns `{ imported, skipped, duplicates }` surface all
63
- three to the escalation logic so dedup ratios are visible.
102
+ Provider is inherited from the selected source decision (not re-selected here)
103
+ and should already be saved on the campaign before import. Response returns
104
+ `{ imported, skipped, duplicates }` — surface all three to the escalation logic
105
+ so dedup ratios are visible.
64
106
 
65
107
  For supplied direct lists, `confirm_lead_list` must receive
66
108
  `targetLeadCount: <importLimit>` or explicit `sourceRowIds` so a 100-row source
@@ -149,7 +191,10 @@ cannot scale further without a new source lane.
149
191
  - Zero-imported + zero-skipped ⇒ hard-fail escalate.
150
192
  - Zero-imported + high-skipped ⇒ lane-exhausted operator notice (NOT a
151
193
  silent proceed).
152
- - `import_leads` is NOT called in atomic mint. Only in Step 13.
194
+ - `import_leads` is NOT called before rubrics are saved and the approved message
195
+ set is synced into the campaign brief.
196
+ - Step 13 MUST reuse an approved source already attached with `campaignOfferId`
197
+ before `import_leads`; it does not replay provider searches.
153
198
  - `import_leads` is NOT called again in Step 14 (validate-sample). Full
154
199
  cohort import is a Step 13 action, not a recovery action for a bad
155
200
  sample.
@@ -6,7 +6,11 @@ message direction hold up against real preview evidence.
6
6
 
7
7
  ## Primary Contract
8
8
 
9
- - `brief.md` is the Phase 83 thesis input
9
+ - CampaignOffer state is canonical; disk artifacts are a debug trail.
10
+ - validation/debug artifacts are not durable campaign state.
11
+ - resume and handoff read campaign state before debug files.
12
+ - `CampaignOffer.campaignBrief` is the durable Phase 83 thesis input;
13
+ `brief.md` is its debug copy
10
14
  - `lead-review.md` and `lead-sample.json` are the required outputs of lead preview
11
15
  - `lead-filter.md` is the primary output of the middle step
12
16
  - `rubric.json` is optional and secondary
@@ -94,12 +98,13 @@ Required:
94
98
 
95
99
  Lead preview must:
96
100
 
97
- - use `find-leads` campaignless preview mode
98
- - avoid `campaignOfferId`
101
+ - use `find-leads` shell-first campaign-attached preview mode
102
+ - pass CampaignOffer campaignId as `campaignOfferId`
99
103
  - avoid `import_leads`
100
- - avoid `selectedLeadListId`
104
+ - do not set `workflowTableId`
101
105
  - avoid `confirm_lead_list`
102
- - avoid DB-backed campaign mutation
106
+ - avoid campaign-table import, workflow-cell queueing, sequence attach, and
107
+ campaign start
103
108
 
104
109
  `lead-review.md` must include:
105
110