@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.
Files changed (37) hide show
  1. package/README.md +4 -3
  2. package/agents/post-find-leads-filter-scout.md +5 -4
  3. package/agents/post-find-leads-message-scout.md +15 -14
  4. package/agents/source-scout-linkedin-engagement.md +6 -5
  5. package/agents/source-scout-prospeo-contact.md +4 -4
  6. package/agents/source-scout-sales-nav.md +4 -4
  7. package/dist/index-dev.js +0 -0
  8. package/dist/index.js +0 -0
  9. package/dist/tools/cells.js +1 -1
  10. package/dist/tools/leads.d.ts +37 -3
  11. package/dist/tools/leads.js +85 -77
  12. package/dist/tools/prompts.js +9 -9
  13. package/dist/tools/readiness.d.ts +7 -1
  14. package/dist/tools/readiness.js +10 -17
  15. package/dist/tools/registry.d.ts +17 -0
  16. package/dist/tools/rubrics.js +23 -20
  17. package/package.json +1 -1
  18. package/skills/create-campaign/SKILL.md +59 -56
  19. package/skills/create-campaign-v2/SKILL.md +44 -42
  20. package/skills/create-campaign-v2/SOUL.md +16 -13
  21. package/skills/create-campaign-v2/core/auto-execute.README.md +16 -17
  22. package/skills/create-campaign-v2/core/auto-execute.yaml +8 -7
  23. package/skills/create-campaign-v2/core/flow.v2.json +81 -149
  24. package/skills/create-campaign-v2/core/policy.md +13 -12
  25. package/skills/create-campaign-v2/references/approval-gate-framing.md +4 -3
  26. package/skills/create-campaign-v2/references/filter-leads.md +5 -4
  27. package/skills/create-campaign-v2/references/lead-validation-preview.md +2 -2
  28. package/skills/create-campaign-v2/references/sample-validation-loop.md +32 -27
  29. package/skills/create-campaign-v2/references/step-13-import-leads.md +44 -33
  30. package/skills/create-campaign-v2/references/watch-guide-narration.md +27 -28
  31. package/skills/create-campaign-v2-tail/SKILL.md +44 -44
  32. package/skills/create-rubric/SKILL.md +5 -5
  33. package/skills/find-leads/SKILL.md +2 -2
  34. package/skills/generate-messages/SKILL.md +2 -1
  35. package/skills/providers/prospeo.md +3 -3
  36. package/skills/providers/sales-nav.md +7 -7
  37. 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 batch (default 25 rows) to prove fit before the
10
- user spends credits on hundreds more leads. The sample loop has one job:
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 batch. If
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"` (the watched app is
24
- already on Filter Leads after `save_rubrics`; `validate-sample` is the
25
- logical loop name, not a visible route)
26
- - Imported review batch from Step 13 (size: `importLimit`, default 25)
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 imported review batch
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
- imported batch count
59
- - default targetCount=25 matches the default review batch, but pass the
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-batch rows where rubric passed
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: default `targetCount=25` returns `ready=true` after the first 25
154
- rubric completions. This matches the default review batch, but if a future
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 25-row review batch may return `ready=false`, `reason="timeout"`,
179
- and partial stats such as 20/25 scored, 4 passing, 4 messages generated. That is
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=25, importLimit=25):
198
-
199
- | passInSample | projectedPass | Handoff? (minProjectedPass=5) |
200
- | ------------ | ------------- | ----------------------------- |
201
- | 0 | 0 | No — escalate or revise |
202
- | 3 | 3 | Noescalate or revise |
203
- | 5 | 5 | Yes — exactly at floor |
204
- | 8 | 8 | Yes |
205
- | 10 | 10 | Yes |
206
- | 25 | 25 | Yes |
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 | Yesone 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
- Bounded review-batch sourcing + import happens after the existing campaign
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
- rubric/message prerequisites are true:
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, queue workflow cells, attach a sequence, or start before those
25
- prerequisites are true.
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 25-row campaign review batch.
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 import the bounded review batch into the campaign table
63
- with
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, targetLeadCount: importLimit })`.
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 importing the bounded
78
- review batch into the campaign table.
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 25-row review batch. It is the
119
- source-list export/materialization count from the approved source math:
120
- `min(rawResultCount, providerMax, ceil(targetGoodFitLeads /
121
- projectedFitRateAfterCleanup))`. If projected good fits remain below the target
122
- (often about 150+ usable prospects unless the campaign/source defaults specify
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 150 and the conservative
130
- fallback fit rate is 15%, so the default source-candidate plan is about 1,000
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, do not import the source list. Route back to
136
- find-leads and move to Sales Nav recent activity instead. After the scrape,
137
- `wait_for_lead_list_ready` must clear the approved source-candidate target; if
138
- it reports `source_under_capacity`, do not call `confirm_lead_list`. The
139
- subsequent `confirm_lead_list` call still uses `targetLeadCount: <importLimit>`
140
- so only the bounded review batch enters the campaign table.
141
-
142
- For supplied direct lists, `confirm_lead_list` must receive
143
- `targetLeadCount: <importLimit>` or explicit `sourceRowIds` so a 100-row source
144
- imports only the bounded review batch before greenlight.
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` imports
234
- only the bounded review batch. Enrichment, fit scoring, and message cells wait
235
- for saved rubrics plus the approved message set.
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 filtering the batch before rubrics and message approval are saved.
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 importing a review batch.",
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 importing so the review batch stays on-brief.",
142
- "nextAction": "Review batch"
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 batch:
163
+ Review/process sample:
164
164
 
