@sellable/mcp 0.1.151 → 0.1.153
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/README.md +4 -3
- package/agents/post-find-leads-filter-scout.md +5 -4
- package/agents/post-find-leads-message-scout.md +15 -14
- package/agents/source-scout-linkedin-engagement.md +6 -5
- package/agents/source-scout-prospeo-contact.md +4 -4
- package/agents/source-scout-sales-nav.md +4 -4
- package/dist/index-dev.js +0 -0
- package/dist/index.js +0 -0
- package/dist/tools/cells.js +1 -1
- package/dist/tools/leads.d.ts +37 -3
- package/dist/tools/leads.js +85 -77
- package/dist/tools/prompts.js +9 -9
- package/dist/tools/readiness.d.ts +7 -1
- package/dist/tools/readiness.js +10 -17
- package/dist/tools/registry.d.ts +17 -0
- package/dist/tools/rubrics.js +23 -20
- package/package.json +1 -1
- package/skills/create-campaign/SKILL.md +59 -56
- package/skills/create-campaign-v2/SKILL.md +44 -42
- package/skills/create-campaign-v2/SOUL.md +16 -13
- package/skills/create-campaign-v2/core/auto-execute.README.md +16 -17
- package/skills/create-campaign-v2/core/auto-execute.yaml +8 -7
- package/skills/create-campaign-v2/core/flow.v2.json +81 -149
- package/skills/create-campaign-v2/core/policy.md +13 -12
- package/skills/create-campaign-v2/references/approval-gate-framing.md +4 -3
- package/skills/create-campaign-v2/references/filter-leads.md +5 -4
- package/skills/create-campaign-v2/references/lead-validation-preview.md +2 -2
- package/skills/create-campaign-v2/references/sample-validation-loop.md +32 -27
- package/skills/create-campaign-v2/references/step-13-import-leads.md +44 -33
- package/skills/create-campaign-v2/references/watch-guide-narration.md +27 -28
- package/skills/create-campaign-v2-tail/SKILL.md +44 -44
- package/skills/create-rubric/SKILL.md +5 -5
- package/skills/find-leads/SKILL.md +2 -2
- package/skills/generate-messages/SKILL.md +2 -1
- package/skills/providers/prospeo.md +3 -3
- package/skills/providers/sales-nav.md +7 -7
- package/skills/providers/signal-discovery.md +47 -23
|
@@ -6,24 +6,27 @@ on every revision round.
|
|
|
6
6
|
|
|
7
7
|
## Principle
|
|
8
8
|
|
|
9
|
-
We spend a bounded review
|
|
10
|
-
user spends credits on
|
|
9
|
+
We spend a bounded review/process sample (default 15 rows) to prove fit before
|
|
10
|
+
the user spends credits on the rest of the confirmed source list. The sample loop has one job:
|
|
11
11
|
answer the question "do we have enough real passing examples for the user to
|
|
12
12
|
judge this campaign?" Message generation starts earlier: the first row that
|
|
13
13
|
passes filters is enough to begin observing or queueing Generate Message for
|
|
14
14
|
that passing row.
|
|
15
15
|
|
|
16
|
-
If the answer is yes, proceed to Step 15 messaging for the review
|
|
16
|
+
If the answer is yes, proceed to Step 15 messaging for the review sample. If
|
|
17
17
|
the answer is no, diagnose whether the brief is wrong or the list is wrong,
|
|
18
18
|
and either revise brief autonomously OR escalate to the user — never
|
|
19
19
|
auto-revise leads.
|
|
20
20
|
|
|
21
21
|
## Inputs
|
|
22
22
|
|
|
23
|
-
- `CampaignOffer.currentStep === "apply-icp-rubric"` (
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
-
|
|
23
|
+
- `CampaignOffer.currentStep === "apply-icp-rubric"` (Filter Leads is reached
|
|
24
|
+
only after saved-filter approval; `validate-sample` is the logical loop name,
|
|
25
|
+
not a visible route)
|
|
26
|
+
- Approved message template/token rules exist in the campaign brief before any
|
|
27
|
+
enrichment or scoring cells are queued
|
|
28
|
+
- Confirmed campaign rows from Step 13, with first review/process sample from
|
|
29
|
+
`reviewBatchRowIds` (size: `importLimit`, default 15)
|
|
27
30
|
- Config from `auto-execute.yaml`: `sample.sampleSize`,
|
|
28
31
|
`sample.minProjectedPass`, `sample.maxRevisionRounds`
|
|
29
32
|
- Persisted counter `revisionRound` (starts at 0 on first entry; persists
|
|
@@ -32,7 +35,7 @@ auto-revise leads.
|
|
|
32
35
|
## Loop Shape
|
|
33
36
|
|
|
34
37
|
```text
|
|
35
|
-
1. pick first sampleSize rows from the
|
|
38
|
+
1. pick first sampleSize rows from the confirmed campaign rows
|
|
36
39
|
|
|
37
40
|
2. paginate the campaign table to collect enrichCellIds where
|
|
38
41
|
enrichStatus = "pending" for the sample rows
|
|
@@ -41,7 +44,7 @@ auto-revise leads.
|
|
|
41
44
|
- strip carryData from every row BEFORE retaining in tail context
|
|
42
45
|
(see §Known Tool Behaviors #4)
|
|
43
46
|
|
|
44
|
-
3. queue_cells(enrichCellIds, batchSize <= 100)
|
|
47
|
+
3. after message-template approval, queue_cells(enrichCellIds, batchSize <= 100)
|
|
45
48
|
- confirm_lead_list does NOT auto-enqueue enrichment on imports
|
|
46
49
|
into an existing workflow table; Step 14 must enqueue manually
|
|
47
50
|
(see §Known Tool Behaviors #1)
|
|
@@ -54,9 +57,9 @@ auto-revise leads.
|
|
|
54
57
|
scoring from the queued Enrich Prospect cells
|
|
55
58
|
|
|
56
59
|
6. wait_for_rubric_results(sample, targetCount = <cohortSize>, minPassedCount = 1)
|
|
57
|
-
- cohortSize = stats.totalRows of the enrichment batch, or the
|
|
58
|
-
|
|
59
|
-
- default targetCount=
|
|
60
|
+
- cohortSize = stats.totalRows of the enrichment batch, or the review sample
|
|
61
|
+
count
|
|
62
|
+
- default targetCount=15 matches the default review sample, but pass the
|
|
60
63
|
explicit batch count anyway so future larger expansion batches do not
|
|
61
64
|
accidentally stop early
|
|
62
65
|
(see §Known Tool Behaviors #3)
|
|
@@ -73,6 +76,7 @@ auto-revise leads.
|
|
|
73
76
|
- partialResult: object when present
|
|
74
77
|
- stats: object
|
|
75
78
|
Never retain the full rows payload in tail context.
|
|
79
|
+
- 15 rows is the default customer-visible process sample
|
|
76
80
|
- 25 rows ≈ 67KB of payload (full post bodies in carryData)
|
|
77
81
|
- 100 rows ≈ 268KB; 500 rows ≈ 1.3MB — blows context on Opus
|
|
78
82
|
(see §Known Tool Behaviors #2)
|
|
@@ -96,7 +100,7 @@ auto-revise leads.
|
|
|
96
100
|
underfloor partial sample
|
|
97
101
|
|
|
98
102
|
9. compute metrics:
|
|
99
|
-
passInSample = count of review
|
|
103
|
+
passInSample = count of review/process sample rows where rubric passed
|
|
100
104
|
projectedPass = round(passInSample / sampleSize * importLimit)
|
|
101
105
|
|
|
102
106
|
10. branch:
|
|
@@ -150,8 +154,8 @@ inspection is required, and strip `carryData` before retention.
|
|
|
150
154
|
|
|
151
155
|
### 3. `wait_for_rubric_results.targetCount` defaults to 25
|
|
152
156
|
|
|
153
|
-
Observed:
|
|
154
|
-
|
|
157
|
+
Observed: older defaults used `targetCount=25`; the current default process
|
|
158
|
+
sample is 15. If a future
|
|
155
159
|
expansion batch is larger, pass the explicit batch size instead of relying on
|
|
156
160
|
the default. The completion semantic is `passRate.completed`, NOT "table
|
|
157
161
|
fully enriched and scored."
|
|
@@ -175,8 +179,8 @@ company, enrichCellId, enrichStatus) over the default shape.
|
|
|
175
179
|
|
|
176
180
|
### 5. `wait_for_rubric_results` can timeout with enough signal to decide
|
|
177
181
|
|
|
178
|
-
Observed: a
|
|
179
|
-
and partial stats such as
|
|
182
|
+
Observed: a 15-row review sample may return `ready=false`, `reason="timeout"`,
|
|
183
|
+
and partial stats such as 12/15 scored, 4 passing, 4 messages generated. That is
|
|
180
184
|
enough to diagnose an underperforming sample. Waiting again without active
|
|
181
185
|
processing makes the experience feel frozen.
|
|
182
186
|
|
|
@@ -194,16 +198,17 @@ once only if active processing is still visible.
|
|
|
194
198
|
projectedPass = round(passInSample / sampleSize * importLimit)
|
|
195
199
|
```
|
|
196
200
|
|
|
197
|
-
Worked examples with defaults (sampleSize=
|
|
198
|
-
|
|
199
|
-
| passInSample | projectedPass | Handoff? (minProjectedPass=
|
|
200
|
-
| ------------ | ------------- |
|
|
201
|
-
| 0 | 0 | No — escalate or revise
|
|
202
|
-
|
|
|
203
|
-
|
|
|
204
|
-
|
|
|
205
|
-
|
|
|
206
|
-
|
|
|
201
|
+
Worked examples with defaults (sampleSize=15, importLimit=15):
|
|
202
|
+
|
|
203
|
+
| passInSample | projectedPass | Handoff? (minProjectedPass=1) |
|
|
204
|
+
| ------------ | ------------- | ------------------------------------------------- |
|
|
205
|
+
| 0 | 0 | No — escalate or revise |
|
|
206
|
+
| 1 | 1 | Yes — one pass is enough for first-message review |
|
|
207
|
+
| 3 | 3 | Yes |
|
|
208
|
+
| 5 | 5 | Yes |
|
|
209
|
+
| 8 | 8 | Yes |
|
|
210
|
+
| 10 | 10 | Yes |
|
|
211
|
+
| 15 | 15 | Yes |
|
|
207
212
|
|
|
208
213
|
When non-default importLimit/sampleSize are configured, the math scales
|
|
209
214
|
the same way. In the default review-batch mode, the sample size equals the
|
|
@@ -10,19 +10,19 @@ CampaignOffer state and the watch link are canonical. Disk artifacts are
|
|
|
10
10
|
optional debug/UAT diagnostics, and normal customer runs should not expose local
|
|
11
11
|
draft files. Resume, gating, and handoff read campaign state first.
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
Source-list materialization and first-sample setup happen after the existing campaign
|
|
14
14
|
shell has the selected source attached with `campaignOfferId`. It happens before
|
|
15
15
|
the post-import fit/message scouts, so those scouts can use the actual campaign
|
|
16
16
|
table sample.
|
|
17
17
|
|
|
18
18
|
Do not queue workflow cells, attach a sequence, or start before the later
|
|
19
|
-
|
|
19
|
+
fit prerequisites are true:
|
|
20
20
|
|
|
21
21
|
1. rubrics saved to the campaign
|
|
22
|
-
2. approved message set synced into `campaignBrief`
|
|
23
22
|
|
|
24
|
-
Do not enrich
|
|
25
|
-
|
|
23
|
+
Do not enrich or queue filter cells before saved rubrics/run conditions are true.
|
|
24
|
+
Do not queue Generate Message cells, attach a sequence, or start before the
|
|
25
|
+
approved message set is synced into `campaignBrief`.
|
|
26
26
|
|
|
27
27
|
Before `import_leads`, the approved source must already be campaign-attached.
|
|
28
28
|
The find-leads scouts receive `campaignOfferId` up front, so selected Signal
|
|
@@ -55,13 +55,12 @@ Supported branches:
|
|
|
55
55
|
`import_leads({ campaignOfferId })` for Signal Discovery unless selected posts
|
|
56
56
|
already exist on the campaign. Provider import materializes the source lead
|
|
57
57
|
list from the approved source-capacity plan; it is not the same target as the
|
|
58
|
-
default
|
|
58
|
+
default 15-row campaign review/process sample.
|
|
59
59
|
- **Supplied LinkedIn profile CSV** — confirm `load_csv_linkedin_leads` only
|
|
60
60
|
after the user approves that supplied-list source. Batch/materialize the
|
|
61
61
|
uploaded CSV into a Sellable lead-list table. Persist the returned
|
|
62
|
-
`leadListId`, then
|
|
63
|
-
|
|
64
|
-
`confirm_lead_list({ sourceLeadListId: leadListId, targetLeadCount: importLimit })`.
|
|
62
|
+
`leadListId`, then copy the confirmed list into the campaign table with
|
|
63
|
+
`confirm_lead_list({ sourceLeadListId: leadListId, reviewBatchLimit: 15 })`.
|
|
65
64
|
Do not call `import_leads` for this branch; the materialized lead list is the
|
|
66
65
|
source.
|
|
67
66
|
Do **not** call `wait_for_lead_list_ready` for this branch; there is no
|
|
@@ -69,13 +68,13 @@ Supported branches:
|
|
|
69
68
|
in an import-failed state before `confirm_lead_list`.
|
|
70
69
|
- **Existing Sellable lead list** — revalidate the source list in the same
|
|
71
70
|
workspace, then reuse it with
|
|
72
|
-
`confirm_lead_list({ sourceLeadListId,
|
|
71
|
+
`confirm_lead_list({ sourceLeadListId, reviewBatchLimit: 15 })`.
|
|
73
72
|
Do **not** call `wait_for_lead_list_ready` for this branch.
|
|
74
73
|
- **Supplied domain/account CSV** — reuse or confirm `load_csv_domains`, keep
|
|
75
74
|
its `domainFilterId`, run a campaign-associated Prospeo people search with
|
|
76
75
|
`campaignOfferId`, provider prompt preflight, and `domainFilterId`, then
|
|
77
|
-
materialize the source list from that search before
|
|
78
|
-
|
|
76
|
+
materialize the source list from that search before copying the confirmed
|
|
77
|
+
list into the campaign table and processing the first 15 rows.
|
|
79
78
|
|
|
80
79
|
## Campaign-Attached Source Contract
|
|
81
80
|
|
|
@@ -115,33 +114,43 @@ import_leads({
|
|
|
115
114
|
|
|
116
115
|
Provider is inherited from the selected source decision (not re-selected here)
|
|
117
116
|
and should already be saved on the campaign before import. For Sales Nav and
|
|
118
|
-
Prospeo, `<sourceCandidateTarget>` is not the
|
|
119
|
-
source-list export/materialization count from the approved source math
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
otherwise), return to find-leads and refine filters before import. Response
|
|
117
|
+
Prospeo, `<sourceCandidateTarget>` is not the 15-row review/process sample. It is
|
|
118
|
+
the source-list export/materialization count from the approved source math. Aim
|
|
119
|
+
for about 1,000 source contacts by default and use the provider cap internally
|
|
120
|
+
when the raw pool is larger. If projected good fits remain below the target,
|
|
121
|
+
return to find-leads and refine filters before import. Response
|
|
124
122
|
returns `{ imported, skipped, duplicates }` — surface all three to the
|
|
125
123
|
escalation logic so dedup ratios are visible.
|
|
126
124
|
|
|
127
125
|
For Signal Discovery, do not scrape every currently selected/promoted sample
|
|
128
126
|
post by default. Before `import_leads`, reconcile selected posts with the
|
|
129
|
-
approved source math. The default good-fit target is
|
|
130
|
-
|
|
127
|
+
approved source math. The default good-fit target is 300 and the planning
|
|
128
|
+
fit-rate assumption is 20%, so the default source-candidate plan is about 1,500
|
|
131
129
|
raw engagers. If the sampled fit rate is stronger or weaker, use the sampled
|
|
132
130
|
rate with a conservative cleanup factor, cap the source candidates at the
|
|
133
131
|
approved provider limit, and select only enough posts to reach that engager
|
|
134
132
|
count. The planning floor is 10% projected fit after cleanup; if Signal
|
|
135
|
-
Discovery falls below that floor
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
133
|
+
Discovery falls below that floor during the pre-scrape sample, revise the source
|
|
134
|
+
plan before importing. After an approved scrape starts, do not discard a nonempty
|
|
135
|
+
completed source list just because it lands below the source-candidate target:
|
|
136
|
+
confirm/copy the completed list for the first review sample, then add more posts
|
|
137
|
+
or switch provider if the sample quality or scale is not enough. The subsequent
|
|
138
|
+
`confirm_lead_list` call uses `reviewBatchLimit: 15`. It copies the confirmed
|
|
139
|
+
source rows into the campaign table and returns only the first 15 rows as the
|
|
140
|
+
review/process sample.
|
|
141
|
+
|
|
142
|
+
Harvest-specific interpretation: Signal Discovery post comments and reactions
|
|
143
|
+
are paged from Harvest at about 100 records per page, with production caps of
|
|
144
|
+
1,500 reactions and 300 comments per selected post. The source list stores
|
|
145
|
+
headline-passing candidates after that scrape. Therefore `71 source rows` means
|
|
146
|
+
71 candidates passed headline/list filtering and were inserted; it does not
|
|
147
|
+
mean the post scrape stopped after 71 raw engagers. Treat raw visible engagement,
|
|
148
|
+
raw fetched pages, inserted source rows, and the 15-row review/process sample as
|
|
149
|
+
separate numbers.
|
|
150
|
+
|
|
151
|
+
For supplied direct lists, `confirm_lead_list` should receive
|
|
152
|
+
`reviewBatchLimit: 15` so the campaign table can hold the confirmed list while
|
|
153
|
+
the first 15 rows drive the initial approval flow.
|
|
145
154
|
|
|
146
155
|
## `mode=add` Two-Step Handshake
|
|
147
156
|
|
|
@@ -230,9 +239,11 @@ cannot scale further without a new source lane.
|
|
|
230
239
|
- Zero-imported + high-skipped ⇒ lane-exhausted operator notice (NOT a
|
|
231
240
|
silent proceed).
|
|
232
241
|
- `import_leads` is allowed immediately after the concrete source-action
|
|
233
|
-
approval. It materializes the source list and then `confirm_lead_list`
|
|
234
|
-
|
|
235
|
-
|
|
242
|
+
approval. It materializes the source list and then `confirm_lead_list` copies
|
|
243
|
+
the confirmed source rows into the campaign table while returning only the
|
|
244
|
+
first 15 rows for the initial review/process sample. Enrichment and fit
|
|
245
|
+
scoring wait for saved rubrics/run conditions; message cells wait for the
|
|
246
|
+
approved message set.
|
|
236
247
|
- Step 13 MUST reuse an approved source already attached with `campaignOfferId`
|
|
237
248
|
before `import_leads`; it does not replay provider searches.
|
|
238
249
|
- `import_leads` is NOT called again in Step 14 (validate-sample). Full
|
|
@@ -38,7 +38,7 @@ states.
|
|
|
38
38
|
Apollo, or existing list.
|
|
39
39
|
- During search iteration, say what you tried, what you are trying next, what
|
|
40
40
|
sample you are checking, and why that helps this campaign.
|
|
41
|
-
- At filter choice, do not say
|
|
41
|
+
- At filter choice, do not say the batch is filtering before rubrics are saved.
|
|
42
42
|
- Avoid internal terms: MCP, tool, currentStep, workflow table, scout, debug.
|
|
43
43
|
- Treat `safety` as optional internal context, not a visible disclaimer.
|
|
44
44
|
- Do not repeat long negative lists like "no leads import, no enrichment, no
|
|
@@ -114,7 +114,7 @@ Source direction override:
|
|
|
114
114
|
"stage": "find-leads",
|
|
115
115
|
"headline": "Testing the requested source",
|
|
116
116
|
"visibleState": "The campaign asked for hiring and account signals, so Codex is starting with Prospeo instead of the default LinkedIn engagement path.",
|
|
117
|
-
"agentIntent": "It is checking whether this source has enough reachable ICP-looking people before
|
|
117
|
+
"agentIntent": "It is checking whether this source has enough reachable ICP-looking people before building the source list.",
|
|
118
118
|
"nextAction": "Review source"
|
|
119
119
|
}
|
|
120
120
|
```
|
|
@@ -138,8 +138,8 @@ Search iteration:
|
|
|
138
138
|
"stage": "find-leads",
|
|
139
139
|
"headline": "Tightening the search",
|
|
140
140
|
"visibleState": "The first Signal Discovery search was broad, so Codex is trying a narrower Claude Code founder lane.",
|
|
141
|
-
"agentIntent": "It is sampling a few engagers before
|
|
142
|
-
"nextAction": "Review
|
|
141
|
+
"agentIntent": "It is sampling a few engagers before scraping so the first review sample stays on-brief.",
|
|
142
|
+
"nextAction": "Review source math"
|
|
143
143
|
}
|
|
144
144
|
```
|
|
145
145
|
|
|
@@ -160,14 +160,14 @@ chat, update the guide to route the user back to Codex/Claude for approval. Do
|
|
|
160
160
|
not keep future-tense copy such as `I'll show a source recommendation` once the
|
|
161
161
|
decision card is visible.
|
|
162
162
|
|
|
163
|
-
Review
|
|
163
|
+
Review/process sample:
|
|
164
164
|
|
|
165
165
|
```json
|
|
166
166
|
{
|
|
167
167
|
"stage": "review-batch",
|
|
168
|
-
"headline": "Preparing your
|
|
169
|
-
"visibleState": "This page shows the leads Codex
|
|
170
|
-
"agentIntent": "Codex is preparing the first campaign
|
|
168
|
+
"headline": "Preparing your first sample",
|
|
169
|
+
"visibleState": "This page shows the confirmed source leads. Codex will process the first 15 for review.",
|
|
170
|
+
"agentIntent": "Codex is preparing the first campaign sample from those leads.",
|
|
171
171
|
"nextAction": "Fit + message"
|
|
172
172
|
}
|
|
173
173
|
```
|
|
@@ -177,10 +177,10 @@ Source approved and import starting:
|
|
|
177
177
|
```json
|
|
178
178
|
{
|
|
179
179
|
"stage": "review-batch",
|
|
180
|
-
"headline": "
|
|
180
|
+
"headline": "Copying source leads",
|
|
181
181
|
"visibleState": "The browser is still showing the approved LinkedIn Engagement source candidates.",
|
|
182
|
-
"agentIntent": "Codex is
|
|
183
|
-
"nextAction": "Review
|
|
182
|
+
"agentIntent": "Codex is copying the confirmed source into the campaign and will process only the first 15 for review.",
|
|
183
|
+
"nextAction": "Review sample ready"
|
|
184
184
|
}
|
|
185
185
|
```
|
|
186
186
|
|
|
@@ -189,13 +189,13 @@ current-tense import copy. Do not leave the guide in source-approved or
|
|
|
189
189
|
future-tense copy such as `I'll show the review-batch outcome`; that makes the
|
|
190
190
|
browser guide describe a different moment than chat.
|
|
191
191
|
|
|
192
|
-
After
|
|
192
|
+
After confirmed source rows are copied:
|
|
193
193
|
|
|
194
194
|
```json
|
|
195
195
|
{
|
|
196
196
|
"stage": "fit-message",
|
|
197
197
|
"headline": "Filtering may not be needed",
|
|
198
|
-
"visibleState": "
|
|
198
|
+
"visibleState": "15 review leads are ready from 181 source candidates. Sample read: 13/15 look like senior target-role fits; 2 need cleanup or manual review. The browser is showing the filter-choice screen.",
|
|
199
199
|
"agentIntent": "Codex is not recommending filters because the sample already looks clean. Fit examples: Dave Kranowitz (Chief Revenue Officer); Marie Didominica (Head of Growth Marketing). Add filters only for a narrow exclusion.",
|
|
200
200
|
"nextAction": "Skip filters or add a narrow cleanup rule",
|
|
201
201
|
"workerStatuses": {
|
|
@@ -209,15 +209,15 @@ Use this after the selected review rows are present and before filters are
|
|
|
209
209
|
saved. The filter recommendation must be row-specific: say why filters are
|
|
210
210
|
recommended from the sample counts and examples, or say filtering may not be
|
|
211
211
|
needed when the visible review rows already look clean. This is a user decision
|
|
212
|
-
gate, not active filtering; do not say filtering the batch before rubrics
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
actually started.
|
|
212
|
+
gate, not active filtering; do not say filtering the batch before rubrics are
|
|
213
|
+
saved. The Message Draft Builder should start after the filter-choice answer;
|
|
214
|
+
mark it running only when that branch actually started.
|
|
216
215
|
When the user chooses filters, immediately persist `enableICPFilters: true` and
|
|
217
216
|
move to `create-icp-rubric` so the watched app shows Filter Rules while Codex
|
|
218
|
-
defines the rules in chat. After `save_rubrics`,
|
|
219
|
-
|
|
220
|
-
|
|
217
|
+
defines the rules in chat. After `save_rubrics`, keep the app on Filter Rules so
|
|
218
|
+
the user can read and approve the saved criteria. Only that approval moves the
|
|
219
|
+
app to `apply-icp-rubric` / Filter Leads, where it waits for message approval
|
|
220
|
+
before enrichment/filtering is queued.
|
|
221
221
|
|
|
222
222
|
Fit + message:
|
|
223
223
|
|
|
@@ -242,7 +242,7 @@ Review ready:
|
|
|
242
242
|
"stage": "review-ready",
|
|
243
243
|
"headline": "Review the message template",
|
|
244
244
|
"visibleState": "The browser remains on Filter Leads while the message template is reviewed in chat.",
|
|
245
|
-
"agentIntent": "Codex is waiting for
|
|
245
|
+
"agentIntent": "Codex is waiting for the approved template before row processing starts.",
|
|
246
246
|
"nextAction": "Approve or revise the template"
|
|
247
247
|
}
|
|
248
248
|
```
|
|
@@ -254,17 +254,16 @@ Template approved, bounded filter test running:
|
|
|
254
254
|
"stage": "fit-message",
|
|
255
255
|
"headline": "Template saved",
|
|
256
256
|
"visibleState": "The browser stays on Filter Leads while the bounded enrichment and filter test runs.",
|
|
257
|
-
"agentIntent": "Codex
|
|
257
|
+
"agentIntent": "Codex is waiting for one passing row with one generated message before moving to review.",
|
|
258
258
|
"nextAction": "Review the first passing generated message"
|
|
259
259
|
}
|
|
260
260
|
```
|
|
261
261
|
|
|
262
|
-
Do not move to Messages immediately after `approve-message`.
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
user to approve the generated message.
|
|
262
|
+
Do not move to Messages immediately after `approve-message`. Filter Leads is
|
|
263
|
+
reached only after saved-filter approval; approving the message unlocks the
|
|
264
|
+
bounded cascade from that screen. Move to Messages once at least one review row
|
|
265
|
+
passes and one generated message is ready. Do not wait for the rest of the batch
|
|
266
|
+
or a stronger sample before asking the user to approve the generated message.
|
|
268
267
|
|
|
269
268
|
Messages waiting for template:
|
|
270
269
|
|
|
@@ -17,8 +17,8 @@ table.
|
|
|
17
17
|
## MANDATORY TOOL ORDER (read this BEFORE any tail step)
|
|
18
18
|
|
|
19
19
|
Every tail run MUST call these tools in this exact order. The tail is
|
|
20
|
-
**
|
|
21
|
-
the
|
|
20
|
+
**first-sample cascade-driven**: you kick off Enrich Prospect only for
|
|
21
|
+
the first review/process sample, and the workflow engine chains DNC Check →
|
|
22
22
|
ICP Score → Passes Rubric → Generate Message automatically. Your job is
|
|
23
23
|
to START the bounded cascade, WAIT until filter results land, OBSERVE message
|
|
24
24
|
generation as soon as one row passes, and stop for review.
|
|
@@ -26,27 +26,27 @@ Do NOT manually run rubric-check, enrich, or message-generation
|
|
|
26
26
|
tools — the cascade already does them.
|
|
27
27
|
|
|
28
28
|
```text
|
|
29
|
-
Step 13 —
|
|
29
|
+
Step 13 — materialize source list + confirm first sample
|
|
30
30
|
materialize/reuse approved source with campaignOfferId
|
|
31
|
-
import_leads(
|
|
31
|
+
import_leads(source-list target; SalesNav/Prospeo default ~1000, Signal Discovery default ~1500 engagers)
|
|
32
32
|
wait_for_lead_list_ready
|
|
33
|
-
confirm_lead_list
|
|
33
|
+
confirm_lead_list(reviewBatchLimit=15)
|
|
34
34
|
wait_for_campaign_table_ready # campaign table exists
|
|
35
|
-
get_rows_minimal # read
|
|
35
|
+
get_rows_minimal # read first review/process sample
|
|
36
36
|
update_campaign(currentStep=filter-choice)
|
|
37
37
|
|
|
38
38
|
Post-import main thread
|
|
39
|
-
launch Message Draft Builder
|
|
39
|
+
launch Message Draft Builder after the filter-choice answer
|
|
40
40
|
launch Lead Fit Builder only after user chooses filters
|
|
41
41
|
save_rubrics({ campaignOfferId, leadScoringRubrics }) after the campaign table exists
|
|
42
42
|
keep the watched app on Filter Leads after rubrics are saved
|
|
43
43
|
while on Filter Leads, show the message template recommendation from the background Message Draft Builder
|
|
44
44
|
after approve-message, update_campaign_brief writes `## Approved Message Template` with `{{...}}` tokens
|
|
45
|
-
only then start the
|
|
45
|
+
only then start the first-sample cascade
|
|
46
46
|
|
|
47
|
-
Step 14 — kick
|
|
48
|
-
queue_cells(cellIds=<review
|
|
49
|
-
wait_for_campaign_table_ready # wait until
|
|
47
|
+
Step 14 — kick first-sample cascade + observe sample
|
|
48
|
+
queue_cells(cellIds=<first 15 review/process Enrich Prospect cells only>) <-- starts bounded chain
|
|
49
|
+
wait_for_campaign_table_ready # wait until sample cascade starts returning filter results
|
|
50
50
|
get_rows_minimal # read passesRubric + message cell status per row
|
|
51
51
|
wait_for_rubric_results(minPassedCount=1, includeRows=false)
|
|
52
52
|
if at least one row passes: update_campaign(currentStep=auto-execute-messaging)
|
|
@@ -210,20 +210,19 @@ import milestone.
|
|
|
210
210
|
token, confirm `load_csv_linkedin_leads` as source-list materialization to batch the
|
|
211
211
|
supplied CSV into a Sellable lead list, persist the returned `leadListId`,
|
|
212
212
|
then call `confirm_lead_list({ sourceLeadListId: leadListId,
|
|
213
|
-
|
|
214
|
-
branch; the lead list is the source, and `confirm_lead_list`
|
|
215
|
-
|
|
213
|
+
reviewBatchLimit: 15 })`. Do not call `import_leads` for this
|
|
214
|
+
branch; the lead list is the source, and `confirm_lead_list` copies the
|
|
215
|
+
confirmed source rows into the campaign table.
|
|
216
216
|
- `existing-lead-list`: revalidate that the lead list still exists in the
|
|
217
217
|
same workspace, reuse it as `sourceLeadListId`, then call
|
|
218
|
-
`confirm_lead_list({ sourceLeadListId,
|
|
218
|
+
`confirm_lead_list({ sourceLeadListId, reviewBatchLimit: 15 })`.
|
|
219
219
|
- `supplied-domains`: reuse or confirm `load_csv_domains`, preserving
|
|
220
220
|
`domainFilterId`; run a campaign-associated Prospeo people search with
|
|
221
221
|
`campaignOfferId`, provider prompt preflight, and that `domainFilterId`;
|
|
222
|
-
then import with `targetLeadCount
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
a later expansion step.
|
|
222
|
+
then import with `targetLeadCount` set to the approved source-list target
|
|
223
|
+
(default about 1,000). The first 15 copied campaign rows are the
|
|
224
|
+
review/process sample; the full confirmed source list is copied into the
|
|
225
|
+
campaign for later expansion.
|
|
227
226
|
Persist or recover materialized IDs on resume: `leadListId`,
|
|
228
227
|
`domainFilterId`, `searchId`, `selectedLeadListId`, `workflowTableId`, and
|
|
229
228
|
imported row IDs. If the source file changed after preview, or an existing lead list
|
|
@@ -236,7 +235,7 @@ campaignOfferId, confirmed: true })` -> `search_signals({ campaignOfferId,
|
|
|
236
235
|
...approved recipe })` -> `select_promising_posts({ campaignOfferId,
|
|
237
236
|
selectionMode: "replace", selections, headlineICPCriteria })` ->
|
|
238
237
|
`import_leads({ campaignOfferId, provider: "signal-discovery",
|
|
239
|
-
|
|
238
|
+
targetEngagerCount: 1500 })`.
|
|
240
239
|
For Signal Discovery, the promotion/select step is load-bearing. Use
|
|
241
240
|
post IDs from the current campaign-scoped `search_signals` response or
|
|
242
241
|
posts the user has visibly promoted in the campaign UI. Never use post IDs
|
|
@@ -248,26 +247,26 @@ targetLeadCount: importLimit })`.
|
|
|
248
247
|
re-run a narrow campaign-scoped `search_signals` call to recover current
|
|
249
248
|
post rows, or ask the user to promote the desired posts in the UI and then
|
|
250
249
|
retry `import_leads`.
|
|
251
|
-
Source approval is the explicit confirmation for this
|
|
252
|
-
|
|
250
|
+
Source approval is the explicit confirmation for this selected-post
|
|
251
|
+
scrape; do not ask for a second yes/no gate between
|
|
253
252
|
`select_promising_posts` and `import_leads`.
|
|
254
253
|
- Sales Nav: `get_provider_prompt({ provider: "sales-nav", campaignOfferId,
|
|
255
254
|
confirmed: true })` -> rebuild/verify filter IDs -> `search_sales_nav({
|
|
256
255
|
campaignOfferId, filters, confirmed: true })` -> `import_leads({
|
|
257
256
|
campaignOfferId, provider: "sales-nav", searchId, targetLeadCount:
|
|
258
|
-
|
|
257
|
+
1000 })`.
|
|
259
258
|
- Prospeo: `get_provider_prompt({ provider: "prospeo", campaignOfferId,
|
|
260
259
|
confirmed: true })` -> reuse `domainFilterId` when present ->
|
|
261
260
|
`search_prospeo({ campaignOfferId, filters, domainFilterId, confirmed:
|
|
262
261
|
true })` -> `import_leads({ campaignOfferId, provider: "prospeo",
|
|
263
|
-
searchId, targetLeadCount:
|
|
262
|
+
searchId, targetLeadCount: 1000 })`.
|
|
264
263
|
3. `wait_for_lead_list_ready` when a provider import job exists, then
|
|
265
264
|
`confirm_lead_list`. Persist both identifiers: `selectedLeadListId` remains
|
|
266
265
|
the source list and `workflowTableId` is the campaign table.
|
|
267
|
-
4. `wait_for_campaign_table_ready` until the
|
|
266
|
+
4. `wait_for_campaign_table_ready` until the first review/process sample rows are
|
|
268
267
|
available in the campaign table.
|
|
269
268
|
5. Call `get_rows_minimal({ tableId: workflowTableId })` and confirm the first
|
|
270
|
-
review
|
|
269
|
+
15-row review/process sample is present. Do not queue cells in Step 13.
|
|
271
270
|
6. If the import returns zero usable leads, ESCALATE per
|
|
272
271
|
`references/escalation-ladder.md` (hard fail).
|
|
273
272
|
7. `update_campaign({ campaignId, currentStep: "filter-choice" })`.
|
|
@@ -283,16 +282,16 @@ succeeded.
|
|
|
283
282
|
|
|
284
283
|
## Step 14: validate-sample (logical loop on Filter Leads)
|
|
285
284
|
|
|
286
|
-
Entered after
|
|
287
|
-
|
|
285
|
+
Entered after rubrics are saved while the watched campaign is already on
|
|
286
|
+
`CampaignOffer.currentStep === "apply-icp-rubric"` / Filter Leads.
|
|
288
287
|
Do not route to a visible `validate-sample` step. Full decision tree lives in
|
|
289
288
|
`references/sample-validation-loop.md`.
|
|
290
289
|
|
|
291
|
-
**Step 14 starts the bounded cascade, then observes it.** Step 13 imported
|
|
292
|
-
review batch only. After `save_rubrics
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
290
|
+
**Step 14 starts the bounded fit cascade, then observes it.** Step 13 imported
|
|
291
|
+
the review batch only. After `save_rubrics`, Step 14 queues the review-batch
|
|
292
|
+
Enrich Prospect cells, waits until filter results start landing, then moves to
|
|
293
|
+
message observation as soon as one row passes and approved-template message
|
|
294
|
+
generation is ready. Step 15 opens review as soon as one passing generated message
|
|
296
295
|
exists. Do not wait for a larger or stronger sample once that first passing
|
|
297
296
|
message is ready. It does NOT call `check_rubric`,
|
|
298
297
|
`bulk_enrich_with_prospeo`, or any other direct enrichment/scoring tool.
|
|
@@ -304,7 +303,7 @@ queue_cells({ tableId: workflowTableId, cellIds: reviewBatchEnrichCellIds })
|
|
|
304
303
|
wait_for_campaign_table_ready({ tableId: workflowTableId })
|
|
305
304
|
get_rows_minimal({ tableId: workflowTableId })
|
|
306
305
|
wait_for_rubric_results({ tableId: workflowTableId, targetCount: cohortSize, minPassedCount: 1, includeRows: false })
|
|
307
|
-
passInSample = count of first sampleSize review
|
|
306
|
+
passInSample = count of first sampleSize review/process sample rows with passesRubric === true
|
|
308
307
|
projectedPass = round(passInSample / sampleSize * importLimit)
|
|
309
308
|
|
|
310
309
|
if wait_for_rubric_results.ready === true and passRate.passed >= 1:
|
|
@@ -390,7 +389,7 @@ Template`. If it does not, fail before the cascade runs. Do not repair
|
|
|
390
389
|
minPassedCount: 1, minMessagesCount: 1, includeRows: false })` until the
|
|
391
390
|
first passing generated message is ready. Do not wait for every passing row's
|
|
392
391
|
Generate Message cell, and do not add "one more wait" for a stronger sample.
|
|
393
|
-
Remaining review
|
|
392
|
+
Remaining review/process sample rows can continue processing in the background.
|
|
394
393
|
4. Read the first ready generated message back via `get_rows` (full) and
|
|
395
394
|
sanity-check that sample against the Phase 84 token contract: no unresolved
|
|
396
395
|
`{{tokens}}`, no invented proof, one sentence per line, etc.
|
|
@@ -414,7 +413,7 @@ job from messaging. Does NOT generate or write message text. A
|
|
|
414
413
|
sequence with no `Generate Message` outputs would try to send empty
|
|
415
414
|
strings, which is why Step 16 requires Step 15 to be complete.
|
|
416
415
|
|
|
417
|
-
1. Observe the review-
|
|
416
|
+
1. Observe the review-sample messages on the same sample that passed
|
|
418
417
|
validation.
|
|
419
418
|
2. If `messaging.critique.enabled` is true (Plan 85-03), run the
|
|
420
419
|
bounded critique pass on the sample output per
|
|
@@ -440,9 +439,9 @@ strings, which is why Step 16 requires Step 15 to be complete.
|
|
|
440
439
|
message that contains unresolved or unsupported tokens (including
|
|
441
440
|
any critique rewrite that tried to introduce one).
|
|
442
441
|
5. If the first passing generated message passes the token contract (and
|
|
443
|
-
critique when enabled), stop at the review
|
|
444
|
-
remaining review
|
|
445
|
-
explicit user expansion approval.
|
|
442
|
+
critique when enabled), stop at the review handoff. Do NOT wait for the
|
|
443
|
+
remaining review/process sample rows and do NOT scale to the full source list
|
|
444
|
+
before explicit user expansion approval.
|
|
446
445
|
6. If the sample fails the token contract or critique, diagnose +
|
|
447
446
|
loop the same way Step 14 does (brief-vs-list), subject to the same
|
|
448
447
|
`maxRevisionRounds` cap.
|
|
@@ -537,13 +536,14 @@ runs.
|
|
|
537
536
|
|
|
538
537
|
## Tail Hard Rules
|
|
539
538
|
|
|
540
|
-
-
|
|
539
|
+
- Source-list materialization and first-sample confirmation happen in Step 13,
|
|
540
|
+
not during atomic mint.
|
|
541
541
|
- Step 13 materializes/reuses the approved source with `campaignOfferId` before
|
|
542
542
|
import. Do not import a legacy campaignless Signal source until selected posts
|
|
543
543
|
exist on the campaign.
|
|
544
|
-
- Full-list expansion
|
|
545
|
-
|
|
546
|
-
|
|
544
|
+
- Full-list expansion happens only after the first review flow proves out. The
|
|
545
|
+
first enrichment/scoring pass must stay capped to the 15-row review/process
|
|
546
|
+
sample.
|
|
547
547
|
- The tail NEVER calls `start_campaign` on its own.
|
|
548
548
|
- The tail NEVER auto-revises leads. Brief revision is autonomous; lead
|
|
549
549
|
revision is always operator-gated.
|