@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.
- package/agents/post-find-leads-filter-scout.md +19 -8
- package/agents/post-find-leads-message-scout.md +21 -6
- package/agents/registry.json +33 -18
- package/agents/source-scout-linkedin-engagement.md +10 -2
- package/agents/source-scout-prospeo-contact.md +9 -2
- package/agents/source-scout-sales-nav.md +10 -2
- package/dist/server.js +7 -1
- package/dist/tools/bootstrap.js +2 -2
- package/dist/tools/campaigns.d.ts +12 -0
- package/dist/tools/campaigns.js +40 -5
- package/dist/tools/context.d.ts +10 -4
- package/dist/tools/context.js +8 -4
- package/dist/tools/leads.d.ts +6 -3
- package/dist/tools/leads.js +39 -43
- package/dist/tools/navigation.d.ts +28 -1
- package/dist/tools/navigation.js +220 -10
- package/dist/tools/prompts.js +1 -1
- package/dist/tools/provider-preflight.js +2 -3
- package/dist/tools/readiness.d.ts +31 -6
- package/dist/tools/readiness.js +6 -1
- package/dist/tools/rubrics.d.ts +2 -0
- package/dist/tools/rubrics.js +2 -0
- package/package.json +1 -1
- package/skills/create-campaign/SKILL.md +24 -12
- package/skills/create-campaign-v2/SKILL.md +166 -114
- package/skills/create-campaign-v2/SOUL.md +19 -11
- package/skills/create-campaign-v2/core/auto-execute.yaml +14 -13
- package/skills/create-campaign-v2/core/flow.v2.json +964 -310
- package/skills/create-campaign-v2/references/approval-gate-framing.md +64 -32
- package/skills/create-campaign-v2/references/filter-leads.md +8 -0
- package/skills/create-campaign-v2/references/final-handoff-contract.md +55 -20
- package/skills/create-campaign-v2/references/lead-validation-preview.md +30 -17
- package/skills/create-campaign-v2/references/step-13-import-leads.md +62 -17
- package/skills/create-campaign-v2/references/validation-criteria.md +10 -5
- package/skills/create-campaign-v2/references/watch-link-handoff.md +72 -72
- package/skills/create-campaign-v2-tail/SKILL.md +123 -92
|
@@ -1,17 +1,25 @@
|
|
|
1
1
|
# Approval Gate Framing
|
|
2
2
|
|
|
3
|
-
This reference
|
|
4
|
-
|
|
5
|
-
|
|
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
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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
|
-
|
|
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`,
|
|
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
|
|
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({
|
|
272
|
-
|
|
273
|
-
|
|
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
|
-
|
|
276
|
-
|
|
277
|
-
|
|
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.
|
|
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.
|
|
331
|
-
|
|
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.
|
|
345
|
-
|
|
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.
|
|
359
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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`, `
|
|
385
|
-
`
|
|
386
|
-
|
|
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
|
|
390
|
-
- Aborting produces
|
|
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.
|
|
23
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
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,
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
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. **
|
|
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
|
-
|
|
90
|
-
|
|
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
|
-
|
|
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
|
-
|
|
100
|
-
must succeed together. If
|
|
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
|
-
- `
|
|
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
|
|
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
|
|
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
|
-
|
|
32
|
+
Shell-first source preview:
|
|
22
33
|
|
|
23
|
-
- use `find-leads`
|
|
34
|
+
- use `find-leads` with CampaignOffer campaignId as `campaignOfferId`
|
|
24
35
|
- do not call `import_leads`
|
|
25
|
-
- do not set `
|
|
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
|
|
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
|
|
35
|
-
only as preview. Pre-
|
|
36
|
-
`campaignOfferId
|
|
37
|
-
|
|
38
|
-
- `supplied-domains` uses `load_csv_domains
|
|
39
|
-
|
|
40
|
-
`campaignOfferId
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
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
|
|
4
|
-
|
|
5
|
-
|
|
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
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
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** —
|
|
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
|
|
27
|
-
lead-list table. Persist the returned
|
|
28
|
-
review batch into the campaign table
|
|
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
|
|
98
|
+
targetLeadCount: <importLimit from auto-execute.yaml; default 10>
|
|
58
99
|
})
|
|
59
100
|
```
|
|
60
101
|
|
|
61
|
-
Provider is inherited from the
|
|
62
|
-
|
|
63
|
-
three to the escalation logic
|
|
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
|
|
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
|
-
-
|
|
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`
|
|
98
|
-
-
|
|
101
|
+
- use `find-leads` shell-first campaign-attached preview mode
|
|
102
|
+
- pass CampaignOffer campaignId as `campaignOfferId`
|
|
99
103
|
- avoid `import_leads`
|
|
100
|
-
-
|
|
104
|
+
- do not set `workflowTableId`
|
|
101
105
|
- avoid `confirm_lead_list`
|
|
102
|
-
- avoid
|
|
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
|
|