@sellable/mcp 0.1.38-rc.3 → 0.1.38

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sellable/mcp",
3
- "version": "0.1.38-rc.3",
3
+ "version": "0.1.38",
4
4
  "type": "module",
5
5
  "description": "Sellable MCP server for Claude Code and Codex campaign workflows",
6
6
  "main": "dist/index.js",
@@ -761,17 +761,17 @@ Do not:
761
761
 
762
762
  ## Step 3: Generate Message
763
763
 
764
- The full Step 3 detail (~45k chars: message-validation workflow, gold-standard rules, proof inventory, candidate generation, finalizer pass, message-review-decision contract, A+B+C subject shape, token-fill rules, etc.) lives in a dedicated on-demand subskill to keep this entry prompt fast to load.
764
+ Step 3 follows the canonical `generate-messages` subskill gold-standard examples, REPLY framework fallback, Proof Inventory, Token Fill Rules, Token Adherence Table, Angle Drafts, Kill/Combine Review, Finalists, Finalizer Pass, Gold-Standard Quality Gate, Skeptical Prospect Review, Winner Gate, and the message-review-decision contract. The `generate-messages` subskill is the authoritative source — this entry prompt does not duplicate it.
765
765
 
766
- When you reach Step 3, **load the dedicated subskill verbatim**:
766
+ When you reach Step 3, load the canonical subskill verbatim via chunked reads:
767
767
 
768
768
  ```
769
- mcp__sellable__get_subskill_prompt({ subskillName: "create-campaign-v2-generate-message" })
769
+ mcp__sellable__get_subskill_prompt({ subskillName: "generate-messages", offset, limit })
770
770
  ```
771
771
 
772
- Follow that prompt verbatim. It contains the full message-validation workflow, gold-standard rules, proof inventory, candidate generation, finalizer pass, message-review-decision contract, and all token-fill rules (A+B+C subject shape, Good/Bad token-fill examples, fallback-if-missing logic, tokenized templates).
772
+ Continue chunked-read until `hasMore=false`. Treat all chunks as one prompt load. Follow it verbatim once loaded. The same directive is locked in `flow.v2.json::messageGeneration.toolRules`.
773
773
 
774
- Do NOT proceed to Step 4 (message review gate) without loading and following the generate-message subskill message-validation.md must prove the full generate-messages workflow ran, and that workflow is defined in the subskill.
774
+ Do NOT proceed to Step 4 (message review gate) without loading and following the full generate-messages workflow. `message-validation.md` must prove the full workflow ran (all required sections present + raw sendable Selected Winner) before `message-review.md` can recommend `approve-message`. If the message review or approval packet is ready but the generate-messages prompt was not retrieved in this run, route back to message-generation instead of asking the commit gate.
775
775
 
776
776
  ## Tail (MANDATORY TOOL ORDER + Steps 13-16 + Threshold Trips + Hard Rules)
777
777
 