165
165
  ```json
166
166
  {
167
167
  "stage": "review-batch",
168
- "headline": "Preparing your review batch",
169
- "visibleState": "This page shows the leads Codex selected for review.",
170
- "agentIntent": "Codex is preparing the first campaign batch from those leads.",
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": "Importing the review batch",
180
+ "headline": "Copying source leads",
181
181
  "visibleState": "The browser is still showing the approved LinkedIn Engagement source candidates.",
182
- "agentIntent": "Codex is importing only the bounded review batch into the campaign now.",
183
- "nextAction": "Review batch ready"
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 review batch import:
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": "25 review leads are in the campaign table from 181 source candidates. Sample read: 23/25 look like senior target-role fits; 2 need cleanup or manual review. The browser is showing the filter-choice screen.",
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 and
213
- message approval are saved. The Message Draft Builder should start immediately
214
- after the bounded review batch exists; mark it running only when that branch
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`, move to `apply-icp-rubric`
219
- so the watched app shows Filter Leads with saved rules before message approval
220
- unlocks any enrichment or filtering.
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 approval before saving the template to the campaign brief and queueing enrichment/filtering.",
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 saved the approved message template, queued the review-batch Enrich Prospect cells, and is waiting for one passing row with one generated message before moving to review.",
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`. The visible route
263
- is already Filter Leads after `save_rubrics`; approving the message only unlocks
264
- the bounded cascade from that screen. Move to Messages only once at least one
265
- review-batch row passes and one generated message is ready for review. Do not
266
- wait for the rest of the review batch or a stronger sample before asking the
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
- **review-batch cascade-driven**: you kick off Enrich Prospect only for
21
- the imported review batch, and the workflow engine chains DNC Check →
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 — import/confirm review batch only
29
+ Step 13 — materialize source list + confirm first sample
30
30
  materialize/reuse approved source with campaignOfferId
31
- import_leads(targetLeadCount=importLimit)
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 imported review batch
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 immediately after workflowTableId + review rows exist
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 bounded review-batch cascade
45
+ only then start the first-sample cascade
46
46
 
47
- Step 14 — kick bounded cascade + observe sample
48
- queue_cells(cellIds=<review-batch Enrich Prospect cells only>) <-- starts bounded chain
49
- wait_for_campaign_table_ready # wait until review-batch cascade starts returning filter results
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
- targetLeadCount: importLimit })`. Do not call `import_leads` for this
214
- branch; the lead list is the source, and `confirm_lead_list` imports the
215
- bounded review batch into the campaign table.
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, targetLeadCount: importLimit })`.
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: importLimit`.
223
- Never exceed `importLimit`. This is a review batch, not a full
224
- campaign-scale import. If a source search/list contains more rows than
225
- the cap, import only the first `importLimit` rows and leave the rest for
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
- targetLeadCount: importLimit })`.
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 bounded review
252
- batch; do not ask for a second yes/no gate between
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
- importLimit })`.
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: importLimit })`.
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 bounded review-batch rows are
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 batch is present. Do not queue cells in Step 13.
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 the message template is approved while the watched campaign is
287
- already on `CampaignOffer.currentStep === "apply-icp-rubric"` / Filter Leads.
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 the
292
- review batch only. After `save_rubrics` and the approved message template are
293
- persisted, Step 14 queues the review-batch Enrich Prospect cells, waits until
294
- filter results start landing, then moves to message observation as soon as one
295
- row passes. Step 15 opens review as soon as one passing generated message
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-batch rows with passesRubric === true
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-batch rows can continue processing in the background.
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-batch messages on the same sample that passed
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-batch handoff. Do NOT wait for the
444
- remaining review-batch rows and do NOT scale to the full source list before
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
- - Review-batch sourcing/import happens in Step 13, not during atomic mint.
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 is not part of the default tail. It requires user
545
- approval after the review batch.
546
- Import must never exceed `importLimit`.
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.