@@ -1,838 +0,0 @@
1
- ---
2
- name: create-campaign-v2-generate-message
3
- description: Step 3 (Generate Message) detailed workflow for create-campaign-v2 — full message-validation + gold-standard rules + proof inventory + candidate generation + finalizer + message-review-decision contract. Loaded on-demand via get_subskill_prompt to keep the entry SKILL.md slim. Internal subskill.
4
- visibility: internal
5
- ---
6
-
7
- # Step 3: Generate Message (Detail)
8
-
9
- This is the full Step 3 detail extracted from the main create-campaign-v2 SKILL.md to keep the entry prompt slim. The agent loads this on-demand when entering Step 3 of the v2 flow. Follow this verbatim.
10
-
11
- ## Step 3: Generate Message
12
-
13
- Read `brief.md`, `lead-review.md`, `lead-sample.json`, optional
14
- `lead-filter.md`, optional `message-prep.md`, and optional
15
- `message-candidate-drafts.md`.
16
-
17
- This branch can run in parallel with Step 2 once `lead-review.md` and
18
- `lead-sample.json` exist. If `lead-filter.md` is not ready yet, draft from the
19
- lead-review message handoff rows, then reconcile after the filter lands.
20
-
21
- Write:
22
-
23
- - `message-validation.md`
24
-
25
- Run the `generate-messages` skill in caller-declared `DRY MODE`. Its SKILL.md
26
- holds the full drafting contract (retrieval, proof inventory, candidates,
27
- finalizer pass, voice rules, safety). This step only covers orchestration.
28
- This is not optional: before any write to `message-validation.md`,
29
- `message-review.md`, `approval-packet.md`, or a commit-gate question, the
30
- current run must load the full `generate-messages` prompt. In hosted/from-
31
- scratch runs, load it with chunked `get_subskill_prompt({ subskillName:
32
- "generate-messages", offset, limit })` calls so every tool result stays small
33
- enough for the streamed harness. Start with `offset: 0`; the MCP will apply a
34
- portable default limit when omitted. Keep calling with `offset: nextOffset`
35
- until `hasMore` is false.
36
- In Codex-hosted runs, this is a quality gate, not just provenance: if the
37
- model cannot retrieve the complete prompt or cannot follow the required
38
- gold-standard deliberation flow, stop at `message-review` and ask for
39
- `revise-messaging`. Do not mint the campaign to compensate for weak copy.
40
- Do not hand-write `message-validation.md` from `message-prep.md` or
41
- `message-candidate-drafts.md`. Those files are planning inputs only; the final
42
- message-validation artifact must come from the actual `generate-messages`
43
- prompt path (`get_message_prompt` / `generate-messages`).
44
-
45
- Orchestration requirements:
46
-
47
- - start once real leads exist: `brief.md`, `lead-review.md`, and
48
- `lead-sample.json` are enough to run generate-message
49
- - use only the find-leads message handoff rows as basis examples, e.g.
50
- "prospects 1, 5, and 6 are solid enough to think from"
51
- - if `lead-filter.md` is ready, use it; if it arrives after message drafting,
52
- reconcile before approval and rerun or revise messaging if the selected
53
- winner depended on rows that fail the final filter
54
- - never write `message-validation.md`, render a message review, render an
55
- approval packet, or ask the commit gate until 100% of the real
56
- `generate-messages` prompt has been read
57
- - before `message-review` can recommend `approve-message`, verify
58
- `message-validation.md` contains the full generate-messages shape:
59
- `Gold Standard Strategy Map`, `Proof Inventory`, `Token Fill Rules`,
60
- `Token Adherence Table`, `Angle Drafts`, `Kill / Combine Review`,
61
- `Finalists`, `Finalizer Pass`, `Gold-Standard Quality Gate`,
62
- `Skeptical Prospect Review`, `Winner Gate`, and a raw sendable
63
- `Selected Winner`. If any are missing, recommend `revise-messaging` and do
64
- not continue to approval or mint.
65
- - pass no `campaignId`
66
- - read only `brief.md`, `lead-review.md`, `lead-sample.json`, optional
67
- `lead-filter.md`, and the `gold-standard-*` references. If
68
- `message-prep.md` exists, use it as a
69
- planning cache instead of redoing the same element inventory from scratch. If
70
- `message-candidate-drafts.md` exists, use it as provisional draft input only
71
- after checking its basis rows against the completed `lead-filter.md`, when
72
- available.
73
- - generate 2-3 sample messages inline
74
- - start output with `Mode: DRY MODE (no DB mutation)`
75
- - treat the archived examples as the **quality bar**, not a paste source;
76
- write messages that could plausibly belong in the archive for this motion
77
- - exact-template preservation only applies when the archived winner is the
78
- same company as the brief
79
- - draft 3 internal candidates and run a Finalizer Pass that combines the
80
- best opener, proof sentence, bridge, and CTA across them into one winner
81
- - if all finalists use the same first substantive line, treat the opener
82
- test as failed; compare materially different opener jobs before selecting
83
- a winner, or route to `revise-message`
84
- - the Finalizer Pass must block adjacent repeated copy. Do not let a pain
85
- line and mechanism line reuse the same noun stack unless the second line
86
- adds plainly new buyer value. Merge, translate, bullet, or cut instead.
87
- - the selected winner subject/body must not contain semicolons.
88
- - the selected winner subject must not be generic category copy like
89
- `first LinkedIn campaign`, `quick question`, `outbound`, or `intro`. Prefer the
90
- original gold-standard A + B + C subject shape: a concrete row/company token
91
- plus a concrete workflow/object plus the offer/result, for example
92
- `{{company}} + LinkedIn outbound + Claude/Codex` or
93
- `{{source_post_topic}} + active leads + first message test`. Keep it short,
94
- buyer-native, and free of abstract category nouns.
95
- - A + B + C is a quality shape, not permission to stuff tokens into the
96
- subject. The subject must pique buyer interest and name a buyer-relevant
97
- problem, workflow, or useful artifact. Do not use sender names, founder names,
98
- "Austin's review", "founder call", "demo", "quick call", or similar
99
- sender-centered language in the subject. Do not use a row token in the subject
100
- unless the filled version is natural, lowercase/common-word where appropriate,
101
- and more interesting than the non-tokenized version.
102
- - the selected winner must not contain sender-intro fragments like
103
- `Usama here`, `derm here`, `board-cert derm here`, `[credential] here`,
104
- or `[role] at [company] here`.
105
- - the selected winner must not use generic signal tokens such as
106
- `{{recentSignal}}`, `{{recent_signal}}`, or `{{recent_signal_quote}}`.
107
- Row personalization must use concrete enriched-row fields such as
108
- `{{post_context}}`, `{{comment_summary}}`, `{{profile_summary}}`,
109
- `{{source_post_topic}}`, `{{headline}}`, or `{{row_proof_note}}`, and it
110
- must avoid source-citation phrases like `caught my eye`.
111
- - do not default to founder-to-founder, MD-to-MD, doctor-to-doctor,
112
- peer-call, compare-notes, or similar identity-call CTA framing unless
113
- the user explicitly selected that route or the brief makes it the
114
- approved offer. Even then, the CTA must name the useful return artifact,
115
- preview, teardown, or working session.
116
- - if the message is selling or introducing a product, the selected winner
117
- must make the product plain before asking for time, but the opener must
118
- not sound like homepage copy. A line shaped like `[Product] is a
119
- [category] platform -- feature, feature, feature` is blocked when it reads
120
- like product copy instead of a human note. For command-native products,
121
- describe the buyer job (`launch a LinkedIn campaign from Claude/Codex`)
122
- before category nouns like `platform` or `MCP`.
123
- - pass the Thomas revision filters before writing findings
124
- - immediately after `message-validation.md` is confirmed and reconciled with
125
- `lead-filter.md`, write `message-review.md` and render the slim chat version
126
- inline starting with `Status: message-review` as the first visible line. Do
127
- not put a markdown heading, preface, or summary before that status line.
128
- This is a customer checkpoint optimized for fast approve / revise judgment.
129
- - Chat-vs-file split (this is the rule the user pinned):
130
- - `message-review.md` (the file) keeps the full detail: `Status:
131
- message-review`, `Subject:`, `Tokenized template:`, `Sample prospect fill:` + `Token fill basis:`, `Rendered examples:`, `Good token fill:`,
132
- `Good omit:`, `Bad token fill:`, `Why bad:`, `Fallback if missing:`,
133
- `Token notes:`, `My take:`, `Suggested adjustment:`, `Question:
134
- approve-message or revise-messaging?`, `Recommendation:
135
- approve-message|revise-messaging`. The detailed gold-standard/rule audit
136
- stays in `message-validation.md`. Both files are unchanged in scope and
137
- are still required.
138
- - The chat rendering shows ONLY: `Status: message-review`, `Subject:`,
139
- `Tokenized template:`, `Sample prospect fill:` (with `Token fill basis:`),
140
- `My take:`, `Suggested adjustment:`, `Question: approve-message or
141
- revise-messaging?`, `Recommendation:`. Then `Open artifacts:` links to
142
- `message-review.md` and `message-validation.md` so the user can read the
143
- full token guidance, good/omit/bad examples, and fallbacks if they want
144
- to before deciding.
145
- - Do NOT inline `Rendered examples:`, `Good token fill:`, `Good omit:`,
146
- `Bad token fill:`, `Why bad:`, `Fallback if missing:`, or `Token notes:`
147
- in chat. Those exist in the file and are linked. Keeping them out of
148
- chat is what makes the gate easy to approve or revise on.
149
- - Never ask the message approval question until the slim chat review is fully
150
- visible. A summary like `Message review is ready` or `the draft avoids a
151
- generic pitch` is not enough. The user must see the actual subject, the
152
- actual tokenized message template, one filled sample prospect version with
153
- its token fill basis, your take, and the suggested adjustment before any
154
- `approve-message` / `revise-messaging` question appears. If you catch
155
- yourself wanting to offer `show me message` as a choice, stop: render the
156
- slim chat review first, then ask only `approve-message` or
157
- `revise-messaging`. Include `Open artifacts:` links to `message-review.md`
158
- and `message-validation.md` before the approval question.
159
- - `My take:` and `Suggested adjustment:` are mandatory customer-facing decision
160
- fields, not optional summary text. They must appear after `Token notes:` and
161
- before the question in every rendered message review. They must each be
162
- non-empty, specific to the current message, and useful for one last manual
163
- feedback pass. Never render a message review that jumps straight from token
164
- notes to `Question:` or `Recommendation:`.
165
- - `Tokenized template:` in `message-review.md` must be a tokenized template with supported
166
- enriched-row tokens such as `{{first_name}}`, `{{company}}`,
167
- `{{workflow_context}}`, `{{headline}}`, `{{source_post_topic}}`, or
168
- `{{row_proof_note}}`. It must not be a one-row-only raw sample. It must
169
- include at least one literal `{{...}}` token. Keep only tokens that future
170
- enriched rows can actually fill or safely omit. If you cannot produce a
171
- tokenized template from supported enriched-row fields, stop with
172
- `Recommendation: revise-messaging`.
173
- - `Sample prospect fill:` must render the same template for one named sample
174
- prospect from `lead-sample.json` or the selected row used in
175
- `message-validation.md`. It must show the complete subject and body the
176
- prospect would receive, plus a one-line `Token fill basis:` naming which row
177
- fields filled each token. This is the user-facing proof that the template
178
- works. Do not ask for approval if the user cannot compare the template to a
179
- concrete filled message.
180
- - `{{profile_signal}}` is never a supported customer-facing token. It is an
181
- internal enrichment label and must not appear in `Subject:`, `Tokenized
182
- template:`, `Sample prospect fill:`, rendered examples, `Token notes:`,
183
- `message-validation.md` selected copy, or `approval-packet.md`. If a
184
- profile-derived signal matters, translate it into a buyer-readable derived
185
- token with a clear fill rule, such as `{{workflow_context}}` or
186
- `{{row_proof_note}}`; otherwise omit the line.
187
- - `Rendered examples:` must include at least one `Good token fill:` rendered
188
- message where the row has a clean signal and one `Good omit:` rendered message
189
- where an optional token line is omitted instead of forced. Each example must
190
- be a complete rendered subject + body, not a bullet list of token names or a
191
- single bridge-line fragment. Do not use bracketed instructions, deferred row
192
- notes, or phrases like `approve the selected winner above`.
193
- - `Bad token fill:` must show at least one realistic wrong fill for the current
194
- campaign, followed by `Why bad:`. This teaches the user and the live campaign
195
- runtime what not to do. Examples: overclaiming signal intent (`you are
196
- evaluating Clay` when the only evidence is a post reaction), using forbidden
197
- phrasing (`you engaged with content...` when the approved voice is `raised
198
- your hand in a conversation around...`), forcing a weak optional token,
199
- lowercasing proper nouns, or making row signal sound like AI mail merge.
200
- - `Fallback if missing:` is required for every token in the template. Each
201
- fallback must say exactly what to do when the row lacks clean data: use a
202
- safe segment-level phrase, omit the sentence/line, or route to
203
- `revise-messaging` if omission would break the message. It must include a
204
- concrete example of the fallback behavior, such as `omit the noticed... line`,
205
- `use "operators"`, or `use "your outbound workflow"`. Never leave a token
206
- with "N/A", "unknown", empty braces, bracketed instructions, or a generic
207
- fill that sounds robotic or creepy.
208
- - rendered examples may only use token values that exist in `lead-sample.json`,
209
- `lead-review.md`, `lead-filter.md`, or the selected winner's documented
210
- supported-token plan. Do not invent example values to make the template look
211
- better. If a token is absent, weak, or not a supported enriched-row field,
212
- show the `Good omit:` version instead of filling it.
213
- - token casing must look intentional. Keep proper nouns, recipient names,
214
- company names, product names, acronyms, and the pronoun `I` capitalized in
215
- both the template examples and rendered output. Casual lowercase is allowed
216
- only for static common words when the chosen message voice uses it. Do not
217
- lowercase token values or invent transform syntax like `{{company_lower}}`,
218
- `{{first_name | lower}}`, or bracketed casing instructions.
219
- - Token usage must be intelligent, not decorative. If a tokenized opener reads
220
- like mail merge (`Reaching out because {{reporting_context}} sits in...`,
221
- `your {{role_context}} work`, `saw your {{topic}}`, or similar), rewrite the
222
- base line so the token either sounds like a natural noun phrase or is omitted.
223
- Prefer row-derived tokens that change the reply reason; otherwise use a clean
224
- segment-level line and document why the row token is omitted.
225
- - founder/sender names should not be used as the hook. Mention a founder name
226
- only when the buyer already has a reason to care about that person or when the
227
- user explicitly wants named-founder branding. In normal first-touch copy,
228
- frame the CTA around the useful return artifact (`map one reporting problem`,
229
- `review one dashboard`, `pressure-test one workflow`) instead of "call with
230
- Austin" or another founder-name CTA.
231
- - message-review hard-fail preflight: before rendering `Status: message-review`,
232
- scan the subject, tokenized template, good-fill example, good-omit example,
233
- `My take`, `Suggested adjustment`, and recommendation. If any of them contain
234
- `felt close enough to send`, `close enough to send`,
235
- `thought this was relevant enough`, `seemed close
236
- enough`, `{{profile_signal}}`, `profile signal`, `is why I thought this might
237
- be relevant`, `Austin's`, a sender/founder name in the subject, a one-row raw
238
- sample under `Tokenized template:`, rendered examples without full copy, a
239
- mail-merge-sounding tokenized opener, missing or empty `My take:`, missing or
240
- empty `Suggested adjustment:`, `My take:` that only says `looks good`,
241
- `strong`, or `approved`, `Suggested adjustment:` that only says `none`, or no
242
- literal `{{...}}` token in `Tokenized template:`, revise the message before
243
- showing it. Do not output
244
- `Recommendation: approve-message` when any hard-fail preflight item is
245
- present.
246
- - `My take` must be 1-3 short bullets or sentences, focused on what a founder
247
- needs to decide: approve as-is, revise once, make it shorter, make it more
248
- specific, change proof, change CTA, or preserve the current length because
249
- cutting would remove reply reason. If a shorter version is recommended,
250
- `Suggested adjustment` must say exactly what to remove or compress. Do not
251
- include a long `What works`, `Gold-standard/rule check`, or internal checklist
252
- in this checkpoint.
253
- - `Suggested adjustment` must give the founder a concrete final-edit option even
254
- when the recommendation is `approve-message`. Use a useful shape like
255
- `Approve as-is, or revise once to [specific edit] if you want [effect]`. It
256
- must invite the user to give manual feedback before minting, for example:
257
- `Reply revise-messaging with the exact line change, proof change, CTA change,
258
- or tone feedback you want before I build the approval packet.` Do not write
259
- `none`.
260
- - if the message is merely plausible, generic, template-shaped, or weaker than
261
- the loaded gold-standard examples, set `Recommendation: revise-messaging`.
262
- The next Codex UAT goal is to prove message quality before mint, so no
263
- approval packet or campaign mutation should happen just because the mechanics
264
- are working.
265
- - immediately after rendering `message-review.md`, use the structured question
266
- gate with exactly two choices, in this order: `approve-message`,
267
- `revise-messaging`. Stop after the question. Do not write `approval-packet.md`,
268
- `customer-roleplay.md`, or a commit-gate question until
269
- `message-review-decision.md` contains `approve-message`.
270
- - if the selected winner or PS uses named customer/logo proof, quantified proof,
271
- team credentials, or investor/accelerator proof that is not explicitly
272
- supported by `brief.md`, `lead-review.md`, `lead-sample.json`, or loaded
273
- reference material, the review must set `Recommendation: revise-messaging`.
274
- Do not approve speculative credibility claims.
275
- - if the selected winner or PS uses vague proof wrappers like `spoken publicly
276
- about`, `publicly talked about`, `trusted by`, `worked with`, `used by`, or
277
- bare customer-logo lists without naming a concrete buyer-relevant result,
278
- mechanism, or risk reducer, the review must set `Recommendation:
279
- revise-messaging`. Supported logos are not automatically body-worthy proof.
280
- Translate to a concrete proof line or omit the proof from the message.
281
- - if customer/logo proof is useful mainly for credibility, prefer a concise PS
282
- over interrupting the body. The PS must name what the customers used the
283
- product/service for, not just list logos. If the body already carries buyer
284
- pain, mechanism, and CTA clearly, adding concrete social proof in the PS is
285
- allowed when it lowers vendor risk.
286
- - the selected winner or PS must not use internal labels like `p.s. relevant
287
- proof:`, `p.s. useful proof:`, `p.s. proof:`, or `p.s. social proof:`.
288
- Rewrite as a natural aside before asking the user to approve.
289
- - if the user chooses `revise-messaging`, write `message-review-decision.md`
290
- with `revise-messaging`, preserve upstream artifacts, delete/regenerate
291
- only `message-validation.md`, `message-review.md`,
292
- `message-review-decision.md`, `customer-roleplay.md`, and
293
- `approval-packet.md`, then rerun this message step.
294
- - after `message-review-decision.md` contains `approve-message`, write
295
- `approval-packet.md` in customer-facing order and render the same approval
296
- packet inline before the commit gate. The approval packet and six-choice
297
- commit question must appear in the same customer-visible turn. Do not emit a
298
- standalone commit-gate question. Include a clear `## Message Review` section
299
- inside the approval packet before `## Approved Message Template`. It should
300
- summarize the customer checkpoint from `message-review.md` and preserve the
301
- same literal `My take:`, `Suggested adjustment:`, `Good token fill:`,
302
- `Good omit:`, `Bad token fill:`, `Why bad:`, `Fallback if missing:`,
303
- `Token fill basis:`, and `Recommendation:` labels.
304
- Include `Open artifacts:` links to `approval-packet.md`,
305
- `message-review.md`, `lead-review.md`, and `brief.md` before the commit-gate
306
- question.
307
- - if `message-validation.md` contains an extractable `Selected Winner`, use
308
- that exact winner as the `## Approved Message Template` in
309
- `approval-packet.md` and in the campaign brief passed to `create_campaign`.
310
- Only substitute concrete enriched-prospect-row `{{tokens}}`; do not rewrite
311
- the copy inline at approval time. Do not use abstract slot tokens like
312
- `{{hookLine}}`, `{{painLine}}`, `{{productLine}}`, `{{closeLine}}`, or
313
- `{{psLine}}`, and do not use `{{recent_signal_quote}}`; use fields that exist
314
- on the enriched prospect row instead.
315
- - the live body under `## Approved Message Template` must be sender-ready copy.
316
- Bracketed text in the body is allowed only when it is a fully-specified
317
- AI-native token (`[ALL_CAPS_NAME — Intent. DO: ... DON'T: ... FALLBACK:
318
- omit the line.]` — see
319
- `mcp/sellable/skills/create-campaign/references/ai-native-tokens.md` for the
320
- contract). Bracketed instruction placeholders that lack a contract are
321
- BLOCKED — including `[ROW BRIDGE ...]`, `[insert ...]`, `[generated ...]`,
322
- and any prose that defers per-row composition to a later step without
323
- giving the model the rules. Either upgrade the placeholder to a full
324
- AI-native token (Intent / DO / DON'T / FALLBACK), put the per-row rules in
325
- `## Token Fill Rules` with concrete enriched-row fields, or route to
326
- `revise-messaging`.
327
- - `approval-packet.md` and the live campaign brief passed to `create_campaign`
328
- must include `## Approved Message Template`, `## Token Fill Rules`, and
329
- `## Token Fill Examples`. `## Token Fill Examples` must copy the approved
330
- examples from `message-review.md` / `message-validation.md`, including
331
- `Good token fill:`, `Good omit:`, `Bad token fill:`, `Why bad:`,
332
- `Fallback if missing:`, and `Token fill basis:`. Do not call
333
- `create_campaign` if these sections are missing from the campaign brief.
334
- - `message-validation.md` must not call the winner a canonical template or
335
- hide per-row generation in bracketed body text. `## Selected Winner` must
336
- be a real sendable message that could be approved as-is.
337
- - block awkward bridge phrasing like `felt close enough to send this`,
338
- `thought this was relevant enough`, or `seemed close enough`. Replace with a
339
- crisp row-backed bridge using a buyer-readable row-derived token, such as
340
- `That {{workflow_context}} work is where teams usually need cleaner reporting
341
- ownership.` If the signal is weak or absent, omit the bridge line entirely.
342
- - if subject, tokenized template, or rendered examples do not meet the bar,
343
- `message-review.md` must recommend `revise-messaging`, not `approve-message`.
344
-
345
- Do not:
346
-
347
- - call `get_campaign`
348
- - call `get_rows`
349
- - call `update_cell`
350
- - call `update_campaign_brief`
351
- - fetch fresh web or LinkedIn research
352
- - mutate DB-backed campaign state
353
-
354
- `message-validation.md` must contain:
355
-
356
- - `Status`
357
- - `Mode`
358
- - `Template Used`
359
- - `Primary Example`
360
- - `Secondary Influence`
361
- - `Lead Sample Basis`
362
- - `Strongest Reply Reason`
363
- - `Pre-Draft Buyer-Role Analysis`
364
- - `Campaign Element Pool`
365
- - `Gold Standard Strategy Map`
366
- - `Current Campaign Translation`
367
- - `Element Scoring`
368
- - `Proof Inventory`
369
- - `Token Fill Rules`
370
- - `Token Adherence Table`
371
- - `Angle Drafts`
372
- - `Kill / Combine Review`
373
- - `Finalists`
374
- - `Candidate Messages`
375
- - `Finalizer Pass`
376
- - `Gold-Standard Quality Gate`
377
- - `Skeptical Prospect Review`
378
- - `Winner Gate`
379
- - `Selected Winner`
380
- - `Findings`
381
- - `Recommendation`
382
-
383
- `approval-packet.md` must contain:
384
-
385
- - campaign direction
386
- - lead source and sample
387
- - filters and rubrics
388
- - selected message(s)
389
- - `## Message Review` with literal `My take:`, `Suggested adjustment:`, and
390
- `Recommendation:` (`approve-message` or `revise-messaging`). If the message
391
- is only okay, say so plainly so the user can choose `revise-messaging` at the
392
- commit gate.
393
- - `## Approved Message Template`; when `message-validation.md` has a selected
394
- winner, this section must preserve that selected winner rather than a newly
395
- drafted approval-template variant
396
- - risks / caveats
397
- - next action: the six-choice commit gate
398
-
399
- `message-review.md` must contain:
400
-
401
- - `Status: message-review`
402
- - `Subject:` using the approved tokenized A + B + C subject shape
403
- - `Tokenized template:` followed by the tokenized approved message template
404
- - `Sample prospect fill:` with one concrete sample prospect's complete rendered
405
- subject + body and a `Token fill basis:` line
406
- - `Rendered examples:`
407
- - `Good token fill:` with a complete concrete rendered subject + body
408
- - `Good omit:` with a complete concrete rendered subject + body where optional
409
- row signal is omitted cleanly
410
- - `Bad token fill:` with a realistic wrong fill for the current campaign
411
- - `Why bad:` explaining the exact overclaim, unsupported token, weak signal,
412
- casing mistake, or mail-merge phrasing that makes the bad fill unacceptable
413
- - `Fallback if missing:` with one concrete fallback or omit example for every
414
- token in the template
415
- - `Token notes:` naming which tokens are safe to fill, which optional token line
416
- should be omitted when absent, and whether the message uses standard sentence
417
- case or casual lowercase static words
418
- - `My take:`
419
- - `Suggested adjustment:` with a concrete final-edit option and explicit
420
- invitation to give manual feedback before approval
421
- - `Question: approve-message or revise-messaging?`
422
- - `Recommendation:` exactly `approve-message` or `revise-messaging`
423
- - The two-choice structured question gate immediately after the rendered review
424
-
425
- `message-review-decision.md` must contain exactly one selected route:
426
- `approve-message` or `revise-messaging`. It does not authorize DB mutation.
427
-
428
- If token sourcing is weak, revise token fill rules or route back to
429
- `revise-filter` or `revise-message` instead of guessing.
430
-
431
- </step_contracts>
432
-
433
- <resume_rules>
434
-
435
- Phase 84 validation resume:
436
-
437
- - No `brief-v1.md` / `brief.md` present -> run `create-campaign-brief`
438
- - `brief-v1.md` present but no `brief.md` -> copy `brief-v1.md` to `brief.md`,
439
- then run `find leads`
440
- - Only `brief.md` present -> compatibility resume: run `find leads`
441
- - `lead-review.md` + `lead-sample.json` present, but no `lead-filter.md` and
442
- no `message-validation.md` -> run `filter leads` and `generate message`
443
- from the same find-leads basis; do not ask the user between them when
444
- lead-review is confirmed
445
- - `lead-review.md` + `lead-sample.json` present, but no `lead-filter.md` -> run
446
- `filter leads`
447
- - `lead-review.md` + `lead-sample.json` present, but no
448
- `message-validation.md` -> run `generate message`
449
- - `lead-filter.md` + `message-validation.md` present -> reconcile message
450
- basis rows against the final filter, then write/render `message-review.md`
451
- and ask the two-choice message review gate
452
- - `message-validation.md` present but no `message-review-decision.md` -> write
453
- `message-review.md`, render it inline starting with `Status:
454
- message-review`, then ask `approve-message` vs `revise-messaging`
455
- - `message-review-decision.md` contains `revise-messaging` -> preserve
456
- upstream artifacts; delete only `message-validation.md`,
457
- `message-review.md`, `message-review-decision.md`,
458
- `customer-roleplay.md`, and `approval-packet.md`; rerun `generate message`
459
- - `message-review-decision.md` contains `approve-message` but no
460
- `approval-packet.md` -> write `approval-packet.md`, render that packet inline
461
- starting with `Status: approval-packet`, then ask the six-choice commit gate
462
- in the same turn
463
- - `message-validation.md` + `message-review-decision.md` +
464
- `approval-packet.md` present -> validation complete,
465
- render `approval-packet.md` inline starting with `Status: approval-packet`,
466
- then ask the six-choice commit gate in the same turn
467
- - `lead-review.md` without `lead-sample.json`, or vice versa -> stop with a
468
- contract violation
469
- - `lead-filter.md` without upstream lead artifacts -> stop with a contract violation
470
-
471
- Phase 85 commit-gate + atomic-mint resume:
472
-
473
- - All validation artifacts present and `message-review-decision.md` contains
474
- `approve-message` but no `approval-packet.md` yet -> write the approval
475
- packet, render it inline, then show the commit gate in the same turn (see
476
- `<commit_gate>` below)
477
- - `approval-packet.md` present but no `commit-gate-decision.md` -> show the
478
- approval packet inline and then the commit gate in the same turn; an existing
479
- positive `customer-roleplay.md` is advisory only
480
- - `commit-gate-decision.md` is a revision choice or `abort` -> route without
481
- DB mutation
482
- - `commit-gate-decision.md` contains `approve` and no `CampaignOffer` minted
483
- yet -> run atomic mint
484
- - `CampaignOffer` exists and `currentStep = "auto-execute-leads"` -> resume
485
- via `create_campaign({ campaignId })` to re-fetch state + `watchUrl`,
486
- re-surface the watch link using `references/watch-link-handoff.md`, and
487
- continue into the autonomous tail at Step 13
488
-
489
- Phase 85 autonomous-tail resume (Plan 85-02):
490
-
491
- - `CampaignOffer.currentStep === "auto-execute-leads"` -> run Step 13
492
- (import) then advance to `validate-sample`
493
- - `CampaignOffer.currentStep === "validate-sample"` -> run Step 14 sample
494
- validation loop. Read the persisted `revisionRound` counter and do NOT
495
- reset it to 0 on resume
496
- - `CampaignOffer.currentStep === "auto-execute-messaging"` -> run Step 15
497
- messaging scale-up
498
- - `CampaignOffer.currentStep === "awaiting-user-greenlight"` -> Step 16:
499
- re-surface `watchUrl` + `handoff.orientation` and STOP. Do not call
500
- `start_campaign` on resume. Wait for UI start or a Claude greenlight
501
- turn
502
- - `CampaignOffer.currentStep === "running"` -> campaign is live. Surface
503
- a "campaign is live" confirmation + `watchUrl`. Do not re-start
504
-
505
- On every tail resume, re-surface the `watchUrl` and the current step's
506
- orientation string (v1 create-campaign watch-mode pattern). Load the
507
- relevant reference for the active step:
508
-
509
- - Step 14 -> `references/sample-validation-loop.md`
510
- - any escalation path -> `references/escalation-ladder.md`
511
- - Step 16 -> `references/final-handoff-contract.md`
512
-
513
- </resume_rules>
514
-
515
- <commit_gate>
516
-
517
- After Phase 84 artifacts are complete, show the user a commit gate before any
518
- DB mutation. Load `references/approval-gate-framing.md` for the full
519
- framing — especially the approval packet ordering and per-choice draft
520
- directory effects.
521
-
522
- Preconditions (block the gate when missing):
523
-
524
- - `brief-v1.md` should exist for from-scratch runs
525
- - `brief.md` must exist
526
- - `lead-review.md` must exist
527
- - `lead-sample.json` must exist
528
- - `lead-filter.md` must exist
529
- - `message-validation.md` must exist
530
- - `message-review.md` must exist
531
- - `message-review-decision.md` must exist and contain `approve-message`
532
- - `approval-packet.md` must be written before asking for a commit decision
533
- - `rubric.json` is optional but strongly preferred
534
-
535
- Anchor validation (block `approve` when empty):
536
-
537
- - `brief.md` has a non-empty `## Message Thesis`
538
- - `lead-review.md` status is `confirmed`
539
- - `lead-sample.json` parses and contains at least one row
540
- - `lead-filter.md` has `Decision`, `Who We'll Keep`, `Who We'll Exclude`,
541
- `Pass Rate`, `Recommendation`, and `Implementation Details`
542
- - `message-validation.md` has `Status: confirmed`, `Mode: DRY MODE (no DB
543
- mutation)`, a `Selected Winner`, and a non-empty `Token Adherence Table`
544
- - `message-review.md` has `Status: message-review`, `Subject:`,
545
- `Tokenized template:` with at least one literal `{{...}}` token,
546
- `Sample prospect fill:` with a complete rendered subject + body and
547
- `Token fill basis:`, `Rendered examples:`, `Good token fill:` with a complete
548
- rendered subject + body, `Good omit:` with a complete rendered subject + body,
549
- `Bad token fill:`, `Why bad:`, `Fallback if missing:`, `Token notes:`,
550
- `My take:`, `Suggested adjustment:`, `Question:
551
- approve-message or revise-messaging?`, and
552
- `Recommendation: approve-message`
553
- - `message-review-decision.md` is exactly `approve-message`
554
-
555
- Present the approval packet in user-facing order: brief, lead sample, lead
556
- filter, message validation. Do not dump raw JSON or opaque validation
557
- anchors at the user.
558
-
559
- When rendering the approval packet inline, start it with exactly
560
- `Status: approval-packet`. Write the same content to `approval-packet.md`
561
- before showing the gate. The same assistant turn must then use the structured
562
- question gate with the six commit choices below. In Claude Code, call
563
- `AskUserQuestion`; in Codex, call `request_user_input` when available. A
564
- commit-gate question without the inline `Status: approval-packet` packet is
565
- invalid UX. If a
566
- customer roleplay is performed, write `customer-roleplay.md` before the final
567
- choice. A roleplay result that says "looks good", "approve", or equivalent is
568
- only critique; it does not satisfy the gate. Write `commit-gate-decision.md`
569
- only after the user/operator makes one explicit choice.
570
-
571
- Use the structured question gate with exactly these 6 choices, in this order:
572
-
573
- ```text
574
- approve
575
- revise-brief
576
- revise-leads
577
- revise-rubric
578
- revise-messaging
579
- abort
580
- ```
581
-
582
- Do not collapse, reorder, or add choices. Do not auto-select.
583
-
584
- If the host UI caps options per question, preserve the same six routes with a
585
- two-question gate: Q1 = `approve`, `revise`, `abort`; Q2 =
586
- `revise-brief`, `revise-leads`, `revise-rubric`, `revise-messaging`. Do not
587
- tell the customer this is because of schema/tool limits. In the split form,
588
- `revise` is only the parent choice for the second question, not a new route.
589
-
590
- </commit_gate>
591
-
592
- <sender_resolution>
593
-
594
- Before atomic mint, the skill needs a concrete sender identity. Resolve
595
- it in this order — stop at the first step that yields a confirmed
596
- sender, and NEVER invent / guess / brute-force LinkedIn URLs:
597
-
598
- 1. **If the fixture/caller provides `senderIdentity.senderId`**, use it
599
- directly. Call `get_sender({ senderId })` to confirm it exists in
600
- the active workspace, then call `enrich_sender({ senderId })` ONCE
601
- to populate `companySnapshot` + `proofDigest`. Do not call
602
- `list_senders`, do not guess alternate URLs.
603
- 2. **Else if the fixture/caller provides a confirmed
604
- `senderIdentity.linkedinUrl`**, call `enrich_sender({ linkedinUrl
605
- })` ONCE. If it errors, DO NOT retry with a variation of the URL.
606
- Fall through to step 3.
607
- 3. **Else if the caller provides `senderIdentity.name` +
608
- `companyDomain` (or workspace context), do ONE `WebSearch` pass**
609
- for the authoritative LinkedIn profile URL ("`{name}` `{company}`
610
- linkedin"). Use ONLY a URL that appears verbatim in a web search
611
- result. Never synthesize a URL from the name. If WebSearch returns
612
- no clean match, escalate to step 4.
613
- 4. **Else escalate.** Print the available sender context and ask the
614
- user to resolve (senderId or confirmed linkedinUrl). Do not proceed
615
- with atomic mint.
616
-
617
- Hard rules:
618
-
619
- - At most ONE `enrich_sender` retry per run, only when the first
620
- response was a transient (5xx) error. Do not retry on 400/404 by
621
- changing the URL.
622
- - At most ONE `WebSearch` pass per run for sender resolution.
623
- - NEVER call `enrich_sender` on a lead's LinkedIn URL. `enrich_sender`
624
- is for the single sender entity only. Lead enrichment happens via
625
- the Enrich Prospect column in the workflow table.
626
- - If the fixture's `mustMatchWorkspaceSender` flag is true and the
627
- resolved sender isn't in the active workspace, ABORT the run with an
628
- explicit mismatch error.
629
-
630
- </sender_resolution>
631
-
632
- <atomic_mint>
633
-
634
- Only run this sequence when the user picks `approve`. Load
635
- `references/watch-link-handoff.md` before starting; it owns the watch-link
636
- surfacing protocol and the partial-mint recovery rules.
637
-
638
- Exact sequence:
639
-
640
- 1. Read `brief.md` and `rubric.json` from the draft directory.
641
- 2. Validate anchors one more time. An empty anchor blocks the approve path.
642
- The brief must include an `## Approved Message Template` section with
643
- the exact live message template and at least one supported `{{token}}`
644
- such as `{{first_name}}`. Rendered examples are not enough. The
645
- Generate Message column switches to template mode only when
646
- `campaignBrief.content` contains `{{...}}`; without this marker it will
647
- use full-generation mode and may rewrite the approved message.
648
- The live template body must contain only sender-ready copy plus supported
649
- tokens. Two token shapes are allowed:
650
- - `{{snake_case}}` field-substitution tokens for atomic values that drop
651
- directly into a fixed slot (greeting, subject, company name).
652
- - `[ALL_CAPS_NAME — Intent. DO: ... DON'T: ... FALLBACK: omit the line.]`
653
- AI-native tokens for personalization sentences. The bracket carries
654
- the contract inline; the model writes a sentence (or omits the line)
655
- per the inline rules. Full spec:
656
- `mcp/sellable/skills/create-campaign/references/ai-native-tokens.md`.
657
- No other bracketed placeholders are allowed — bracketed prose without an
658
- explicit Intent / DO / DON'T / FALLBACK contract (e.g., `[ROW BRIDGE]`,
659
- `[insert ...]`, `[generated ...]`) is BLOCKED because it leaves the
660
- per-row generation under-specified.
661
- Include `## Token Fill Rules` for every token in the template. Field
662
- tokens may be row fields such as `{{first_name}}`, `{{company}}`,
663
- `{{role}}`, `{{headline}}`, `{{profile_summary}}`, `{{post_context}}`,
664
- `{{comment_summary}}`, `{{source_post_topic}}`, or `{{row_proof_note}}`
665
- when those exact fields are supported by the row/enrichment data. AI-native
666
- tokens are listed in the rules table by name and type with a pointer back
667
- to the inline contract (the rules themselves live inside the bracket — do
668
- not duplicate them in the table). Do not document static sender identity,
669
- product name, proof points, casing style, `{{recent_signal_quote}}`, or
670
- abstract slot tokens such as `{{hookLine}}`, `{{painLine}}`,
671
- `{{productLine}}`, `{{closeLine}}`, or `{{psLine}}` — those are abstract
672
- slots without a contract; convert them to AI-native bracket tokens with
673
- real Intent / DO / DON'T / FALLBACK clauses, or cut them.
674
- Include `## Token Fill Examples` copied from the approved message review and
675
- validation artifacts. It must preserve `Good token fill:`, `Good omit:`,
676
- `Bad token fill:`, `Why bad:`, `Fallback if missing:`, and
677
- `Token fill basis:` so the minted campaign brief teaches future row
678
- generation how to fill tokens, what fills are blocked, and what to do when
679
- row data is missing. If the brief does not contain `## Token Fill Rules` and
680
- `## Token Fill Examples`, do not call `create_campaign`; route back to
681
- message review or approval packet generation.
682
- 3. Call `bootstrap_create_campaign({ flowVersion: "v2" })`. Respect
683
- `safeToProceed`; on blocking errors, surface and stop.
684
- 4. Call
685
- `create_campaign({ campaignBrief, currentStep: "auto-execute-leads" })`.
686
- The `campaignBrief` argument must preserve the approved message
687
- template section verbatim, including the `{{tokens}}`.
688
- If the brief has no supported `{{token}}`, do not call
689
- `create_campaign`. Fail the approve path and ask for a corrected
690
- template, unless the user explicitly approved freeform AI-generated
691
- messages and the tool call sets `messageGenerationMode:
692
- "ai-generated"`.
693
- Include `senderIds` when a workspace sender is available. If the
694
- customer-named sender is not configured in the current UAT workspace,
695
- use an available workspace sender for the rehearsal and state that
696
- mismatch in the final runtime notes. Do not silently mint without a
697
- sender because sequence selection depends on sender tier/settings.
698
- Capture `{ campaignId, watchUrl }` from the response.
699
- - If the call errors, surface the error and stop. No rollback needed; no
700
- campaign exists.
701
- - If the response is missing `watchUrl`, treat it as a recoverable
702
- failure. Stop before `save_rubrics`. Do not print a watch link.
703
- Surface the contract violation explicitly — a missing `watchUrl` is a
704
- plumbing bug, not a silent skip.
705
- 5. Call `save_rubrics({ campaignOfferId: campaignId, rubric })` using the
706
- payload derived from `rubric.json` (or from `lead-filter.md` if
707
- `rubric.json` is absent).
708
- - If this call fails after `create_campaign` succeeded, either rollback
709
- the `CampaignOffer` row, or mark an explicit recoverable failure state
710
- so the user can retry. Never leave a silent partial campaign. Do NOT
711
- print the watch link during this recovery window.
712
- 6. Flip the brief status in the draft to `committed`. Move the draft
713
- directory from
714
- `.sellable/create-campaign-v2/drafts/{workspace-slug}/{campaign-slug}/`
715
- to
716
- `.sellable/create-campaign-v2/drafts/{workspace-slug}/.committed/{campaign-slug}/`.
717
- 7. Surface the `watchUrl` using the exact block defined in
718
- `references/watch-link-handoff.md`. Only print after both
719
- `create_campaign` AND `save_rubrics` have succeeded.
720
- 8. Hand off to the autonomous tail (Plan 85-02). Do not run Step 13 lead
721
- import here.
722
-
723
- If the approved lead source exists only as a provider lane in
724
- `lead-review.md` (for example a Sales Navigator filter/search URL) and no
725
- live `searchId` or `sourceLeadListId` is present, recreate the approved
726
- source by calling the matching provider search tool first, then import from
727
- the returned id. This is replaying the approved lane, not inventing a new
728
- source. Do not skip import/messages on that basis.
729
-
730
- Do NOT call `import_leads` during atomic mint. Full lead sourcing and import
731
- happen in Plan 85-02 Step 13 after mint.
732
-
733
- </atomic_mint>
734
-
735
- <revision_routing>
736
-
737
- When the user picks a revision choice, do not mint a campaign. Do not print
738
- a watch link. Route back to the relevant upstream step and reset the draft
739
- directory per `references/approval-gate-framing.md`:
740
-
741
- - `revise-brief` -> back to Phase 83 or Phase 84 depending on what changed.
742
- Preserve `brief.md`; delete all downstream artifacts.
743
- - `revise-leads` -> back to Phase 84 `find leads`. Preserve `brief.md`;
744
- delete lead and downstream artifacts.
745
- - `revise-rubric` -> back to Phase 84 `filter leads`. Preserve `brief.md`,
746
- `lead-review.md`, `lead-sample.json`; delete `lead-filter.md`,
747
- `message-validation.md`, and `rubric.json`.
748
- - `revise-messaging` -> back to Phase 84 `generate message`. Preserve
749
- upstream artifacts; delete only `message-validation.md`,
750
- `message-review.md`, `message-review-decision.md`,
751
- `customer-roleplay.md`, and `approval-packet.md`.
752
- - `abort` -> move the draft directory to
753
- `.sellable/create-campaign-v2/drafts/{workspace-slug}/.aborted/{campaign-slug}-{timestamp}/`.
754
- No DB rows touched.
755
-
756
- Revisions must produce zero DB rows.
757
-
758
- </revision_routing>
759
-
760
- <boundaries>
761
-
762
- Phase 84 (pre-commit-gate) boundary:
763
-
764
- Do not create live campaign state during validation. Specifically, do not:
765
-
766
- - mint a campaign
767
- - import leads
768
- - persist selected lead lists
769
- - attach downstream assets
770
- - save remote rubric state
771
- - move the user into a live execution step
772
-
773
- Phase 85 (commit-gate and beyond) boundary:
774
-
775
- - The commit gate is the only place in this skill where DB mutation is
776
- authorized. Do not call `create_campaign`, `save_rubrics`,
777
- `update_campaign`, or any other mutating tool before the user picks
778
- `approve`.
779
- - Revision choices and `abort` must never call a mutating tool.
780
- - `import_leads` is not part of atomic mint. Defer it to Plan 85-02 Step 13.
781
- - Never print the `watchUrl` until both `create_campaign` and `save_rubrics`
782
- have succeeded.
783
- - Never fabricate or reconstruct the `watchUrl` — capture it from the
784
- `create_campaign` response.
785
- - Once a campaign has been minted, every provider search and every
786
- provider-import call from this skill MUST include the minted
787
- `campaignOfferId`. This is universal — Prospeo, Sales Nav, Apollo,
788
- Signal Discovery, and any future provider. The only place a
789
- campaignless search is correct is the pre-mint Phase 84 `find leads`
790
- discovery sample. Post-mint expansion, scale-up imports, alternate
791
- lane explorations, account-based reruns, signal-monitor lanes, and
792
- operator-driven follow-up searches all persist to the campaign and
793
- must carry `campaignOfferId`. A search without `campaignOfferId`
794
- after mint orphans the search from the campaign UI's Contact
795
- Search panel and the campaign's `Searches` history, even when the
796
- search produced rows that later get imported. If the search
797
- helper enforces a `confirmed: true` gate, pass that with
798
- `campaignOfferId` rather than dropping it.
799
-
800
- </boundaries>
801
-
802
- <autonomous_tail>
803
-
804
- The review-batch tail runs AFTER the atomic mint succeeds. It is driven by
805
- `core/auto-execute.yaml` and progresses through four steps — Step 13
806
- (import review batch), Step 14 (validate-sample), Step 15
807
- (auto-execute-messaging for the review batch), Step 16
808
- (awaiting-user-greenlight) — before stopping and waiting for a user
809
- greenlight.
810
-
811
- Cost boundary: the commit gate approves creating a campaign and producing a
812
- real review batch. It does NOT approve spending enrichment/message credits on
813
- hundreds of leads. Two separate scales apply post-mint and they MUST be kept
814
- distinct in your head:
815
-
816
- - **Import scale** (how many rows land in the campaign table). Defaults to
817
- `import.importLimit` (25). The user MAY explicitly raise this at or before
818
- mint with phrasing like "import all of them", "pull the full search", "all
819
- ~2,000 from this lane", or a numeric target. When the user has stated the
820
- full-scale import intent, pass that count as `targetLeadCount` so the table
821
- carries the full pool. Importing rows is cheap (no enrichment runs yet);
822
- storing them up front lets the user kick remaining enrichment manually
823
- without re-importing later.
824
- - **Cascade scale** (how many rows get enriched/scored/messaged before user
825
- approval). ALWAYS stays at the configured review batch (`sample.sampleSize`
826
- or `import.importLimit` — default 25) until the user explicitly approves the
827
- sample. This is the cost gate. Even when the user said "import all", you
828
- queue Enrich Prospect cells for only the first review-batch rows. The
829
- remaining rows sit `pending` in the table for the user to kick manually
830
- after they've eyeballed the sample.
831
-
832
- A pre-stated full-import intent is NOT a pre-approval to cascade everything.
833
- Re-confirm before queueing enrichment beyond the review batch.
834
-
835
- If the source lane contains hundreds of leads and the user has not stated a
836
- full-import intent, leave the rest untouched in the source — do not import
837
- beyond `importLimit` until the user asks to expand.
838
-