@sellable/mcp 0.1.207 → 0.1.209

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -264,37 +264,25 @@ Parallel execution contract:
264
264
 
265
265
  - Source scout names come from `agents/registry.json` and are exposed at runtime
266
266
  through `get_source_scout_registry`. Add new scouts there first; installer,
267
- Codex config, Claude agent files, and prompts should consume the registry.
268
- - Post-find-leads scout names also come from `agents/registry.json` and are
269
- exposed through `get_post_find_leads_scout_registry`. After
270
- `confirm_lead_list` imports a non-empty review batch and rows are proven, ask
271
- the filter-choice question immediately. Do not load this registry or deep
272
- filter/message prompts before that question. Once the user answers, launch the
273
- message-generation scout from the same campaign/table basis. If the user
274
- chooses filters, also launch the filter-leads scout, show Filter Rules, save
275
- rubrics, then ask for filter approval. After approval, move to Filter Leads
276
- and show `Filters saved + waiting for message approval` while the message
267
+ Codex config, Claude agent files, and prompts can consume the registry for
268
+ explicit source-comparison/debug runs. Normal create-campaign source work
269
+ stays inline in the parent thread with the active provider MCP tools.
270
+ - `get_post_find_leads_scout_registry` returns only the Message Drafting worker
271
+ for normal create-campaign runs. After `confirm_lead_list` imports a non-empty
272
+ review batch and rows are proven, ask the filter-choice question immediately.
273
+ Do not load this registry or deep filter/message prompts before that question.
274
+ Once the user answers, launch Message Drafting from the same campaign/table
275
+ basis. If the user chooses filters, keep filter/rubric work in the parent
276
+ thread: show Filter Rules, load `references/filter-leads.md`, save rubrics,
277
+ then ask for filter approval. After approval, move to Filter Leads and show
278
+ `Filters saved + waiting for message approval` while the message
277
279
  recommendation is reviewed. If filters are skipped, move to Messages/message
278
280
  review. Enrichment/filtering and Generate Message cells wait for message
279
281
  approval.
280
- - Claude host: after Find Buyers Plan approval, use the installed
281
- `source-scout-linkedin-engagement`, `source-scout-sales-nav`, and
282
- `source-scout-prospeo-contact` Task/Agent subagents as the normal source
283
- path. Launch the approved single lane, or launch all credible lanes in one
284
- assistant message when comparison/fallback is needed.
285
- - Codex host: after Find Buyers Plan approval, use the named custom scout for
286
- the approved source lane when subagents are available:
287
- `source-scout-linkedin-engagement`, `source-scout-sales-nav`, or
288
- `source-scout-prospeo-contact`. Use a generic background agent when the named
289
- scout is unavailable and the host can spawn one; parent-thread provider MCP
290
- calls are the explicit fallback, not the default path.
291
- - For prospect setup, Codex/Claude should launch Prospect Filters
292
- (`post-find-leads-filter-scout`) and Message Drafting
293
- (`post-find-leads-message-scout`) in the same turn/message when those
294
- subagents are available in the current session.
295
- If not, run them sequentially with MCP tools/assets, do not surface install
296
- status to the customer, and do not claim parallel work.
297
- - If neither backend is available, run sequentially with the same output schema.
282
+ - Source scouts and Prospect Filters are not normal create-campaign background
283
+ agents. Only Message Drafting may spawn as a background/custom agent on the
284
+ normal path; if it cannot be launched and the user does not approve inline
285
+ fallback, stop instead of silently drafting in the parent thread.
298
286
 
299
287
  Config path resolution (in order):
300
288
 
@@ -12,17 +12,22 @@ Required inputs:
12
12
 
13
13
  - `campaignId`
14
14
  - campaign revision or `campaignUpdatedAt`
15
- - campaign brief content
15
+ - campaign brief summary from the parent, then the current campaign brief loaded
16
+ through Sellable tools
16
17
  - selected source decision and provider/list state
17
18
  - `selectedLeadListId`
18
19
  - `workflowTableId`
19
- - initial campaign-table execution slice rows, including row ids/hash when available
20
+ - compact initial campaign-table execution-slice basis: copied source row count,
21
+ review-batch row count, and review-batch basis/hash. Do not require the
22
+ parent to paste every row id when scoped row tools are available.
20
23
  - filter choice
21
24
 
22
25
  Required first steps:
23
26
 
24
27
  1. Verify the campaign/source/table ids from the parent thread match the live
25
- campaign context.
28
+ campaign context. Read current campaign/table state and the review batch
29
+ through scoped Sellable tools such as `get_campaign`, `get_campaign_context`,
30
+ and `get_rows_minimal({ tableId: workflowTableId })`.
26
31
  2. Load the filter-leads reference before designing rubrics:
27
32
  `get_subskill_asset({ subskillName: "create-campaign-v2", assetPath: "references/filter-leads.md" })`.
28
33
  3. Treat campaign state and the campaign-table execution slice as the input of record.
@@ -33,6 +38,9 @@ Owned outputs:
33
38
  - Durable campaign rubrics via `save_rubrics({ campaignOfferId, leadScoringRubrics })`
34
39
  when the filter is confirmed and production-shaped rubrics are safe to write.
35
40
  `save_rubrics` is the durable writer.
41
+ - If `save_rubrics` is unavailable in the branch, return production-shaped
42
+ `leadScoringRubrics` and mark `save_rubrics` as not yet persisted so the
43
+ parent can save them before filter approval.
36
44
  - concise filter/rubric summary returned to the parent thread
37
45
 
38
46
  Do not write or modify local markdown/json artifacts. Durable output is only
@@ -21,12 +21,14 @@ Use the live campaign inputs supplied by the parent thread:
21
21
 
22
22
  - `campaignId`
23
23
  - campaign revision or `campaignUpdatedAt`
24
- - `campaignBrief` / campaign brief content model
24
+ - campaign brief summary from the parent, then the current `campaignBrief` /
25
+ campaign brief content loaded through Sellable tools
25
26
  - selected source decision and provider state
26
27
  - `selectedLeadListId` or selected source list context
27
28
  - `workflowTableId`
28
- - initial campaign-table execution slice rows from that selected list, including row IDs
29
- and a sample row hash when available
29
+ - compact initial campaign-table execution-slice basis: copied source row count,
30
+ review-batch row count, and review-batch basis/hash. Do not require the
31
+ parent to paste a long review-batch row-id list.
30
32
  - filter basis at branch start: `pending`, `use-filters`, or `skip-filters`
31
33
  - any already-saved fit/rubric result summaries supplied by the parent
32
34
 
@@ -38,22 +40,34 @@ read stale local markdown files to reconstruct campaign state.
38
40
  All live reads must come from scoped MCP/product tools by campaign and
39
41
  workspace, such as `get_campaign`, `get_campaign_context`, and
40
42
  `get_rows_minimal({ tableId: workflowTableId })`, or from equivalent parent
41
- thread payloads. Reject the task as `blocked` if the campaign id, workspace,
42
- `selectedLeadListId`, `workflowTableId`, or execution-slice row ids do not match
43
- the branch input.
43
+ thread payloads. Load the current campaign brief and the current review-batch
44
+ rows from those tools before drafting. Reject the task as `blocked` if the
45
+ campaign id, workspace, `selectedLeadListId`, `workflowTableId`, review-batch
46
+ row count, or review-batch basis/hash does not match the branch input.
44
47
 
45
48
  ## Required First Steps
46
49
 
47
- 1. Load the full normal-path message prompt:
50
+ 1. Load the current campaign brief and compact campaign/table state:
51
+ campaign name, `campaignId`, `selectedLeadListId`, `workflowTableId`,
52
+ copied source row count, review-batch row count, review-batch basis/hash,
53
+ selected source summary, filter choice, and parent-supplied brief summary.
54
+ Then read the current campaign/table state through scoped Sellable tools.
55
+ Do not ask the parent to paste row data or a long row-id list.
56
+ 2. Load the full normal-path message prompt:
48
57
 
49
58
  `get_subskill_prompt({ subskillName: "generate-messages" })`
50
59
 
51
- 2. Use that prompt as the drafting contract. Do not use any alternate prompt,
52
- examples-only shortcut, or create-campaign safety/checklist instructions as a
53
- substitute for the full message prompt.
54
- 3. Draft only from the campaign brief, selected source context, and initial
55
- campaign-table execution slice rows supplied by the parent.
56
- 4. Keep the work provisional until the user chooses `Use Template` in Messages.
60
+ 3. Load the reference assets required by that prompt's Reference Asset Loading
61
+ pack, including validation/QA criteria. If a required validation asset cannot
62
+ load, return `blocked` or `retry-needed` instead of drafting from memory.
63
+ 4. Use that prompt as the drafting and QA contract. Do not use any alternate
64
+ prompt, examples-only shortcut, or create-campaign safety/checklist
65
+ instructions as a substitute for the full message prompt.
66
+ 5. Draft only from the current campaign brief, selected source context, and
67
+ review-batch rows loaded through scoped Sellable tools.
68
+ 6. Run the normal message validation/QA rules before final return. The final
69
+ recommendation must include the QA pass/fail receipt, not just draft copy.
70
+ 7. Keep the work provisional until the user chooses `Use Template` in Messages.
57
71
 
58
72
  ## Owned Output
59
73
 
@@ -64,10 +78,12 @@ Return the following to the parent thread:
64
78
  - one rendered good-fill sample for a plausible passing campaign-table row
65
79
  - one omit/fallback sample when the row signal is not safe
66
80
  - pass/fail notes against the generate-messages quality gates
81
+ - `qaReceipt` / validation notes showing token safety, proof safety, fit
82
+ uncertainty, bad-fill avoidance, and "would I take this call?" judgment
67
83
  - message-draft runtime status: `ready`, `blocked`, `retry-needed`, or `stale`
68
84
  - basis token containing campaign revision/updatedAt, brief hash,
69
- `selectedLeadListId`, `workflowTableId`, execution-slice row ids/hash, filter
70
- choice, and rubric/filter basis when present
85
+ `selectedLeadListId`, `workflowTableId`, compact execution-slice row
86
+ count/hash, filter choice, and rubric/filter basis when present
71
87
  - output timestamp/hash and any retry/error detail
72
88
 
73
89
  Do not write local markdown/json artifacts in normal live campaign runs. Return
@@ -98,7 +114,7 @@ row data became available after this branch started.
98
114
 
99
115
  Treat later filter/enrichment data as optional rewrite context. If campaign id,
100
116
  brief hash, selected source, `selectedLeadListId`, `workflowTableId`, and
101
- execution-slice row ids/hash still match, keep the initial recommendation usable
117
+ execution-slice row count/hash still match, keep the initial recommendation usable
102
118
  and report `status: ready` with `basisStatus: "usable_initial"` or
103
119
  `"enriched_rewrite_available"`. The parent thread may offer the user a choice
104
120
  to keep the initial draft or rewrite with enriched/filter data, but the rewrite
@@ -107,21 +123,23 @@ must be explicit user opt-in.
107
123
  Retry or regenerate without asking only when the initial recommendation is
108
124
  missing, failed, structurally invalid, unsafe, or mismatched on campaign id,
109
125
  brief hash, selected source, `selectedLeadListId`, `workflowTableId`, or
110
- execution-slice rows. Filter/rubric/enrichment basis drift alone is not a stale
126
+ execution-slice row count/hash. Filter/rubric/enrichment basis drift alone is not a stale
111
127
  blocker.
112
128
 
113
- ## User Revision Feedback
129
+ ## User Revision Feedback And QA
114
130
 
115
- If the parent sends user feedback about the template before `approve-message`,
116
- treat it as a Message Drafting revision, not a campaign write. Use the current
117
- `messageDraftRecommendation`, basis token/hash, campaign/table basis, and the
118
- latest user feedback as inputs. Load or reuse the full
119
- `generate-messages` contract, then return a revised recommendation with:
131
+ If the parent sends user feedback, a QA request, or a rewrite request about the
132
+ template before `approve-message`, treat it as Message Drafting work, not a
133
+ campaign write. Use the current `messageDraftRecommendation`, basis token/hash,
134
+ campaign/table basis, and latest user feedback as inputs. Load or reuse the full
135
+ `generate-messages` contract and validation assets, then return a revised or
136
+ QA-only recommendation with:
120
137
 
121
138
  - revised proposed template
122
139
  - what changed and why
123
140
  - token fill rule/fallback changes, if any
124
141
  - one rendered good-fill sample
142
+ - `qaReceipt` with pass/fail notes against the loaded validation assets
125
143
  - pass/fail notes against the same quality gates
126
144
  - updated output timestamp/hash and basis token
127
145
 
@@ -159,10 +159,10 @@
159
159
  "target": "filter-leads",
160
160
  "inputs": [
161
161
  "campaignId",
162
- "campaignBrief",
162
+ "campaignBrief summary plus branch-loaded current campaignBrief",
163
163
  "source decision and selectedLeadList/source state",
164
164
  "workflowTableId",
165
- "imported review-batch rows"
165
+ "compact review-batch count/hash or branch-loaded review rows"
166
166
  ],
167
167
  "producesArtifacts": [],
168
168
  "optionalProducesArtifacts": [],
@@ -187,6 +187,9 @@
187
187
  "tools": [
188
188
  "mcp__sellable__get_subskill_prompt",
189
189
  "mcp__sellable__get_subskill_asset",
190
+ "mcp__sellable__get_campaign",
191
+ "mcp__sellable__get_campaign_context",
192
+ "mcp__sellable__get_rows_minimal",
190
193
  "mcp__sellable__save_rubrics"
191
194
  ]
192
195
  }
@@ -86,7 +86,7 @@ export interface SourceScoutRegistryResponse {
86
86
  }
87
87
  export interface PostFindLeadsScoutRegistryResponse {
88
88
  version: number;
89
- trigger: "find_leads_source_approved";
89
+ trigger: "prospect_setup_after_confirm_lead_list";
90
90
  requiredInputs?: string[];
91
91
  scouts: Array<{
92
92
  id: string;
@@ -147,7 +147,7 @@ export const promptToolDefinitions = [
147
147
  },
148
148
  {
149
149
  name: "get_source_scout_registry",
150
- description: "Return the canonical Sellable source-scout agent names and host filenames. Use this before the source-finding step so Codex and Claude can launch the same named scouts from one registry when the current host exposes them.",
150
+ description: "Return the canonical Sellable source-scout agent names and host filenames for explicit source-comparison/debug runs only. Normal create-campaign source work stays inline in the parent thread.",
151
151
  inputSchema: {
152
152
  type: "object",
153
153
  properties: {},
@@ -157,7 +157,7 @@ export const promptToolDefinitions = [
157
157
  },
158
158
  {
159
159
  name: "get_post_find_leads_scout_registry",
160
- description: "Return canonical Sellable prospect-setup worker names for Prospect Filters and Message Drafting after Start Import copies confirmed source rows.",
160
+ description: "Return the canonical Sellable Prospect Filters and Message Drafting worker names after Start Import copies confirmed source rows.",
161
161
  inputSchema: {
162
162
  type: "object",
163
163
  properties: {},
@@ -257,9 +257,9 @@ export function getSourceScoutRegistry() {
257
257
  legacy: agent.legacy,
258
258
  })),
259
259
  usage: {
260
- codex: "Source finding is agent-first after Find Buyers Plan approval. Launch the approved returned `name` as a Codex custom agent whenever the host exposes it; YOLO/autonomous mode counts as campaign-scoped permission. Launch multiple returned `name` values in one assistant turn only when comparing lanes, a prior lane failed, or the active flow marks the first lane borderline.",
261
- claude: "Source finding is agent-first after Find Buyers Plan approval. Invoke the approved returned `name` with Task/Agent when the current Claude session lists it. Invoke multiple Task/Agent subagents with subagent_type equal to returned `name` values only when comparison, fallback, or borderline-source conditions apply.",
262
- parentThreadRule: "Named source scouts are the normal path, not optional acceleration, after the Find Buyers Plan approval. If the approved named scout is absent or blocked, do not customer-surface install status; either spawn a generic gpt-5.5 high background agent with the same source-lane basis or continue inline only when the user explicitly approves parent-thread fallback. Default fallback order: Signal Discovery / LinkedIn engagement, then Sales Nav recent activity, then broader Sales Nav title search, then Prospeo account/contact search. Do not preload every provider prompt before spawning agents; each scout loads only the provider prompt for its lane.",
260
+ codex: "Source finding is parent-thread and sequential by default in create-campaign. Do not launch source scouts in the normal campaign flow. Returned `name` values are for explicit source-comparison/debug runs only.",
261
+ claude: "Source finding is parent-thread and sequential by default in create-campaign. Do not invoke source-scout Task/Agent workers in the normal campaign flow. Returned `name` values are for explicit source-comparison/debug runs only.",
262
+ parentThreadRule: "Normal create-campaign source work stays inline in the parent thread with MCP provider tools. Default fallback order: Signal Discovery / LinkedIn engagement, then Sales Nav recent activity, then broader Sales Nav title search, then Prospeo account/contact search. Do not preload every provider prompt; load only the provider prompt for the active sequential source.",
263
263
  },
264
264
  };
265
265
  }
@@ -268,14 +268,14 @@ export function getPostFindLeadsScoutRegistry() {
268
268
  const scouts = registry.agents.filter((agent) => String(agent.kind || "") === "post-find-leads-scout");
269
269
  return {
270
270
  version: registry.version,
271
- trigger: "find_leads_source_approved",
271
+ trigger: "prospect_setup_after_confirm_lead_list",
272
272
  requiredInputs: [
273
273
  "campaignId",
274
274
  "campaign revision or campaignUpdatedAt",
275
275
  "campaignBrief",
276
276
  "source decision and selectedLeadList/source state",
277
277
  "workflowTableId",
278
- "initial campaign-table execution slice rows from selectedLeadList with row ids/hash",
278
+ "compact review-batch row count and basis/hash; branch reads rows through scoped tools",
279
279
  "filter choice and filter/rubric basis when present",
280
280
  ],
281
281
  scouts: scouts.map((agent) => ({
@@ -308,7 +308,11 @@ export function getPostFindLeadsScoutRegistry() {
308
308
  requiredState: ["leadScoringRubrics", "messageDraftRecommendation"],
309
309
  noFilterRequiredState: ["messageDraftRecommendation"],
310
310
  skipFilterRule: "leadScoringRubrics may be absent only when the user explicitly skips filters with enableICPFilters=false; messageDraftRecommendation is still required before template review.",
311
- show: ["readable_filters_with_reasons", "sample_message_for_approval"],
311
+ show: [
312
+ "readable_filters_with_reasons",
313
+ "sample_message_for_approval",
314
+ "message_qa_receipt",
315
+ ],
312
316
  nextStage: "message-review",
313
317
  },
314
318
  messageDraftBranchContract: {
@@ -328,15 +332,16 @@ export function getPostFindLeadsScoutRegistry() {
328
332
  "basisToken",
329
333
  "basis.selectedLeadListId",
330
334
  "basis.workflowTableId",
331
- "basis.reviewSampleRowHash or basis.reviewSampleRowIds",
335
+ "basis.reviewSampleRowCount",
336
+ "basis.reviewSampleRowHash",
332
337
  ],
333
- promptRequired: 'get_subskill_prompt({ subskillName: "generate-messages" }); the full generate-messages prompt and its Reference Asset Loading pack are required inside the Message Drafting branch for normal message drafting. If required assets cannot be loaded, return blocked/retry-needed instead of drafting from prompt memory.',
338
+ promptRequired: 'Load current campaign brief/table state through scoped tools, then get_subskill_prompt({ subskillName: "generate-messages" }); the full generate-messages prompt and its Reference Asset Loading pack, including message validation/QA assets requested by that prompt, are required inside the Message Drafting branch for normal drafting, QA, and user-requested rewrites. If required assets cannot be loaded, return blocked/retry-needed instead of drafting from prompt memory.',
334
339
  basisFields: [
335
340
  "campaign revision or updatedAt",
336
341
  "brief hash",
337
342
  "selectedLeadListId",
338
343
  "workflowTableId",
339
- "initial campaign-table execution slice row ids/hash",
344
+ "compact initial campaign-table execution slice row count/hash",
340
345
  "filter choice",
341
346
  "filter/rubric basis when present",
342
347
  ],
@@ -349,6 +354,7 @@ export function getPostFindLeadsScoutRegistry() {
349
354
  "status",
350
355
  "basisStatus",
351
356
  "basisToken",
357
+ "qaReceipt or validationNotes",
352
358
  "outputAt",
353
359
  "outputHash",
354
360
  "error or retry detail",
@@ -356,9 +362,9 @@ export function getPostFindLeadsScoutRegistry() {
356
362
  reusePolicy: "The first completed Message Drafting recommendation remains the default review candidate. Later Prospect Filters, Filter Leads, enrichment, or rubric completion may make an enriched rewrite available, but does not automatically retry or replace the initial draft unless campaign/brief/source/list/table/review-sample identity mismatches or the initial output failed. User copy feedback before approve-message is an explicit Message Drafting revision and must be routed back through the message branch with the current recommendation and basis.",
357
363
  },
358
364
  usage: {
359
- codex: "After confirm_lead_list copies source rows and the initial campaign-table execution slice exists, ask the filter-choice question immediately. Do not spawn returned prospect-setup worker names before that question. Once the user answers, spawn Message Drafting from the same campaign/table basis whenever Codex agent-launch policy is satisfied. Treat YOLO/autonomous mode as campaign-scoped permission to use Sellable background agents for pre-launch work. If the user has not enabled YOLO and has not explicitly asked for background agents/subagents/parallel agents/delegation/message bg agent in this campaign, ask once for permission before loading generate-messages in the parent. If permission is granted and the named custom agent is unavailable, spawn a generic gpt-5.5 xhigh background agent with the same campaign/table basis. If the user chooses filters, also spawn Prospect Filters when allowed, move to Filter Rules, save rubrics, ask for filter approval, then keep the browser on Filter Leads and show `Filters saved + waiting for message approval` while the message recommendation is reviewed. If filters are skipped, move to Messages/message review.",
360
- claude: "After confirm_lead_list copies source rows and the initial campaign-table execution slice exists, ask the filter-choice question immediately. Do not invoke returned prospect-setup Task/Agent names before that question. Once the user answers, invoke Message Drafting from the same campaign/table basis. If the user chooses filters, also invoke Prospect Filters, move to Filter Rules, save rubrics, ask for filter approval, then keep the browser on Filter Leads and show `Filters saved + waiting for message approval` while the message recommendation is reviewed. If filters are skipped, move to Messages/message review.",
361
- parentThreadRule: 'Message drafting is agent-first, not optional acceleration. If post-find-leads-message-scout is available, run that compatibility agent as background Message Drafting after the filter-choice answer. In Codex, YOLO/autonomous mode counts as campaign-scoped permission to use Sellable background agents for pre-launch work; if the user has not enabled YOLO and has not explicitly asked for background agents/subagents/parallel agents/delegation/message bg agent in this campaign, ask once for that permission before loading generate-messages in the parent. If permission is granted and the named custom agent is unavailable, spawn a generic gpt-5.5 xhigh background agent with the same campaign/table basis. Do not silently fall back to parent-thread message drafting. The parent may run the message branch inline only if the user explicitly says to continue without a background agent; when doing so, state that message generation is running inline. Local markdown/json files are not normal-path inputs. The filter-choice question is the first post-import user gate; do not load prospect-setup registries or filter references before it. Message drafting starts after the filter-choice answer, must load get_subskill_prompt({ subskillName: "generate-messages" }) and its Reference Asset Loading pack inside the branch or explicitly approved inline fallback, must read live campaign table state through scoped MCP/product tools, and must reject mismatched selectedLeadListId/workflowTableId/campaign/workspace input. Do not use any alternate or examples-only message prompt. User copy feedback before approve-message must be routed back to Message Drafting with the current recommendation, basis token, campaign/table basis, and latest user text; the parent must not rewrite the template or call update_campaign_brief before approve-message. On the filter path, keep the browser on Filter Rules after save_rubrics so the user can approve the saved criteria; only then move to Filter Leads, show `Filters saved + waiting for message approval`, and wait there for message approval. Enrichment, filtering, Generate Message cells, sender setup, sequence attach, and launch wait for template approval on the Use Template path. On the skip path, move to Messages/message review and wait for message approval before enrichment or Settings. Do not render message review from checklist or shortcut instructions; message review requires a messageDraftRecommendation whose basis proves the generate-messages prompt ran for the current campaign/table execution slice. Do not automatically rerun Message Drafting after filters/enrichment finish; show the initial draft by default and offer an enriched rewrite only with explicit user opt-in.',
365
+ codex: "After confirm_lead_list copies source rows and the initial campaign-table execution slice exists, ask the filter-choice question immediately. Do not spawn anything before that question. If filters are chosen, spawn Prospect Filters and Message Drafting from the same campaign/table basis whenever Codex agent-launch policy is satisfied, then the parent waits for Prospect Filters first, verifies save_rubrics succeeded, asks filter approval, and only then joins Message Drafting for template review. If filters are skipped, spawn only Message Drafting. Treat YOLO/autonomous mode as campaign-scoped permission for these post-import workers. If the user has not enabled YOLO and has not explicitly asked for background agents/subagents/parallel agents/delegation/message bg agent in this campaign, ask once before loading long filter/message prompts in the parent. If permission is granted and the named Message Drafting custom agent is unavailable, spawn a generic gpt-5.5 xhigh Message Drafting background agent with the same campaign/table basis.",
366
+ claude: "After confirm_lead_list copies source rows and the initial campaign-table execution slice exists, ask the filter-choice question immediately. Do not invoke any Task/Agent before that question. If filters are chosen, invoke Prospect Filters and Message Drafting from the same campaign/table basis; parent waits for Prospect Filters first, verifies save_rubrics succeeded, asks filter approval, then joins Message Drafting. If filters are skipped, invoke only Message Drafting and move to Messages/message review.",
367
+ parentThreadRule: 'Post-import background work is limited to Prospect Filters and Message Drafting; source scouts stay out of the normal create-campaign path. If filters are chosen and the workers are available, launch post-find-leads-filter-scout and post-find-leads-message-scout after the filter-choice answer. The parent thread remains the orchestrator: wait for Prospect Filters first, require save_rubrics success or persist the returned production rubrics, keep the browser on Filter Rules, ask filter approval, then move to Filter Leads and join/wait for Message Drafting. Do not move into message review, queue cells, or call update_campaign_brief until saved filters are approved and messageDraftRecommendation is ready. If filters are skipped, launch only Message Drafting. In Codex, YOLO/autonomous mode counts as campaign-scoped permission for these post-import workers; if the user has not enabled YOLO and has not explicitly asked for background agents/subagents/parallel agents/delegation/message bg agent in this campaign, ask once for permission before loading long filter/message prompts in the parent. If Message Drafting is allowed but the named worker is unavailable, use a generic gpt-5.5 xhigh Message Drafting background agent. Do not silently fall back to parent-thread message drafting. The Message Drafting handoff must be compact: campaign identity/name, brief summary, source summary, selectedLeadListId, workflowTableId, copied source row count, review-batch row count/hash, and filter choice. Do not paste full reviewBatchRowIds, row data, or local debug artifacts into the message spawn prompt; the message branch must load the current campaign brief, campaign/table state, review rows, full generate-messages prompt, and validation/QA assets through scoped tools. Local markdown/json files are not normal-path inputs. The filter-choice question is the first post-import user gate; do not load this registry or filter references before it. Message drafting starts after the filter-choice answer, must load get_subskill_prompt({ subskillName: "generate-messages" }) and its Reference Asset Loading plus message validation/QA assets inside the branch or explicitly approved inline fallback, must read live campaign table state through scoped MCP/product tools, and must reject mismatched selectedLeadListId/workflowTableId/campaign/workspace input. Do not use any alternate or examples-only message prompt. User copy feedback, message QA, or rewrite requests before approve-message must be routed back to Message Drafting with the current recommendation, basis token, campaign/table basis, and latest user text; the parent must not rewrite or QA the template from memory and must not call update_campaign_brief before approve-message. On the filter path, the parent thread keeps the browser on Filter Rules after save_rubrics so the user can approve the saved criteria; only then move to Filter Leads, show `Filters saved + waiting for message approval`, and wait there for message approval. Enrichment, filtering, Generate Message cells, sender setup, sequence attach, and launch wait for template approval on the Use Template path. On the skip path, move to Messages/message review and wait for message approval before enrichment or Settings. Do not render message review from checklist or shortcut instructions; message review requires a messageDraftRecommendation whose basis proves the generate-messages prompt and QA assets ran for the current campaign/table execution slice. Do not automatically rerun Message Drafting after filters/enrichment finish; show the initial draft by default and offer an enriched rewrite only with explicit user opt-in.',
362
368
  },
363
369
  };
364
370
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sellable/mcp",
3
- "version": "0.1.207",
3
+ "version": "0.1.209",
4
4
  "type": "module",
5
5
  "description": "Sellable MCP server for Claude Code and Codex campaign workflows",
6
6
  "main": "dist/index.js",
@@ -248,34 +248,12 @@ summary should be a compact `## Source Recommendation` block with:
248
248
  - fallback: switch to active LinkedIn profiles if the first sample is too noisy
249
249
  or has too few prospects
250
250
 
251
- When the user has not supplied a source and multiple source angles are viable,
252
- scout those angles as independent branches when the host can actually do it:
253
- LinkedIn posts / people reacting or commenting (internal `signal-discovery`
254
- provider prompt), Sales Nav / title + company filters, and Prospeo Contact /
255
- domains, hiring filters, or broad verified-contact expansion when relevant. In
256
- Codex, source discovery is agent-first after the Find Buyers Plan approval. For
257
- the approved single source lane, explicitly spawn that returned named custom
258
- scout; for example, choosing LinkedIn posts should spawn
259
- `source-scout-linkedin-engagement` even when no comparison is needed. When
260
- multiple source angles are credible, explicitly spawn the named custom scouts
261
- `source-scout-linkedin-engagement`, `source-scout-sales-nav`, and
262
- `source-scout-prospeo-contact` for the credible lanes only when the current host
263
- exposes those names; Codex does not infer subagent fan-out from generic wording.
264
- YOLO/autonomous mode counts as campaign-scoped permission to use Sellable source
265
- background agents. In Claude Code, invoke the generated `source-scout-*`
266
- Task/Agent subagent for the approved lane, or all credible lanes in one
267
- assistant message when comparison/fallback is needed, only when the current
268
- session lists those names. The installer writes them from the same canonical
269
- Sellable agent registry with explicit Sellable MCP tool allowlists.
270
- The create-campaign-v2 subskill calls `get_source_scout_registry` before
271
- dispatch so the current registry, not this copy, is the runtime source of truth.
272
- If the named source scout is unavailable, use a generic `gpt-5.5` high
273
- background agent when the host can spawn generic agents. Continue source
274
- discovery in the parent thread only when agent launch is impossible or the user
275
- explicitly says to continue inline. Do not claim a scout ran unless one did, and
276
- do not surface install status to the customer. In chat, call the downstream copy
277
- stage `message generation`; message validation is only an internal approval
278
- proof.
251
+ Source discovery stays inline in the parent thread for normal create-campaign
252
+ runs. Use the approved provider prompt and MCP tools sequentially; do not spawn
253
+ source-scout background agents unless the user explicitly asks for a source
254
+ comparison/debug run. Do not claim a scout ran unless one did, and do not surface
255
+ install status to the customer. In chat, call the downstream copy stage
256
+ `message generation`; message validation/QA is owned by Message Drafting.
279
257
 
280
258
  For campaign-attached Signal Discovery sampling, promote/select the exact posts
281
259
  with `select_promising_posts` before `fetch_post_engagers` so the user can see
@@ -288,20 +266,27 @@ math should import the smallest set that covers a target. The watch guide should
288
266
  say that we are checking people from these posts to confirm the right people are
289
267
  actually engaging and the source is viable.
290
268
 
291
- After confirmed source rows exist in the campaign table, use the same registry pattern for
292
-
293
- prospect setup work, but do not load that registry or any deep filter/message prompt
269
+ After confirmed source rows exist in the campaign table, do not load the
270
+ message registry or any deep filter/message prompt
294
271
  before the filter-choice question. After `confirm_lead_list`, ask add filters
295
- vs skip filters immediately. Once the user answers, launch Message Drafting
296
- from the same campaign/table basis. If the user chooses filters, also launch the
297
- Prospect Filters worker, move to Filter Rules, save rubrics, then ask for filter
298
- approval. After approval, move to Filter Leads and show `Filters saved + waiting
299
- for message approval` while the message recommendation is reviewed. If the user
300
- skips filters, move to Messages/message review. Enrichment/filtering and
301
- Generate Message cells wait for message approval. AI Generated is an explicit
302
- opt-out from the template path. If the
303
- prospect setup agents are absent, the main thread still orchestrates the same
304
- branches from compact MCP context.
272
+ vs skip filters immediately. Once the user answers, launch only Message Drafting
273
+ from the same campaign/table basis. If the user chooses filters, the parent
274
+ thread moves to Filter Rules, loads the filter reference, saves rubrics, then
275
+ asks for filter approval while Message Drafting runs. After approval, move to
276
+ Filter Leads and show `Filters saved + waiting for message approval` while the
277
+ message recommendation is reviewed. If the user skips filters, move to
278
+ Messages/message review. Enrichment/filtering and Generate Message cells wait
279
+ for message approval. AI Generated is an explicit opt-out from the template
280
+ path.
281
+
282
+ The Message Drafting handoff must stay compact. Include campaign identity,
283
+ campaign name, `selectedLeadListId`, `workflowTableId`, copied source row count,
284
+ review-batch row count, review-batch basis/hash, filter choice, source summary,
285
+ and a concise campaign brief summary. Do not paste the full review-batch row ID
286
+ list or row data into the spawn prompt. Message Drafting must load the current
287
+ campaign brief, campaign/table state, and review-batch rows through Sellable
288
+ tools, then load the normal message validation/QA rules and run a QA pass before
289
+ returning its concise review-ready recommendation.
305
290
 
306
291
  Use rendered Markdown for user review surfaces, not fenced code blocks. Keep
307
292
  lines short, use indexed section labels and bullets, and translate internal
@@ -432,26 +417,23 @@ Treat host capabilities as concrete functions, not prose conventions:
432
417
  - `load_subprompt_asset`: call
433
418
  `mcp__sellable__get_subskill_asset({ subskillName, assetPath, offset?, limit? })`
434
419
  and continue chunks until `hasMore` is false.
435
- - `load_source_scout_registry`: call
436
- `mcp__sellable__get_source_scout_registry({})` before any scout dispatch.
420
+ - `load_source_scout_registry`: explicit source-comparison/debug runs only; do
421
+ not call it in the normal create-campaign source path.
437
422
  - `load_post_find_leads_scout_registry`: call
438
423
  `mcp__sellable__get_post_find_leads_scout_registry({})` after source
439
- import and before dispatching Prospect Filters/Message Drafting.
440
- - `launch_source_scout`: Claude Code uses `Task` with `subagent_type` equal to
441
- the registry `name` only when the session lists those agents; Codex uses
442
- named custom agents such as
443
- `source-scout-linkedin-engagement`, `source-scout-sales-nav`, and
444
- `source-scout-prospeo-contact` when subagents are available.
445
- - `launch_post_find_leads_scout`: Claude Code uses `Task` with `subagent_type`
446
- equal to the returned prospect-setup registry `name` only when the session
447
- lists those agents; Codex uses the returned compatibility agents such as
448
- `post-find-leads-filter-scout` and `post-find-leads-message-scout` when
449
- subagents are available.
424
+ import and before dispatching Prospect Filters / Message Drafting.
425
+ - `launch_prospect_filters`: Claude Code uses `Task` with `subagent_type`
426
+ `post-find-leads-filter-scout` when filters are chosen; Codex uses the
427
+ returned compatibility agent. Prospect Filters must save or return production
428
+ rubrics before message review can proceed.
429
+ - `launch_message_drafting`: Claude Code uses `Task` with `subagent_type`
430
+ `post-find-leads-message-scout` when listed; Codex uses the returned
431
+ compatibility agent or a generic `gpt-5.5` / `xhigh` Message Drafting agent.
450
432
 
451
433
  If a required interactive question function or MCP loader is missing, stop and
452
- explain the Sellable install/reload problem. Named scout agents are optional
453
- acceleration: if they are absent, do not surface install status to the customer;
454
- fall back to product-native MCP orchestration instead of local scripts.
434
+ explain the Sellable install/reload problem. Source work uses product-native MCP
435
+ orchestration in the parent; the only normal post-import background branches are
436
+ Prospect Filters and Message Drafting.
455
437
 
456
438
  Never narrate local draft housekeeping to the user. If you create directories,
457
439
  save drafts, write artifacts, or persist intermediate state, translate it into
@@ -767,13 +749,20 @@ updates.
767
749
  copies of this file; packaged Claude Code and Codex runs must use the MCP
768
750
  asset loader so they share the same config.
769
751
  3. Follow that prompt and workflow config exactly.
770
- 4. For message generation, keep the parent thread as a lean orchestrator and
752
+ 4. For filter and message setup, keep the parent thread as a lean orchestrator.
753
+ When filters are chosen, use `post-find-leads-filter-scout` for Prospect
754
+ Filters and `post-find-leads-message-scout` for Message Drafting. The parent
755
+ waits for Prospect Filters first, verifies `save_rubrics` succeeded or saves
756
+ returned production rubrics itself, asks filter approval, and only then joins
757
+ Message Drafting for template review. When filters are skipped, launch only
758
+ Message Drafting.
759
+ 5. For message generation, keep the parent thread as a lean orchestrator and
771
760
  use the `post-find-leads-message-scout` compatibility agent for Message
772
761
  Drafting whenever the host exposes it
773
762
  and the current host policy allows agent launch. The worker must load
774
763
  `mcp__sellable__get_subskill_prompt({ subskillName: "generate-messages" })`.
775
764
  In Codex, YOLO/autonomous mode counts as campaign-scoped permission to use
776
- Sellable background agents for pre-launch work. If the user has not enabled
765
+ this single Message Drafting background agent. If the user has not enabled
777
766
  YOLO and has not explicitly asked for background agents, subagents, parallel
778
767
  agents, delegation, or a message/bg agent in this campaign, ask once for
779
768
  permission before loading the long message prompt in the parent. If
@@ -783,20 +772,28 @@ updates.
783
772
  silently fall back to parent-thread message drafting; parent fallback is
784
773
  allowed only when the user explicitly says to continue without a background
785
774
  agent.
786
- Do not use any alternate or examples-only message prompt. Message review requires Message Drafting output:
775
+ Do not use any alternate or examples-only message prompt. Message review and
776
+ message QA require Message Drafting output:
787
777
  do not draft from a checklist, local markdown artifact, or parent-thread
788
778
  intuition. Use campaign state, campaign brief content, selected source state, and
789
779
  initial campaign-table execution slice rows as the source of truth; do not read stale local
790
780
  markdown such as `message-validation.md`, inspect the database directly, or
791
- synthesize local validation artifacts from general knowledge.
792
- 5. Create the campaign shell early with the v1 brief so the user can open the
781
+ synthesize local validation artifacts from general knowledge. The handoff to
782
+ Message Drafting should pass compact basis only, not a long row-id list; the
783
+ branch loads current brief/table rows and validation assets itself.
784
+ 6. Create the campaign shell early with the v1 brief so the user can open the
793
785
  watch link and see useful setup state immediately. Materialize the approved
794
786
  source list, copy confirmed rows into the campaign, and internally process the
795
- first campaign-table execution slice after the source is attached to the campaign; do not
796
- queue workflow cells, attach a sequence, or start until saved filters and the
787
+ first campaign-table execution slice after the source is attached to the
788
+ campaign; do not load prospect-setup registries/prompts before asking add
789
+ filters vs skip filters. Once the user answers, launch Prospect Filters plus
790
+ Message Drafting when filters are chosen, or only Message Drafting when
791
+ filters are skipped. Do not queue workflow cells, attach a
792
+ sequence, or start until saved filters and the
797
793
  message template/token rules are approved. When filters are chosen, immediately
798
794
  call `mcp__sellable__update_campaign({ campaignId, enableICPFilters: true, currentStep: "create-icp-rubric", watchNarration })`
799
- so the watched app moves to Filter Rules while rubrics are drafted/saved.
795
+ so the watched app moves to Filter Rules while Prospect Filters drafts/saves
796
+ rubrics.
800
797
  After rubrics save, keep Filter Rules visible for approval; after approval,
801
798
  move to Filter Leads and show `Filters saved + waiting for message approval`
802
799
  until the template is approved.
@@ -205,14 +205,11 @@ source-plan question or call `search_signals`, `search_sales_nav`,
205
205
  `search_prospeo`, `fetch_post_engagers`, or provider subagents until approved.
206
206
  Only then persist `leadSourceType`, `leadSourceProvider`, and provider `currentStep`.
207
207
 
208
- Call `get_source_scout_registry` before source scouting. After Find Buyers Plan
209
- approval, source scouting is agent-first. Launch the approved returned source
210
- scout; YOLO/autonomous mode counts as permission. For LinkedIn posts, launch
211
- `source-scout-linkedin-engagement` even as a single lane; use Sales Nav/Prospeo
212
- scouts for those lanes. Use multiple scouts only for comparison/fallback. If a
213
- named scout is unavailable, use a generic `gpt-5.5` high background agent.
214
- Parent source MCP calls require impossible agent launch or approved inline
215
- fallback. Each scout loads its own provider prompt.
208
+ Source work stays inline in the parent thread in normal create-campaign runs.
209
+ Do not launch source-scout background agents. Run the approved provider probes
210
+ with MCP tools, loading only the provider prompt for the active sequential
211
+ source. Use source scouts only if the user explicitly asks for a source
212
+ comparison/debug run.
216
213
  Use a 10% planning floor after cleanup. If LinkedIn engagement falls below it,
217
214
  move to Sales Nav, then Prospeo. If Prospeo also falls below 10%, tighten the
218
215
  ICP/source direction instead of inventing another provider.
@@ -268,44 +265,61 @@ After `confirm_lead_list` copies a non-empty source into the campaign and
268
265
  records the compact review batch, ask the filter-choice question immediately.
269
266
  Do not call
270
267
  `get_post_find_leads_scout_registry`, load filter/message subskill prompts, or
271
- spawn Prospect Filters / Message Drafting before that question. The only
268
+ spawn Message Drafting before that question. The only
272
269
  customer-facing work before the question should be a short setup summary and the choice:
273
270
  add filters, skip filters, or revise source. Do not include another watch link
274
271
  in this summary; the existing app view updates through campaign state.
275
272
 
276
- If the user chooses filters, load the prospect-setup registry/reference needed
277
- for filtering and start Prospect Filters. Also start Message Drafting from the
278
- same campaign/table basis once filter-choice is known. If filters are skipped,
279
- move the watched app to Messages and run Message Drafting there.
273
+ Once filter choice is known, use the post-import workers for the heavy prompt
274
+ work. If the user chooses filters, launch both Prospect Filters
275
+ (`post-find-leads-filter-scout`) and Message Drafting
276
+ (`post-find-leads-message-scout`) from the same campaign/table basis. If filters
277
+ are skipped, launch only Message Drafting and move the watched app to Messages.
280
278
  Debug markdown/json artifacts are optional only.
281
279
 
280
+ The parent thread is the orchestrator, not the writer of long filter/message
281
+ logic. On the filters path, after spawning both workers, the parent must wait for
282
+ Prospect Filters first. Do not join Message Drafting, render message review, or
283
+ move to Filter Leads until Prospect Filters has either successfully called
284
+ `save_rubrics` or returned production-shaped rubrics that the parent then saves
285
+ with `save_rubrics`. If neither happened, block and report the filter-save
286
+ failure. Only after saved filters are verified should the parent ask for filter
287
+ approval, then wait for Message Drafting if it is still running.
288
+
289
+ Keep the Message Drafting handoff compact: campaign identity/name,
290
+ `selectedLeadListId`, `workflowTableId`, copied-row count, review-batch
291
+ count/hash, filter choice, source summary, and a concise brief summary. Do not
292
+ paste the full row ID list or row data. The branch loads the current brief,
293
+ table state, and review rows through Sellable tools before drafting.
294
+
282
295
  When the user chooses filters, immediately call
283
296
  `update_campaign({ campaignId, enableICPFilters: true, currentStep: "create-icp-rubric", watchNarration })`
284
- before rubric thinking or branch work. After `save_rubrics`, keep Filter Rules
285
- visible so the user can approve saved criteria. Only after approval should
297
+ before branch work so the app is on Filter Rules. After `save_rubrics`, keep
298
+ Filter Rules visible so the user can approve saved criteria. Only after approval should
286
299
  `update_campaign` move to `apply-icp-rubric` / Filter Leads and show
287
300
  `Filters saved + waiting for message approval` until the template is approved.
288
301
  Do not queue enrichment/filtering/Generate Message cells until message
289
302
  approval. Tell the user Message Drafting is preparing the template in the
290
303
  background.
291
304
 
292
- Prospect Filters persists production rubrics with `save_rubrics` when filters
293
- are enabled. It must not require `brief.md`, `lead-review.md`, or
294
- `lead-sample.json`.
295
-
296
- Run compatibility agent `post-find-leads-message-scout` as Message Drafting
297
- when allowed. Keep the long prompt and refs there.
305
+ Prospect Filters owns filter drafting and may persist production rubrics with
306
+ `save_rubrics`. The parent must verify that durable save before continuing. Run
307
+ compatibility agent `post-find-leads-message-scout` as Message Drafting when
308
+ allowed. Keep the long message prompt and refs there.
298
309
 
299
- Codex: YOLO counts as campaign bg-agent permission. If no permission, ask once
300
- after filter choice. Use generic `gpt-5.5` / `xhigh` if needed. Never draft in
301
- parent silently.
310
+ Codex: YOLO counts as permission for these post-import workers. If no
311
+ permission, ask once after filter choice. Use generic `gpt-5.5` / `xhigh`
312
+ Message Drafting if needed. Never draft in parent silently.
302
313
 
303
314
  If the user critiques or edits the template before `approve-message`, route the
304
315
  feedback back to Message Drafting with current `messageDraftRecommendation`,
305
316
  basis token, campaign/table basis, and latest user text. Do not rewrite copy in
306
317
  the parent or call `update_campaign_brief`. Message Drafting must load/use
307
318
  `generate-messages` and return a revised `messageDraftRecommendation`; parent
308
- renders it and asks approval.
319
+ renders it and asks approval. Message QA and validation before approval also
320
+ belong to Message Drafting; the parent renders its QA receipt, not a fresh
321
+ parent-thread critique. The branch must load the normal validation/QA rules and
322
+ run a QA pass before returning its concise review-ready recommendation.
309
323
 
310
324
  Load:
311
325
 
@@ -275,62 +275,65 @@ gate; tell the customer what happens next instead.
275
275
 
276
276
  ## Parallelism + Naming
277
277
 
278
- After the Find Buyers Plan is approved, source selection is agent-first. Launch
279
- the approved named source scout when exposed; YOLO/autonomous mode counts as
280
- permission. A single LinkedIn-post lane still uses
281
- `source-scout-linkedin-engagement`, not parent `search_signals`. Only try a
282
- fallback after the current place fails, the user asks for comparison, or the
283
- first place is borderline. Keep fallback copy plain: people talking on LinkedIn,
284
- recent LinkedIn titles, broader LinkedIn titles, then company/contact search.
278
+ After the Find Buyers Plan is approved, source selection stays in the parent
279
+ thread for normal create-campaign runs. Do not launch source scouts unless the
280
+ user explicitly asks for a source comparison/debug run. Only try a fallback after
281
+ the current place fails, the user asks for comparison, or the first place is
282
+ borderline. Keep fallback copy plain: people talking on LinkedIn, recent
283
+ LinkedIn titles, broader LinkedIn titles, then company/contact search.
285
284
  Use a 10% planning floor after conservative cleanup. If LinkedIn engagement falls
286
285
  below that floor, move to Sales Nav. If Sales Nav falls below that floor after
287
286
  reasonable refinement, move to Prospeo. Prospeo is the terminal fallback; if it
288
287
  also falls below 10%, tighten the ICP/source direction instead of inventing
289
288
  another provider.
290
289
 
291
- Source-angle comparison should be real, not implied. Call
292
- `get_source_scout_registry` before dispatching source scouts and use the
293
- returned canonical `name` values. In Codex or Claude Code, launch multiple named
294
- source scouts in the same turn only when the user explicitly requested source
295
- comparison, a prior lane failed, or the active flow marks the first lane as
296
- borderline. The parent thread should not fetch every provider prompt first; each
297
- scout loads only its matching provider prompt. If the named scout is unavailable,
298
- use a generic `gpt-5.5` high background agent when possible. Parent source MCP
299
- calls are allowed only when agent launch is impossible or the user approves
300
- inline fallback. If inline, keep output numeric, do not claim a scout ran, and
301
- do not surface install status.
290
+ Source-angle comparison should be real, not implied. Normal source work uses MCP
291
+ provider tools inline from the parent and loads only the active provider prompt.
292
+ Do not claim a source scout ran and do not surface install status.
302
293
 
303
294
  For prospect setup work, call `get_post_find_leads_scout_registry` after
304
295
  the user chooses filters, not before the filter-choice question. After
305
296
  `confirm_lead_list` copies source rows and records the first review/process
306
- sample, ask add-filters vs skip-filters
307
- immediately. Once the user answers, start Message Drafting via
308
- `post-find-leads-message-scout` from the same campaign/table basis. If the user
309
- chooses filters, also start Prospect Filters via
310
- `post-find-leads-filter-scout`, move the browser to Filter Rules, save rubrics,
311
- then ask for filter approval. After approval, keep the
312
- browser on Filter Leads and show `Filters saved + waiting for message approval`
313
- while the message recommendation is reviewed. If the user skips filters, move
314
- the browser to Messages/message review. The join gate is saved-filter approval or
315
- a resolved skip-filter choice, plus a message recommendation grounded in the same
316
- campaign/table basis. Enrichment, filtering, and Generate Message cells wait for
317
- template approval on the Use Template path.
318
-
319
- Only promise parallel prospect setup work when parallel work actually started. If the
320
- host cannot or should not launch background branches, do not silently move the
321
- long message prompt into the parent thread. In Codex, ask once for permission to
322
- use background Message Drafting. If the user declines or asks to continue
323
- without background agents, say the real sequence:
297
+ sample, ask add-filters vs skip-filters immediately. If the user chooses
298
+ filters, launch both Prospect Filters (`post-find-leads-filter-scout`) and
299
+ Message Drafting (`post-find-leads-message-scout`) from the same campaign/table
300
+ basis. If the user skips filters, launch only Message Drafting and move the
301
+ browser to Messages/message review.
302
+
303
+ The parent thread is the orchestrator. On the filters path it must wait for
304
+ Prospect Filters first, verify `save_rubrics` succeeded or save the returned
305
+ production-shaped rubrics itself, keep the browser on Filter Rules, and ask for
306
+ filter approval. Only after saved filters are approved should it move the
307
+ browser to Filter Leads and join/wait for Message Drafting. The join gate is
308
+ saved-filter approval or a resolved skip-filter choice, plus a message
309
+ recommendation grounded in the same campaign/table basis. Enrichment, filtering,
310
+ and Generate Message cells wait for template approval on the Use Template path.
311
+
312
+ Keep the Message Drafting handoff compact: campaign identity, campaign name,
313
+ selected lead list ID, workflow table ID, copied source row count, review-batch
314
+ row count, review-batch basis/hash, filter choice, source summary, and a concise
315
+ campaign brief summary. Do not paste the full review-batch row ID list or row
316
+ data into the spawn prompt. Message Drafting loads the current campaign brief,
317
+ campaign/table state, review rows, full generate-messages prompt, and
318
+ validation/QA rules inside its own branch before returning review-ready output.
319
+
320
+ Only promise background work for Prospect Filters or Message Drafting when it
321
+ actually started. If the host cannot or should not launch those branches, do not
322
+ silently move long filter/message prompts into the parent thread. In Codex, ask
323
+ once for permission to use post-import background agents. If the user declines
324
+ or asks to continue without background agents, say the real sequence:
324
325
 
325
326
  ```text
326
327
  I’ll tighten the filter first, then run message generation from the same sample.
327
328
  ```
328
329
 
329
- Do not say `kicking off two workstreams`, `in parallel`, or `background` as
330
- aspirational copy.
330
+ Do not say `kicking off two workstreams`, `in parallel`, or `background` for
331
+ source work. On the filters path, the only normal background workers are
332
+ Prospect Filters and Message Drafting.
331
333
 
332
334
  Call the post-filter message stage `message generation` in chat.
333
- Message validation is an internal approval proof, not the workstream name.
335
+ Message validation/QA is owned by Message Drafting; the parent renders its
336
+ approval proof, not a fresh critique from memory.
334
337
 
335
338
  Be explicit about what has and has not happened:
336
339
 
@@ -1 +1 @@
1
- {"version":"v2.1-compact","workflow":"create-campaign-v2","principle":"CampaignOffer state/watch link are canonical. Create shell, approve Find Buyers Plan and Start Import, confirm source rows, process first slice, approve filters/message, then run bounded cascade before Settings and explicit start.","normalCustomerPath":"Use campaign state, MCP responses, and concise watchNarration. Do not create, read, link, or surface local draft files. Print the exact tokenized create_campaign.watchUrl only once at the initial brief handoff.","legacyCompatibility":{"validationSubskill":"create-campaign-v2-validation","tailSubskill":"create-campaign-v2-tail","rule":"Legacy validation/rehearsal files are opt-in diagnostics only and are not active flow gates."},"commitGateChoices":["approve","revise-brief","revise-leads","revise-rubric","revise-messaging","abort"],"canonicalStateFields":["campaignId","watchUrl","campaignBrief","currentStep","watchNarration","interactionMode","providerSearchAssociation","selectedLeadListId","workflowTableId","filterChoice","leadScoringRubrics","messageDraftRecommendation","approvedMessageTemplate","senderIds","sequenceTemplate","runningState"],"watchNarrationTransitionContract":{"rule":"Every watched currentStep switch must include fresh watchNarration.","requiredFields":["stage","headline","visibleState","agentIntent","nextAction"],"copyMustInclude":["what just happened","current visible page/state","next user action"],"appliesToTools":["create_campaign","update_campaign","attach_recommended_sequence","start_campaign"],"oneTimeWatchLinkPolicy":"After the initial brief handoff, customer-facing turns must not print another watch link unless the user asks or link recovery is needed."},"lazyReferences":{"watch":["references/watch-link-handoff.md","references/watch-guide-narration.md"],"source":["references/lead-validation-preview.md","references/step-13-import-leads.md"],"filter":["references/filter-leads.md"],"message":[],"tail":["references/sample-validation-loop.md","references/step-15-re-cascade.md","references/final-handoff-contract.md"]},"safetyBoundaries":["Do not call list_senders before Settings after message approval.","Do not import leads until Start Import is approved.","Do not queue cells until confirmed source rows exist in the campaign and the message/filter gates are satisfied.","Do not call start_campaign until the user explicitly confirms launch.","Do not use local files as durable state in normal customer runs."],"yoloMode":{"triggerPhrases":["yolo","--yolo","mode=yolo","autopilot","use best guesses","use best estimates","answer for me","just run it","yolo autopilot"],"identityRequestOnlyWhenMissing":true,"operatorDirectionsRule":"Treat freeform directions supplied at invocation or later as durable operator directions; newest conflicting direction wins.","setInteractionModeAfterCampaignCreate":"autonomous","autoSelectsPreLaunchChoices":["campaign focus","brief approval","source plan","Start Import approval","filters vs skip filters","filter rubric approval","message template approval when recommendation is approve-message","generated message review when quality floor passes","sender selection when exactly one connected sender is safe"],"mustPauseFor":["missing LinkedIn profile URL or handle","missing credentials or required data","ambiguous choice with no reasonable estimate","source/message/filter quality floor failure","final live launch confirmation"],"neverAutoStart":true},"steps":[{"id":"bootstrap","label":"Bootstrap","onEnter":[{"tool":"bootstrap_create_campaign","requiredValues":{"flowVersion":"v2"}},{"tool":"get_subskill_prompt","requiredValues":{"subskillName":"create-campaign-v2"}}],"allowedTools":["bootstrap_create_campaign","get_auth_status","get_active_workspace","get_subskill_prompt","AskUserQuestion","request_user_input"],"doNotAllow":["create_campaign","list_senders","save_rubrics","import_leads","confirm_lead_list","update_campaign","queue_cells","start_campaign"],"waitFor":"bootstrap_complete","transitions":{"bootstrap_complete":"brief-interview"}},{"id":"brief-interview","label":"Client, offer, research, and brief","onEnter":[{"action":"resolve_campaign_identity_before_strategy_packet","allowedTools":["fetch_company","fetch_linkedin_profile","WebFetch","WebSearch"],"mustNotInferFromNameOnly":true,"doNotPresentSenderPickerBeforeIdentityInference":true,"fallback":"Ask only: \"What is your LinkedIn profile URL or handle?\" Normalize bare handles or `/in/...` paths to `https://www.linkedin.com/in/{handle}/`. If the input is not a profile, ask again for the person profile URL before strategy questions.","requiresLinkedInProfileUrl":true,"doNotAcceptAsIdentityInput":["non-profile URL","company page","company/name-only input without a profile handle"],"acceptsLinkedInProfileHandle":true,"normalizeLinkedInProfileHandleToUrl":true},{"action":"run_campaign_identity_research_before_strategy_packet","target":"research-sender","requiredCompletion":"complete_sender_research","allowedTools":["get_subskill_prompt","fetch_linkedin_profile","fetch_company","fetch_linkedin_posts","fetch_company_posts","WebFetch","WebSearch","complete_sender_research"],"requiredInput":"LinkedIn profile URL or handle"},{"action":"confirm_target_before_strategy_packet","uses":"request_user_input","singleChoice":true,"question":"Who should we target first?","options":["Use Sellable's researched recommendation","Choose a different buyer","I'm not sure yet"],"copyMustInclude":["research basis","recommendation is editable","who to target"],"requiredOption":"Other / custom","neverCollectOpenTextWithStructuredQuestion":true,"skipIf":"user already supplied a clear target or yolo_mode with any reasonable best-estimate target"},{"action":"confirm_current_offer_before_strategy_packet","uses":"request_user_input","question":"What should we pitch to prospects first?","options":["Use Sellable's researched recommendation","Use my current pitch","Shape the pitch together"],"copyMustInclude":["research basis","public LinkedIn research may be stale","recommendation is editable","pitch to prospects"],"skipIf":"user already supplied a clear current offer or yolo_mode with any reasonable researched recommendation"},{"action":"confirm_trust_proof_before_strategy_packet","uses":"request_user_input","singleChoice":true,"question":"What is the strongest credibility signal we can lead with?","options":["Use Sellable's recommended proof","Use customer proof or a case study","Use founder or company credibility"],"copyMustInclude":["build trust with prospects","recommendation is editable","proof to use or avoid"],"requiredOption":"Other / custom","skipIf":"user already supplied clear proof to use or avoid, or yolo_mode with any reasonable researched proof"},{"action":"choose_prospect_source_path_before_strategy_packet","uses":"request_user_input","singleChoice":true,"question":"How should we find prospects?","options":["Find prospects for me","Use a CSV of LinkedIn profiles","Use a CSV of company domains"],"copyMustInclude":["prospects","no one added yet","find prospects for me"],"skipIf":"user already supplied a source path or yolo_mode with any reasonable source path"},{"action":"infer_strategy_packet_in_yolo_mode","when":"yolo_mode","skipStructuredSetupQuestions":true,"inferFields":["buyer segment","offer/CTA","proof to use or avoid","lead source","filter choice","message direction"],"sourceOfTruth":"identity/company lookup plus operator directions","mustStateAssumptionsInBrief":true},{"action":"render_supplied_setup_receipt_before_brief_when_setup_questions_skip","when":"any setup question is skipped because the user already supplied or yolo mode inferred identity, target, offer, proof, or source","output":"normal chat receipt before the campaign brief or watch-link handoff","copyMustInclude":["Accepted LinkedIn input: normalized to the required LinkedIn profile URL.","Offer path: supplied current offer or Sellable's researched recommendation; you can replace it with your current offer."],"copyMustNotInclude":["Campaign Identity"]},{"action":"create_watchable_campaign_shell_with_v1_brief","tool":"create_campaign","requiredFields":["name","campaignBrief","clientProspectId or senderLinkedinUrl","currentStep","watchNarration"],"requiredValues":{"currentStep":"create-offer","watchNarration.stage":"brief"},"capture":["campaignId","watchUrl"],"canonicalStateWrites":["campaignId","watchUrl","campaignBrief","currentStep:create-offer"]},{"action":"surface_campaign_shell_watch_link","watchUrlSource":"create_campaign.watchUrl","requiredWatchUrlShape":"exact create_campaign.watchUrl; direct /campaign-builder/{campaignId}?mode={claude|codex} URL with workspaceId and token","copyMustInclude":["We'll keep building the campaign in this chat.","You can watch it being built in real time in the app here:","I'll ask for your approval whenever I need your expertise or taste before moving forward.","Send changes here."],"immediateNextMainChatLine":"Ask for brief approval now. After approval, say: \"Brief approved. Next, we'll choose where to find buyers. I won't add anyone yet.\"","codexBrowserHandoff":{"openWhenAvailable":false,"printWatchLinkOnly":true,"tellUserCommandEnterOrClick":false,"mustNotUseBrowserAutomation":true,"fallbackMustNotClaimInspection":true},"copyMustAvoid":["bare /campaign-builder/{campaignId} URL","derived watch URL","second Watch link:","Next, we'll choose where to find buyers"],"oneTimeOnly":true}],"allowedTools":["get_subskill_prompt","fetch_company","fetch_company_posts","fetch_linkedin_profile","fetch_linkedin_posts","complete_sender_research","create_campaign","AskUserQuestion","request_user_input"],"doNotAllow":["list_senders","save_rubrics","import_leads","confirm_lead_list","queue_cells","start_campaign"],"waitFor":["campaign_shell_created","brief_ready","confirm_with_user"],"transitions":{"campaign_shell_created":"brief-review","brief_ready":"brief-review","confirm_with_user":"brief-review"}},{"id":"brief-review","label":"Brief review","onEnter":[{"action":"render_brief_inline","minimumVisibleDetail":"campaign direction, buyer, offer, proof, source plan, safety gates","copyMustInclude":["This brief is based on Sellable's research and your answers.","If your site or LinkedIn is stale","Do you agree with this researched campaign direction?"],"copyMustNotInclude":["quoted first-message copy","Message Angle as final proof","This approval covers","does not approve adding people","does not approve scraping posts","selecting a sender","attaching a sequence","Campaign Identity","what should this campaign sell first","sell first","[from you]","[from LinkedIn]","[from website]","[from case study]","[Sellable recommendation]"],"messageHintRule":"If any message-shape hint remains, keep it as non-final bullets and say real examples come after leads are found and filtered.","approvalBoundaryRule":"Keep this gate positive and short. Do not list later steps this approval does not cover; ask approve/revise and then move to choosing where to find buyers.","sectionLabelRule":"Use ## Sender and Company for the person/company context; never render ## Campaign Identity.","skipIf":"the full brief was already rendered in the current shell-creation handoff before create_campaign returned","renderOnceRule":"The brief must be visible exactly once before approval. Do not render it again immediately after shell creation; append the one watch-link handoff and ask the approval question.","copyMustAvoid":["duplicate brief render after shell creation","second Watch link:"]},{"action":"ask_brief_choice","uses":"request_user_input","choices":["Approve brief","Revise brief","Approve + YOLO autopilot setup"],"skipIf":"yolo_mode; auto_continue after rendering assumptions unless brief quality floor fails","question":"Do you agree with this researched campaign direction?","autonomousChoice":"Approve + YOLO autopilot setup","choiceDescriptions":{"Approve + YOLO autopilot setup":"Use best guesses to finish setup and stop before final launch."}}],"requiredCampaignState":["campaignId","watchUrl","campaignBrief","currentStep"],"allowedTools":["AskUserQuestion","request_user_input","update_campaign"],"doNotAllow":["create_campaign","list_senders","import_leads","confirm_lead_list","queue_cells","start_campaign"],"waitFor":["user_brief_confirmed","revise_brief","auto_continue"],"transitions":{"user_brief_confirmed":"find-leads","revise_brief":"brief-interview","auto_continue":"find-leads"}},{"id":"find-leads","label":"Find leads","sourceSelectionFunnel":{"preScoutRecommendationGate":{"required":true,"label":"Find buyers plan approval","mustHappenBefore":["get_provider_prompt","search_signals","search_sales_nav","search_prospeo","fetch_post_engagers","source-scout dispatch"],"show":["buyer groups or places we could check","best place to start","why the right buyers are likely to be there","what signs the next search will check","where to look next if the first place is too thin","what approval authorizes"],"approvalAuthorizes":"looking for the best places to find buyers; no one added yet","explanationMustInclude":["where the right buyers are already talking","why that place fits this campaign","enough likely prospects","what counts as a good sign","where to look next if thin","I won't add anyone yet","choose where to find buyers"],"yoloMode":{"autoApproveRecommendedLane":true,"mustShowAssumedChoice":true,"pauseIfConfidenceLow":true},"customerLanguage":{"readability":"grade-5","requiredHeading":"Find Buyers Plan","prefer":["place to look","best place to start","people to check","prospects","I won't add anyone yet"],"avoid":["lane","source scouting","provider","precision/scale tradeoff","evidence quality","pilot volume","ICP","workflow pain","lead-source scouting","not importing leads"],"termRule":{"buyers":"target market","peopleToCheck":"raw reactions/comments before fit is known","prospects":"likely usable people after fit","leads":"campaign rows"}},"questionOrder":"update_campaign to pick-provider, render ## Find Buyers Plan in normal chat without repeating the watch link, then open the structured approval question; never ask first or show the plan after approval","copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"],"approvalFreshnessRule":"Only an approval question opened after the visible Find Buyers Plan, or an explicit source choice made after that plan, can set source_lane_approved. Do not reuse brief approval or pre-plan provider state."},"defaultWhenSourceUnspecified":["signal-discovery","sales-nav-recent-active","sales-nav-general","prospeo"],"parallelAllowedOnlyWhen":["user explicitly requested source comparison"]},"onEnter":[{"tool":"update_campaign","requiredValues":{"currentStep":"pick-provider","watchNarration.stage":"find-leads","watchNarration.headline":"Review where to find buyers","watchNarration.visibleState":"The app is showing source selection before lead finding starts.","watchNarration.agentIntent":"Codex is explaining where it recommends looking first and where it will look next if that is too thin.","watchNarration.nextAction":"Approve where to look first or choose another place","watchNarration.safety":"Only deciding where to look. No one is added yet."}},{"tool":"get_source_scout_registry"},{"action":"render_find_buyers_plan_inline","oneShot":true,"requiredPrecondition":"currentStep=pick-provider has been saved with update_campaign","mustHappenAfter":["update_campaign currentStep=pick-provider"],"mustHappenBefore":["ask_find_buyers_plan_choice","get_provider_prompt","search_signals","search_sales_nav","search_prospeo","fetch_post_engagers","source-scout dispatch"],"output":"normal chat text before any request_user_input approval panel","requiredHeading":"Find Buyers Plan","requiredInlineFields":["plain-language buyer groups or places for this campaign","best place to start","why the right buyers are likely to be there","what signs the next search will check","where to look next if the first place is too thin","what approval authorizes"],"copyMustInclude":["Find Buyers Plan","best place to start","I won't add anyone","choose where to find buyers","I won't add anyone yet"],"copyMustAvoid":["lane","source scouting","provider","precision/scale tradeoff","evidence quality","pilot volume","ICP","workflow pain","lead-source scouting","not importing leads","Watch link:","campaign-builder URL","repeat the watch URL"],"approvalBoundary":"Explain what approval authorizes before asking: looking for the best places to find buyers; no one is added yet.","linkPolicy":"Do not include another watch link. The first brief handoff already gave the user the live app URL."},{"action":"ask_find_buyers_plan_choice","uses":"request_user_input","oneShot":true,"requiredPrecondition":"render_find_buyers_plan_inline completed in normal chat after pick-provider state was saved; no earlier approval can satisfy this","skipIf":"source_lane_approved was set by ask_find_buyers_plan_choice after the visible Find Buyers Plan, leadSourceProvider was explicitly chosen by the user after that plan, or yolo_mode auto-selected source_lane_approved after showing the assumed choice","choices":["Start with LinkedIn posts","Start with active LinkedIn profiles","Start with company/contact search","Choose a different place","Pause here"],"approvalChoiceLabelsByProvider":{"signal-discovery":"Start with LinkedIn posts","sales-nav":"Start with active LinkedIn profiles","prospeo":"Start with company/contact search"},"approvalState":"source_lane_approved","copyMustInclude":["Approve this plan for where to find buyers?","Start with LinkedIn posts"],"mustNotHappenBefore":["render_find_buyers_plan_inline"],"approvalStateRule":"Set source_lane_approved here after the visible plan; never from brief approval, generic approval, pre-plan provider state, or artifacts."},{"action":"persist_provider_search_step_before_search","tool":"update_campaign","requiredPrecondition":"source_lane_approved set after the visible Find Buyers Plan approval question","providerCurrentStepMap":{"signal-discovery":"signal-discovery","sales-nav":"sales-nav","prospeo":"prospeo"},"requiredValues":{"leadSourceType":"new","leadSourceProvider":"approved provider","currentStep":"provider-specific current step","watchNarration.stage":"find-leads","watchNarration.headline":"Searching the approved place","watchNarration.safety":"Looking only. No one is added yet."},"mustRunBefore":["search_signals","search_sales_nav","search_prospeo","fetch_post_engagers"]},{"action":"run_agent_first_source_funnel","requiredPrecondition":"source_lane_approved and get_source_scout_registry returned current named scouts","defaultOrder":["source-scout-linkedin-engagement","source-scout-sales-nav","source-scout-prospeo-contact"],"campaignOfferIdRequired":true,"stopOnFirstViableUnlessComparisonRequested":true,"agentFirst":true,"singleLaneUsesNamedScout":true,"yoloAutonomousCountsAsAgentPermission":true,"namedScoutByProvider":{"signal-discovery":"source-scout-linkedin-engagement","sales-nav":"source-scout-sales-nav","prospeo":"source-scout-prospeo-contact"},"parentFallbackRule":"parent MCP source calls only when agent launch is impossible or user approves inline fallback."}],"requiredCampaignState":["campaignId","campaignBrief","currentStep"],"allowedTools":["get_source_scout_registry","get_provider_prompt","lookup_sales_nav_filter","search_sales_nav","search_prospeo","search_signals","select_promising_posts","fetch_post_engagers","fetch_company","fetch_linkedin_profile","load_csv_linkedin_leads","load_csv_domains","get_rows_minimal","update_campaign","AskUserQuestion","request_user_input","get_campaign_navigation_state"],"doNotAllow":["create_campaign","list_senders","save_rubrics","import_leads","confirm_lead_list","queue_cells","start_campaign"],"waitFor":["lead_review_ready","revise_brief","confirm_with_user"],"transitions":{"lead_review_ready":"lead-review","revise_brief":"brief-interview","confirm_with_user":"lead-review"}},{"id":"lead-review","label":"Start Import approval","onEnter":[{"action":"show_source_decision_card","requiredInlineFields":["primary source and exact filters/recipe","Start Import action awaiting approval","for Signal Discovery: compact Source Recommendation in plain language with selected posts, people to check, likely prospects, first review, and fallback","for Signal Discovery: recommended scrape post count and target people-to-check volume","first campaign review size, clearly separate from source sampling","runner-up and why it lost","raw volume","sampled people","sampled fits as n/N plus percentage/range","estimated usable prospects","cleanup risk","what happens after Start Import"],"copyMustAvoid":["lead-source scouting","source scouting","headline-fit","engagers needed","execution slice","ICP","good buyers","real buyers","This approval covers","It does not approve","does not approve filters","filters, messages, sender selection","sender selection, sequence, or sending","Watch link:","campaign-builder URL","repeat the watch URL"],"approvalBoundaryRule":"Use positive Start Import copy only. Include: \"After approval, I will build the source list, add it to the campaign, and review the first 15 leads before we scale.\" Never list future non-approvals.","linkPolicy":"Do not include another watch link in Source Recommendation; describe the Start Import decision only."},{"action":"ask_source_review_choice","uses":"request_user_input","choices":["Approve scraping N recommended LinkedIn posts","Start Import for the approved source list","Revise source","Pause here"],"approvalChoiceLabelsByProvider":{"signal-discovery":"Approve scraping {scrapePostCount} recommended LinkedIn posts?","sales-nav":"Start Import for the approved Sales Nav source list","prospeo":"Start Import for the approved Prospeo source list"},"postApprovalContract":{"singleUseApproval":true,"doNotRepeatAfterApproval":["Source Recommendation","show_source_decision_card","ask_source_review_choice"],"requiredNextActionAfterApproval":"ack once; call import_leads immediately","signalDiscoveryNextTool":"import_leads({ provider: \"signal-discovery\", targetEngagerCount, maxPostsToScrape, confirmed: true })"},"autoSelectIf":"yolo_mode and projected usable pool clears the source quality floor","copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"]}],"requiredCampaignState":["campaignId","campaignBrief","providerSearchAssociation"],"allowedTools":["AskUserQuestion","request_user_input","update_campaign"],"doNotAllow":["create_campaign","list_senders","save_rubrics","import_leads","confirm_lead_list","queue_cells","start_campaign"],"waitFor":["lead_review_confirmed","revise_leads","confirm_with_user","auto_continue"],"transitions":{"lead_review_confirmed":"auto-execute-leads","revise_leads":"find-leads","confirm_with_user":"auto-execute-leads","auto_continue":"auto-execute-leads"}},{"id":"auto-execute-leads","label":"Materialize confirmed source list","currentStepValue":"auto-execute-leads","reference":"references/step-13-import-leads.md","onEnter":[{"tool":"get_subskill_prompt","requiredValues":{"subskillName":"create-campaign-v2-tail"}},{"tool":"import_leads","requiredFields":["campaignOfferId","selected source/list","sourceListTarget or targetEngagerCount"],"requiredValues":{"salesNavProspeoDefaultSourceListTarget":1000,"signalDiscoveryDefaultEngagerTarget":1500},"modeAddHandshake":{"firstCallReturns":"needsModeSelection when adding to an existing campaign-attached list","requiredFollowup":"retry with mode=add after explicit user/source-state compatibility is confirmed"},"surfaceDedupRatio":true},{"tool":"wait_for_lead_list_ready","repeatUntil":"ready_true_or_cancelled_or_import_failed","requiredValues":{"requireComplete":true},"onImportStillRunning":"re-run wait_for_lead_list_ready; do not call confirm_lead_list just because rows exist","partialOverrideRule":"Only if the user explicitly asks to keep going early may the next confirm_lead_list call pass allowPartialSourceList: true."},{"tool":"confirm_lead_list","requiredFields":["campaignOfferId","selectedLeadListId","reviewBatchLimit"],"requiredValues":{"reviewBatchLimit":15},"capture":["workflowTableId","reviewBatchRowIds"],"requiredPrecondition":"wait_for_lead_list_ready returned ready:true, unless the user explicitly asked to keep going early with a partial list","partialOverrideField":"allowPartialSourceList:true only after explicit user early-continue instruction","mustNotRunWhen":["wait_for_lead_list_ready reason=import_still_running and no explicit early-continue instruction","wait_for_lead_list_ready reason=cancelled","wait_for_lead_list_ready reason=import_failed","missing import job metadata"]},{"tool":"wait_for_campaign_table_ready"},{"tool":"get_rows_minimal","requiredValues":{"tableId":"{workflowTableId}","limit":15},"capture":["reviewBatchRowHash"]},{"action":"summarize_review_batch_and_advance_to_filter_choice","maxCustomerCopyLines":4,"copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"],"linkPolicy":"Ask add filters vs skip filters without repeating the watch link.","purpose":"ask filter choice immediately"}],"requiredCampaignState":["campaignId","providerSearchAssociation","selectedLeadListId"],"allowedTools":["get_subskill_prompt","import_leads","wait_for_lead_list_ready","confirm_lead_list","wait_for_campaign_table_ready","get_rows_minimal","update_campaign","AskUserQuestion","request_user_input"],"doNotAllow":["get_post_find_leads_scout_registry","Task","spawn_agent","list_senders","queue_cells","start_campaign","enrich_with_prospeo","bulk_enrich_with_prospeo","save_rubrics"],"waitFor":"source_list_confirmed_and_review_sample_ready","transitions":{"source_list_confirmed_and_review_sample_ready":"filter-choice","escalation_triggered":"escalation"},"hardRules":["import_leads_then_repeated_wait_for_lead_list_ready_then_confirm_lead_list_then_wait_for_campaign_table_ready","partial_source_rows_do_not_unblock_confirm_lead_list_without_explicit_user_early_continue","cancelled_or_failed_source_import_stops_before_campaign_table_copy"]},{"id":"filter-choice","label":"Filter choice","currentStepValue":"filter-choice","onEnter":[{"tool":"update_campaign","requiredValues":{"currentStep":"filter-choice","watchNarration.stage":"fit-message"}},{"action":"ask_filter_choice","uses":"request_user_input","choices":["Use filters","Skip filters","Revise source"],"autoSelectIf":"yolo_mode; choose filters unless source is tightly curated or user directed otherwise","copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"],"copyMustInclude":["source rows are in the campaign","add fit filters before message drafting or skip filters"]}],"hardRules":["ask_filter_choice_immediately_after_review_batch_import","do_not_call_get_subskill_prompt_before_filter_choice","do_not_call_get_subskill_asset_before_filter_choice","do_not_call_get_post_find_leads_scout_registry_before_filter_choice","do_not_spawn_prospect_setup_before_filter_choice"],"requiredCampaignState":["campaignId","campaignBrief","selectedLeadListId","workflowTableId"],"allowedTools":["AskUserQuestion","request_user_input","update_campaign","get_campaign_navigation_state"],"doNotAllow":["get_subskill_prompt","get_subskill_asset","get_post_find_leads_scout_registry","Task","spawn_agent","create_campaign","list_senders","import_leads","confirm_lead_list","queue_cells","start_campaign","generate_messages"],"waitFor":["filters_enabled","filters_skipped","revise_leads"],"transitions":{"filters_enabled":"prospect-setup","filters_skipped":"message-generation","revise_leads":"find-leads"}},{"id":"prospect-setup","label":"Prospect setup","onEnter":[{"action":"persist_add_filters_approval","tool":"update_campaign","when":"filters_enabled","requiredFields":["campaignId","enableICPFilters","currentStep","watchNarration"],"requiredValues":{"enableICPFilters":true,"currentStep":"create-icp-rubric","watchNarration.stage":"fit-message","watchNarration.headline":"Create filter rules","watchNarration.visibleState":"Filters are enabled and the browser is showing Filter Rules while Codex defines the rubric.","watchNarration.nextAction":"Review saved filter rules"},"mustRunBefore":["get_post_find_leads_scout_registry","launch_prospect_filters_after_filter_choice","save_rubrics"]},{"tool":"get_post_find_leads_scout_registry"},{"action":"launch_prospect_filters_after_filter_choice","mode":"parallel_when_host_supports_subagents","target":"post-find-leads-filter-scout","inputs":["campaignId","campaignBrief","selectedLeadListId","workflowTableId","reviewBatchRowIds/hash","filterChoice"],"stateSource":"live campaign/table","debugFilesOptionalOnly":true},{"action":"launch_message_drafting_after_filter_choice","mode":"parallel_when_host_supports_subagents","target":"post-find-leads-message-scout","when":"filters_enabled","doesNotQueueCells":true,"customerNarration":"Say Message Drafting is preparing."},{"action":"save_filter_rubrics_to_campaign","tool":"save_rubrics","when":"filters_enabled and rubrics are production-shaped","requiredFields":["campaignOfferId","leadScoringRubrics"],"writesCampaignState":"leadScoringRubrics","requiredSideEffects":{"enableICPFilters":true,"currentStep":"create-icp-rubric","watchNarration.headline":"Filter rules saved for review"}},{"action":"ask_filter_rubric_review_choice","uses":"request_user_input","choices":["Approve filters","Revise filters","Pause"],"copyMustInclude":["These rules prevent wasted sends before I score/import the list.","Recommended because of the researched ICP, source sample, and repeated false-positive patterns.","one pass example and one block example when available","approval is for the filter rules","Approve these filter rules."],"autoSelectIf":"yolo_mode and rubrics are production-shaped","copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"]}],"requiredCampaignState":["campaignId","campaignBrief","selectedLeadListId","workflowTableId"],"allowedTools":["get_subskill_prompt","get_subskill_asset","get_post_find_leads_scout_registry","save_rubrics","get_campaign","get_rows_minimal","update_campaign","get_campaign_navigation_state","AskUserQuestion","request_user_input","Task","spawn_agent"],"doNotAllow":["create_campaign","list_senders","import_leads","confirm_lead_list","queue_cells","start_campaign","check_rubric","generate_messages"],"waitFor":["filter_rubrics_approved","revise_leads","revise_rubric","revise_messaging"],"hardRules":["after_save_rubrics_currentStep_must_stay_create-icp-rubric_until_filter_approval","filter_approval_required_before_apply-icp-rubric_or_queue_campaign_cells","do_not_move_browser_to_messages_until_filter_leads_step_is_current_or_filters_are_explicitly_skipped","no_prospect_setup_worker_or_deep_prompt_before_filter_choice","prospect_filters_start_only_after_filters_enabled","message_drafting_after_filter_choice","message_drafting_no_cells"],"transitions":{"filter_rubrics_approved":"message-generation","revise_leads":"find-leads","revise_rubric":"filter-rubric","revise_messaging":"message-generation","confirm_with_user":"message-review"}},{"id":"filter-rubric","label":"Rubric revision","onEnter":[{"tool":"get_subskill_asset","requiredValues":{"subskillName":"create-campaign-v2","assetPath":"references/filter-leads.md"}},{"tool":"save_rubrics","requiredFields":["campaignOfferId","leadScoringRubrics"],"writesCampaignState":"leadScoringRubrics","requiredSideEffects":{"enableICPFilters":true,"currentStep":"create-icp-rubric","watchNarration.headline":"Filter rules saved for review"}},{"action":"ask_filter_rubric_review_choice","uses":"request_user_input","choices":["Approve filters","Revise filters","Pause"],"copyMustInclude":["These rules prevent wasted sends before I score/import the list.","approval object is filter rules only","approval is for the filter rules","Approve these filter rules."],"autoSelectIf":"yolo_mode and rubrics are production-shaped","copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"]}],"requiredCampaignState":["campaignId","workflowTableId"],"allowedTools":["get_subskill_asset","save_rubrics","AskUserQuestion","request_user_input"],"doNotAllow":["create_campaign","list_senders","import_leads","confirm_lead_list","update_campaign","queue_cells","start_campaign","check_rubric"],"waitFor":["filter_rubrics_approved","revise_leads","confirm_with_user","auto_continue"],"transitions":{"filter_rubrics_approved":"message-generation","revise_leads":"find-leads","confirm_with_user":"message-generation","auto_continue":"message-generation"}},{"id":"message-generation","label":"Message drafting","onEnter":[{"action":"set_message_review_visible_step_by_filter_choice","tool":"update_campaign","branchRules":["yes after filter approval: currentStep=apply-icp-rubric; wait on Filter Leads","no: currentStep=messages; message review"],"watchNarration.stage":"fit-message"},{"action":"run_or_reconcile_message_drafting","target":"post-find-leads-message-scout","toolCallRequiredBeforeDraft":["run Message Drafting via post-find-leads-message-scout; yolo counts","if no yolo and permission missing, ask once","use generic gpt-5.5 xhigh agent if named unavailable","load get_subskill_prompt({ subskillName: \"generate-messages\" }) + refs"],"stateSource":"campaignBrief, source, selectedLeadListId, workflowTableId, execution-slice row ids/hash","outputState":"messageDraftRecommendation","revisionMode":{"when":"revise_messaging or latest user message requests template changes before approve-message","route":"send feedback to post-find-leads-message-scout or generic gpt-5.5 xhigh Message Drafting branch","requiredInputs":["latest user feedback","current messageDraftRecommendation and basisToken/outputHash","campaign/table execution-slice state"],"forbiddenParentActions":["rewrite template in parent","update_campaign_brief before approve-message"],"output":"revised messageDraftRecommendation rendered before request_user_input"}}],"requiredCampaignState":["campaignId","campaignBrief","selectedLeadListId","workflowTableId"],"allowedTools":["get_subskill_prompt","get_campaign","get_rows_minimal","update_campaign"],"toolRules":["message_drafting_agent_first_no_silent_parent_fallback; yolo counts","brief.md, lead-review.md, and lead-sample.json are optional debug context only.","messageDraftRecommendation returns templateRecommendation, tokenFillRules, renderedSample, concerns, status, basisToken, outputAt, outputHash, and error/retry detail.","If campaign/source/table/execution-slice basis does not match, classify the output stale or blocked.","message_revision_feedback_routes_to_message_drafting_agent_not_parent"],"doNotAllow":["create_campaign","list_senders","save_rubrics","import_leads","confirm_lead_list","queue_cells","start_campaign","generate_messages","AskUserQuestion","request_user_input"],"waitFor":["message_validation_ready","revise_rubric","revise_messaging"],"transitions":{"message_validation_ready":"message-review","revise_rubric":"filter-rubric","revise_messaging":"message-generation"},"revisionLoop":{"trigger":"user gives copy feedback, asks for an update, or chooses revise-messaging","required":["route latest user feedback to Message Drafting with current recommendation and live campaign/table state","produce a new messageDraftRecommendation with revisionNotes","render revised ## Message Template, ## Rendered Example, and What changed before asking approval"],"blocked":["asking whether an unseen new version is better","reusing the old template after acknowledging feedback","rewriting the template in the parent thread","update_campaign_brief before approve-message","request_user_input or AskUserQuestion during message-generation"],"outputState":"messageDraftRecommendation"},"hardRules":["message_revision_must_render_new_template_before_approval_question","message_revision_saves_only_after_approve_message","message_generation_must_not_call_request_user_input","message_generation_must_transition_to_message_review_only_after_renderable_draft_state","message_drafting_agent_first_no_silent_parent_fallback","message_revision_routes_to_message_drafting_agent"]},{"id":"message-review","label":"Message review","onEnter":[{"action":"render_message_review_from_state","requiredState":["campaignBrief","selectedLeadListId","workflowTableId","messageDraftRecommendation"],"requiredVisibleLabels":["## Message Template","## Rendered Example","Good token fill:","My take:","Question: approve-message or revise-messaging?","Recommendation:"],"doNotShowByDefault":["Token Notes","Good omit / fallback","Bad fill to avoid","Token Adherence Table"],"internalPersistenceOnly":["Token Fill Rules","Token Fill Examples","fallback guidance","bad-fill avoidance notes"],"copyMustInclude":["QA receipt before approval","tokens resolved","company/person match","proof claim","prospect angle","language/tone when known","obvious bad fits","Would I take this call?","weak personalization can burn the sender's reputation","full list remains paused"],"copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"],"revisionRenderRule":"After revise_messaging or user copy feedback, route feedback to Message Drafting and show revised template, rendered example, and What changed before ask_message_review_choice; never ask if an unseen version is better."},{"action":"ask_message_review_choice","uses":"request_user_input","choices":["approve-message","revise-messaging"],"copyMustInclude":["approve this message template and continue","nothing sends from this approval"],"autoSelectIf":"yolo_mode and Recommendation is approve-message; revise autonomously when Recommendation is revise-messaging","copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"],"requiredPrecondition":"current or revised template and rendered example are visible in this turn","revisionPrecondition":"after revise_messaging, revised template is visible before this question"},{"action":"sync_approved_message_set_to_campaign_brief","tool":"update_campaign_brief","when":"after approve-message","requiredFields":["campaignId","approvedMessageTemplate","Token Fill Rules","Token Fill Examples"],"writesCampaignState":"approvedMessageTemplate","revisionApprovalRule":"If the user requested revisions, write only the latest Message Drafting-approved revised template and token rules after approve-message.","forbiddenBefore":"approve-message"}],"requiredCampaignState":["campaignId","workflowTableId","messageDraftRecommendation"],"allowedTools":["AskUserQuestion","request_user_input","update_campaign_brief","update_campaign","get_rows_minimal"],"doNotAllow":["create_campaign","list_senders","save_rubrics","import_leads","confirm_lead_list","start_campaign"],"waitFor":["message_approved","revise_messaging"],"transitions":{"message_approved":"validate-sample","revise_messaging":"message-generation"}},{"id":"validate-sample","label":"Validate campaign-table execution slice","currentStepValue":"apply-icp-rubric","reference":"references/sample-validation-loop.md","visibleStepRule":"Filter Leads is reached only after saved-filter approval; on approve-message, save the template and queue bounded Enrich Prospect cells through selector-based campaign processing.","onEnter":[{"tool":"update_campaign","requiredValues":{"currentStep":"apply-icp-rubric","watchNarration.stage":"fit-message"},"watchNarrationRule":"Say the template is saved and Filter Leads is now running the bounded enrichment and scoring pass."},{"tool":"queue_campaign_cells","requiredFields":["workflowTableId","columnRole","rowSelector"],"requiredValues":{"columnRole":"enrich","rowSelector":{"type":"reviewBatch"},"forceRerun":false},"targetCountSource":"WorkflowTable.config.mcp.reviewBatch.rowCount (default 15)"},{"tool":"wait_for_campaign_processing","requiredFields":["workflowTableId","minPassedCount"],"requiredValues":{"minPassedCount":1},"readVia":"stats_only_tool_result","timeoutGuidance":"If this times out, do not repoll identical args; use partial diagnostics to revise filters or surface blocked sample state.","customerSummaryPattern":["{checked} leads checked","{passed} passed fit scoring","{blocked} blocked before sending","full list remains paused until approval"]},{"action":"handle_partial_or_timeout_sample","customerStatus":"sample-needs-revision","rule":"Do not repeat waits indefinitely; surface partial status and route to revision.","copyMustInclude":"completed, passed, pending, blocked before sending, and full list remains paused"}],"requiredCampaignState":["campaignId","workflowTableId","approvedMessageTemplate","filterRubricsApproved when enableICPFilters=true"],"allowedTools":["get_subskill_asset","queue_campaign_cells","select_campaign_cells","get_campaign_table_schema","wait_for_campaign_processing","update_campaign","AskUserQuestion","request_user_input"],"doNotAllow":["import_leads","list_senders","start_campaign","enrich_with_prospeo","bulk_enrich_with_prospeo","check_rubric"],"hardRules":["campaign_processing_waits_are_stats_only_by_default","queue_review_batch_by_selector_never_fetch_rows_for_cell_ids","timeout_never_repeats_without_customer_handoff","timeout_or_underfloor_sample_never_advances_to_settings"],"waitFor":["sample_validated","sample_revision_required"],"transitions":{"sample_validated":"auto-execute-messaging","sample_revision_required":"lead-review","revise_leads":"find-leads","revise_rubric":"filter-rubric","escalation_triggered":"escalation"}},{"id":"auto-execute-messaging","label":"Generate initial campaign-row messages","currentStepValue":"auto-execute-messaging","references":["references/parallel-critique-protocol.md","references/thomas-variant-selection.md","references/thomas-revision-filters.md","references/step-15-re-cascade.md"],"onEnter":[{"tool":"queue_campaign_cells","batchSize":100,"when":"any passing row has pending, empty, or stale Generate Message cell","requiredFields":["workflowTableId","columnRole","rowSelector"],"requiredValues":{"columnRole":"generateMessage","rowSelector":{"type":"needsGeneratedMessage"},"forceRerun":false},"cellSource":"selector-engine needsGeneratedMessage rows using approved campaign brief template"},{"tool":"wait_for_campaign_processing","purpose":"wait_for_first_current_revision_generated_message","requiredValues":{"minGeneratedMessages":1,"templateRevision":"current"},"readVia":"stats_only_tool_result","invariant":"A generated message implies the pass gate; stale or wrong-revision messages are excluded from readiness."},{"action":"observe_generate_message_results","reference":"references/step-15-re-cascade.md"},{"action":"enforce_token_contract","modeFromConfig":"messaging.tokenContract"},{"action":"optional_critique_pass","enabledFromConfig":"messaging.critique.enabled","plan":"85-03","protocol":"references/parallel-critique-protocol.md","sampleSizeFromConfig":"messaging.critique.sampleSize","budgetUsdCapFromConfig":"messaging.critique.budgetUsdCap","perCriticTimeoutFromConfig":"messaging.critique.perCriticTimeoutSeconds","totalTimeoutFromConfig":"messaging.critique.totalTimeoutSeconds","criticsFromConfig":"messaging.critique.critics","enforceFinalizerPassFromConfig":"messaging.critique.synthesis.enforceFinalizerPass","rejectOnFakeProofFromConfig":"messaging.critique.rejectOnFakeProof","rejectOnUnsupportedTokenFromConfig":"messaging.critique.rejectOnUnsupportedToken","onBudgetTrip":"halt_critique_continue_plain_tail","onPerCriticTimeout":"drop_critic_proceed_with_remaining","onTotalTimeout":"persist_plain_message_for_row","onTokenContractViolation":"persist_plain_message_for_row","onFakeProof":"persist_plain_message_for_row"},{"action":"optional_opus_subset","enabledFromConfig":"messaging.critique.opus.enabled","selection":"references/thomas-variant-selection.md","maxMessagesPerPassFromConfig":"messaging.critique.opus.maxMessagesPerPass","budgetUsdCapFromConfig":"messaging.critique.opus.budgetUsdCap","onOpusBudgetTrip":"halt_opus_continue_non_opus","onOpusTokenContractViolation":"fallback_to_non_opus_rewrite"},{"tool":"update_campaign","requiredValues":{"currentStep":"auto-execute-messaging","watchNarration.stage":"review-ready"},"watchNarrationRule":"Say the first passing generated message is ready in Messages; next is review before Settings."},{"action":"ask_generated_message_review_choice","uses":"request_user_input","choices":["Approve reviewed draft rows and continue to Settings","Revise filters","Revise message template","Pause here"],"copyMustInclude":["approve the reviewed draft rows and continue to Settings","tokens resolved","company/person match","proof claim","prospect angle","language/tone when known","obvious bad fits","Would I take this call?","weak personalization can burn the sender's reputation","full list remains paused","nothing sends from this approval"],"autoSelectIf":"yolo_mode and the first passing generated message clears the quality floor","copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"]}],"allowedTools":["get_subskill_asset","get_campaign_table_schema","select_campaign_cells","queue_campaign_cells","wait_for_campaign_processing","revise_message_template_and_rerun","update_campaign","AskUserQuestion","request_user_input"],"doNotAllow":["import_leads","list_senders","start_campaign","generate_messages"],"hardRules":["generated_message_count_excludes_stale_or_wrong_revision_messages","message_template_revision_uses_brief_update_then_generate_message_rerun","do_not_overwrite_generated_message_cells_directly","critique_failure_never_escalates","critique_sample_size_bounded_by_config","first_passing_generated_message_unblocks_review","critics_fixed_at_targeting_copy_voice","synthesis_enforces_phase_84_token_contract","opus_reserved_for_highest_value_subset","proposed_token_never_persisted_in_rewrite"],"waitFor":["generated_messages_approved","sample_revision_required"],"transitions":{"generated_messages_approved":"awaiting-user-greenlight","revise_filters":"filter-rubric","revise_messaging":"message-generation","escalation_triggered":"escalation"}},{"id":"awaiting-user-greenlight","label":"Settings, sender, sequence, and greenlight","currentStepValue":"awaiting-user-greenlight","reference":"references/final-handoff-contract.md","onEnter":[{"tool":"get_campaign"},{"tool":"list_senders","purpose":"surface available connected senders only at Settings"},{"tool":"update_campaign","requiredValues":{"currentStep":"settings","watchNarration.stage":"review-ready"},"watchNarrationRule":"Say message review is complete and the app is on Settings for sender choice. Mention lead research/filtering already happened outside the user's LinkedIn account and nothing sends until Start."},{"action":"surface_sender_and_slack_handoff","requiredVisibleContent":["connect or select a LinkedIn sender","Lead research and filtering already happened outside your LinkedIn account.","This account is only used for approved sending after final launch.","Nothing sends until the final Start step.","Slack reply review","recommended sequence","final launch confirmation is still ahead"],"copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"]},{"action":"ask_sender_selection","uses":"request_user_input","singleChoice":true,"choices":["Use this connected sender","Connect a different sender in Settings","Pause here"],"autoSelectIf":"yolo_mode and exactly one safe connected sender is available","copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"]},{"action":"attach_selected_sender","tool":"update_campaign","when":"user selected an available connected sender","requiredValues":{"senderIds":["{selectedSenderId}"],"currentStep":"sequence","watchNarration.stage":"review-ready"},"watchNarrationRule":"Say the sender was attached and the app is moving to Sequence review; sequence remains editable and nothing sends until Start."},{"tool":"attach_recommended_sequence","when":"after senderIds are attached","requiredValues":{"campaignId":"{campaignId}","currentStep":"send","watchNarration.stage":"review-ready"},"watchNarrationRule":"The sequence tool owns this visible beat: say the recommended follow-up sequence is attached, the app is on final launch review, and it is still not sending until Start."},{"action":"ask_final_launch_greenlight","uses":"request_user_input","singleChoice":true,"choices":["Start campaign","Review campaign first","Pause here"],"copyMustInclude":["quality confidence means sample messages and prospect angle were checked","launch confidence means sender, sequence, and Start are ready","approved messages begin sending according to the sequence","replies and meetings follow connected settings","you can monitor and pause","no hidden extra approval disappears after clicking Start"],"onUserStart":"claude-greenlight","neverAutoSelectInYolo":true,"copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"]}],"allowedTools":["get_campaign","get_campaign_navigation_state","list_senders","update_campaign","attach_recommended_sequence","AskUserQuestion","request_user_input"],"doNotAllow":["start_campaign","import_leads"],"autoStart":false,"watchRequired":true,"waitFor":["sender_connection_required","sender_attached","sequence_attached","ready_to_launch","user_greenlight","ui_start_detected"],"transitions":{"sender_connection_required":"awaiting-user-greenlight","sender_attached":"awaiting-user-greenlight","sequence_attached":"awaiting-user-greenlight","ready_to_launch":"awaiting-user-greenlight","user_greenlight":"claude-greenlight","ui_start_detected":"running"}},{"id":"claude-greenlight","label":"Explicit launch","reference":"references/final-handoff-contract.md","onEnter":[{"tool":"get_campaign"},{"action":"verify_sequence_and_current_step_before_start","requiredState":["workflowTableId","senderIds","sequenceTemplate","at least one approved generated message","currentStep in awaiting-user-greenlight|claude-greenlight|send"]},{"tool":"start_campaign","requiredFields":["campaignId"],"persistsCurrentStep":"running","watchNarrationRule":"After start_campaign succeeds, the running state must say the final greenlight was accepted, the campaign is now live/running, and the user can watch progress from the campaign."}],"allowedTools":["get_campaign","attach_recommended_sequence","start_campaign","AskUserQuestion","request_user_input"],"watchRequired":true,"waitFor":"campaign_started","transitions":{"campaign_started":"running"}},{"id":"running","label":"Campaign is live","currentStepValue":"running","onEnter":[{"action":"surface_campaign_live_confirmation_without_watch_link","copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"]}],"allowedTools":["get_campaign","AskUserQuestion","request_user_input"],"doNotAllow":["start_campaign"],"terminal":true},{"id":"escalation","label":"Escalation","reference":"references/escalation-ladder.md","allowedTools":["AskUserQuestion","request_user_input"],"doNotAllow":["start_campaign","import_leads"],"transitions":{"revise_brief":"brief-interview","revise_leads":"find-leads","revise_rubric":"filter-rubric","revise_messaging":"message-generation","abort":"abort"}},{"id":"abort","label":"Abort","allowedTools":["AskUserQuestion","request_user_input"],"terminal":true}]}
1
+ {"version":"v2.1-compact","workflow":"create-campaign-v2","principle":"CampaignOffer state/watch link are canonical. Create shell, approve Find Buyers Plan and Start Import, confirm source rows, process first slice, approve filters/message, then run bounded cascade before Settings and explicit start.","normalCustomerPath":"Use campaign state, MCP responses, and concise watchNarration. Do not create, read, link, or surface local draft files. Print the exact tokenized create_campaign.watchUrl only once at the initial brief handoff.","legacyCompatibility":{"validationSubskill":"create-campaign-v2-validation","tailSubskill":"create-campaign-v2-tail","rule":"Legacy validation/rehearsal files are opt-in diagnostics only and are not active flow gates."},"commitGateChoices":["approve","revise-brief","revise-leads","revise-rubric","revise-messaging","abort"],"canonicalStateFields":["campaignId","watchUrl","campaignBrief","currentStep","watchNarration","interactionMode","providerSearchAssociation","selectedLeadListId","workflowTableId","filterChoice","leadScoringRubrics","messageDraftRecommendation","approvedMessageTemplate","senderIds","sequenceTemplate","runningState"],"watchNarrationTransitionContract":{"rule":"Every watched currentStep switch must include fresh watchNarration.","requiredFields":["stage","headline","visibleState","agentIntent","nextAction"],"copyMustInclude":["what just happened","current visible page/state","next user action"],"appliesToTools":["create_campaign","update_campaign","attach_recommended_sequence","start_campaign"],"oneTimeWatchLinkPolicy":"After the initial brief handoff, customer-facing turns must not print another watch link unless the user asks or link recovery is needed."},"lazyReferences":{"watch":["references/watch-link-handoff.md","references/watch-guide-narration.md"],"source":["references/lead-validation-preview.md","references/step-13-import-leads.md"],"filter":["references/filter-leads.md"],"message":[],"tail":["references/sample-validation-loop.md","references/step-15-re-cascade.md","references/final-handoff-contract.md"]},"safetyBoundaries":["Do not call list_senders before Settings after message approval.","Do not import leads until Start Import is approved.","Do not queue cells until confirmed source rows exist in the campaign and the message/filter gates are satisfied.","Do not call start_campaign until the user explicitly confirms launch.","Do not use local files as durable state in normal customer runs."],"yoloMode":{"triggerPhrases":["yolo","--yolo","mode=yolo","autopilot","use best guesses","use best estimates","answer for me","just run it","yolo autopilot"],"identityRequestOnlyWhenMissing":true,"operatorDirectionsRule":"Treat freeform directions supplied at invocation or later as durable operator directions; newest conflicting direction wins.","setInteractionModeAfterCampaignCreate":"autonomous","autoSelectsPreLaunchChoices":["campaign focus","brief approval","source plan","Start Import approval","filters vs skip filters","filter rubric approval","message template approval when recommendation is approve-message","generated message review when quality floor passes","sender selection when exactly one connected sender is safe"],"mustPauseFor":["missing LinkedIn profile URL or handle","missing credentials or required data","ambiguous choice with no reasonable estimate","source/message/filter quality floor failure","final live launch confirmation"],"neverAutoStart":true},"steps":[{"id":"bootstrap","label":"Bootstrap","onEnter":[{"tool":"bootstrap_create_campaign","requiredValues":{"flowVersion":"v2"}},{"tool":"get_subskill_prompt","requiredValues":{"subskillName":"create-campaign-v2"}}],"allowedTools":["bootstrap_create_campaign","get_auth_status","get_active_workspace","get_subskill_prompt","AskUserQuestion","request_user_input"],"doNotAllow":["create_campaign","list_senders","save_rubrics","import_leads","confirm_lead_list","update_campaign","queue_cells","start_campaign"],"waitFor":"bootstrap_complete","transitions":{"bootstrap_complete":"brief-interview"}},{"id":"brief-interview","label":"Client, offer, research, and brief","onEnter":[{"action":"resolve_campaign_identity_before_strategy_packet","allowedTools":["fetch_company","fetch_linkedin_profile","WebFetch","WebSearch"],"mustNotInferFromNameOnly":true,"doNotPresentSenderPickerBeforeIdentityInference":true,"fallback":"Ask only: \"What is your LinkedIn profile URL or handle?\" Normalize bare handles or `/in/...` paths to `https://www.linkedin.com/in/{handle}/`. If the input is not a profile, ask again for the person profile URL before strategy questions.","requiresLinkedInProfileUrl":true,"doNotAcceptAsIdentityInput":["non-profile URL","company page","company/name-only input without a profile handle"],"acceptsLinkedInProfileHandle":true,"normalizeLinkedInProfileHandleToUrl":true},{"action":"run_campaign_identity_research_before_strategy_packet","target":"research-sender","requiredCompletion":"complete_sender_research","allowedTools":["get_subskill_prompt","fetch_linkedin_profile","fetch_company","fetch_linkedin_posts","fetch_company_posts","WebFetch","WebSearch","complete_sender_research"],"requiredInput":"LinkedIn profile URL or handle"},{"action":"confirm_target_before_strategy_packet","uses":"request_user_input","singleChoice":true,"question":"Who should we target first?","options":["Use Sellable's researched recommendation","Choose a different buyer","I'm not sure yet"],"copyMustInclude":["research basis","recommendation is editable","who to target"],"requiredOption":"Other / custom","neverCollectOpenTextWithStructuredQuestion":true,"skipIf":"user already supplied a clear target or yolo_mode with any reasonable best-estimate target"},{"action":"confirm_current_offer_before_strategy_packet","uses":"request_user_input","question":"What should we pitch to prospects first?","options":["Use Sellable's researched recommendation","Use my current pitch","Shape the pitch together"],"copyMustInclude":["research basis","public LinkedIn research may be stale","recommendation is editable","pitch to prospects"],"skipIf":"user already supplied a clear current offer or yolo_mode with any reasonable researched recommendation"},{"action":"confirm_trust_proof_before_strategy_packet","uses":"request_user_input","singleChoice":true,"question":"What is the strongest credibility signal we can lead with?","options":["Use Sellable's recommended proof","Use customer proof or a case study","Use founder or company credibility"],"copyMustInclude":["build trust with prospects","recommendation is editable","proof to use or avoid"],"requiredOption":"Other / custom","skipIf":"user already supplied clear proof to use or avoid, or yolo_mode with any reasonable researched proof"},{"action":"choose_prospect_source_path_before_strategy_packet","uses":"request_user_input","singleChoice":true,"question":"How should we find prospects?","options":["Find prospects for me","Use a CSV of LinkedIn profiles","Use a CSV of company domains"],"copyMustInclude":["prospects","no one added yet","find prospects for me"],"skipIf":"user already supplied a source path or yolo_mode with any reasonable source path"},{"action":"infer_strategy_packet_in_yolo_mode","when":"yolo_mode","skipStructuredSetupQuestions":true,"inferFields":["buyer segment","offer/CTA","proof to use or avoid","lead source","filter choice","message direction"],"sourceOfTruth":"identity/company lookup plus operator directions","mustStateAssumptionsInBrief":true},{"action":"render_supplied_setup_receipt_before_brief_when_setup_questions_skip","when":"any setup question is skipped because the user already supplied or yolo mode inferred identity, target, offer, proof, or source","output":"normal chat receipt before the campaign brief or watch-link handoff","copyMustInclude":["Accepted LinkedIn input: normalized to the required LinkedIn profile URL.","Offer path: supplied current offer or Sellable's researched recommendation; you can replace it with your current offer."],"copyMustNotInclude":["Campaign Identity"]},{"action":"create_watchable_campaign_shell_with_v1_brief","tool":"create_campaign","requiredFields":["name","campaignBrief","clientProspectId or senderLinkedinUrl","currentStep","watchNarration"],"requiredValues":{"currentStep":"create-offer","watchNarration.stage":"brief"},"capture":["campaignId","watchUrl"],"canonicalStateWrites":["campaignId","watchUrl","campaignBrief","currentStep:create-offer"]},{"action":"surface_campaign_shell_watch_link","watchUrlSource":"create_campaign.watchUrl","requiredWatchUrlShape":"exact create_campaign.watchUrl; direct /campaign-builder/{campaignId}?mode={claude|codex} URL with workspaceId and token","copyMustInclude":["We'll keep building the campaign in this chat.","You can watch it being built in real time in the app here:","I'll ask for your approval whenever I need your expertise or taste before moving forward.","Send changes here."],"immediateNextMainChatLine":"Ask for brief approval now. After approval, say: \"Brief approved. Next, we'll choose where to find buyers. I won't add anyone yet.\"","codexBrowserHandoff":{"openWhenAvailable":false,"printWatchLinkOnly":true,"tellUserCommandEnterOrClick":false,"mustNotUseBrowserAutomation":true,"fallbackMustNotClaimInspection":true},"copyMustAvoid":["bare /campaign-builder/{campaignId} URL","derived watch URL","second Watch link:","Next, we'll choose where to find buyers"],"oneTimeOnly":true}],"allowedTools":["get_subskill_prompt","fetch_company","fetch_company_posts","fetch_linkedin_profile","fetch_linkedin_posts","complete_sender_research","create_campaign","AskUserQuestion","request_user_input"],"doNotAllow":["list_senders","save_rubrics","import_leads","confirm_lead_list","queue_cells","start_campaign"],"waitFor":["campaign_shell_created","brief_ready","confirm_with_user"],"transitions":{"campaign_shell_created":"brief-review","brief_ready":"brief-review","confirm_with_user":"brief-review"}},{"id":"brief-review","label":"Brief review","onEnter":[{"action":"render_brief_inline","minimumVisibleDetail":"campaign direction, buyer, offer, proof, source plan, safety gates","copyMustInclude":["This brief is based on Sellable's research and your answers.","If your site or LinkedIn is stale","Do you agree with this researched campaign direction?"],"copyMustNotInclude":["quoted first-message copy","Message Angle as final proof","This approval covers","does not approve adding people","does not approve scraping posts","selecting a sender","attaching a sequence","Campaign Identity","what should this campaign sell first","sell first","[from you]","[from LinkedIn]","[from website]","[from case study]","[Sellable recommendation]"],"messageHintRule":"If any message-shape hint remains, keep it as non-final bullets and say real examples come after leads are found and filtered.","approvalBoundaryRule":"Keep this gate positive and short. Do not list later steps this approval does not cover; ask approve/revise and then move to choosing where to find buyers.","sectionLabelRule":"Use ## Sender and Company for the person/company context; never render ## Campaign Identity.","skipIf":"the full brief was already rendered in the current shell-creation handoff before create_campaign returned","renderOnceRule":"The brief must be visible exactly once before approval. Do not render it again immediately after shell creation; append the one watch-link handoff and ask the approval question.","copyMustAvoid":["duplicate brief render after shell creation","second Watch link:"]},{"action":"ask_brief_choice","uses":"request_user_input","choices":["Approve brief","Revise brief","Approve + YOLO autopilot setup"],"skipIf":"yolo_mode; auto_continue after rendering assumptions unless brief quality floor fails","question":"Do you agree with this researched campaign direction?","autonomousChoice":"Approve + YOLO autopilot setup","choiceDescriptions":{"Approve + YOLO autopilot setup":"Use best guesses to finish setup and stop before final launch."}}],"requiredCampaignState":["campaignId","watchUrl","campaignBrief","currentStep"],"allowedTools":["AskUserQuestion","request_user_input","update_campaign"],"doNotAllow":["create_campaign","list_senders","import_leads","confirm_lead_list","queue_cells","start_campaign"],"waitFor":["user_brief_confirmed","revise_brief","auto_continue"],"transitions":{"user_brief_confirmed":"find-leads","revise_brief":"brief-interview","auto_continue":"find-leads"}},{"id":"find-leads","label":"Find leads","sourceSelectionFunnel":{"preScoutRecommendationGate":{"required":true,"label":"Find buyers plan approval","mustHappenBefore":["get_provider_prompt","search_signals","search_sales_nav","search_prospeo","fetch_post_engagers"],"show":["buyer groups or places we could check","best place to start","why the right buyers are likely to be there","what signs the next search will check","where to look next if the first place is too thin","what approval authorizes"],"approvalAuthorizes":"looking for the best places to find buyers; no one added yet","explanationMustInclude":["where the right buyers are already talking","why that place fits this campaign","enough likely prospects","what counts as a good sign","where to look next if thin","I won't add anyone yet","choose where to find buyers"],"yoloMode":{"autoApproveRecommendedLane":true,"mustShowAssumedChoice":true,"pauseIfConfidenceLow":true},"customerLanguage":{"readability":"grade-5","requiredHeading":"Find Buyers Plan","prefer":["place to look","best place to start","people to check","prospects","I won't add anyone yet"],"avoid":["lane","source scouting","provider","precision/scale tradeoff","evidence quality","pilot volume","ICP","workflow pain","lead-source scouting","not importing leads"],"termRule":{"buyers":"target market","peopleToCheck":"raw reactions/comments before fit is known","prospects":"likely usable people after fit","leads":"campaign rows"}},"questionOrder":"update_campaign to pick-provider, render ## Find Buyers Plan in normal chat without repeating the watch link, then open the structured approval question; never ask first or show the plan after approval","copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"],"approvalFreshnessRule":"Only an approval question opened after the visible Find Buyers Plan, or an explicit source choice made after that plan, can set source_lane_approved. Do not reuse brief approval or pre-plan provider state."},"defaultWhenSourceUnspecified":["signal-discovery","sales-nav-recent-active","sales-nav-general","prospeo"],"parallelAllowedOnlyWhen":["user explicitly requested source comparison"]},"onEnter":[{"tool":"update_campaign","requiredValues":{"currentStep":"pick-provider","watchNarration.stage":"find-leads","watchNarration.headline":"Review where to find buyers","watchNarration.visibleState":"The app is showing source selection before lead finding starts.","watchNarration.agentIntent":"Codex is explaining where it recommends looking first and where it will look next if that is too thin.","watchNarration.nextAction":"Approve where to look first or choose another place","watchNarration.safety":"Only deciding where to look. No one is added yet."}},{"action":"render_find_buyers_plan_inline","oneShot":true,"requiredPrecondition":"currentStep=pick-provider has been saved with update_campaign","mustHappenAfter":["update_campaign currentStep=pick-provider"],"mustHappenBefore":["ask_find_buyers_plan_choice","get_provider_prompt","search_signals","search_sales_nav","search_prospeo","fetch_post_engagers"],"output":"normal chat text before any request_user_input approval panel","requiredHeading":"Find Buyers Plan","requiredInlineFields":["plain-language buyer groups or places for this campaign","best place to start","why the right buyers are likely to be there","what signs the next search will check","where to look next if the first place is too thin","what approval authorizes"],"copyMustInclude":["Find Buyers Plan","best place to start","I won't add anyone","choose where to find buyers","I won't add anyone yet"],"copyMustAvoid":["lane","source scouting","provider","precision/scale tradeoff","evidence quality","pilot volume","ICP","workflow pain","lead-source scouting","not importing leads","Watch link:","campaign-builder URL","repeat the watch URL"],"approvalBoundary":"Explain what approval authorizes before asking: looking for the best places to find buyers; no one is added yet.","linkPolicy":"Do not include another watch link. The first brief handoff already gave the user the live app URL."},{"action":"ask_find_buyers_plan_choice","uses":"request_user_input","oneShot":true,"requiredPrecondition":"render_find_buyers_plan_inline completed in normal chat after pick-provider state was saved; no earlier approval can satisfy this","skipIf":"source_lane_approved was set by ask_find_buyers_plan_choice after the visible Find Buyers Plan, leadSourceProvider was explicitly chosen by the user after that plan, or yolo_mode auto-selected source_lane_approved after showing the assumed choice","choices":["Start with LinkedIn posts","Start with active LinkedIn profiles","Start with company/contact search","Choose a different place","Pause here"],"approvalChoiceLabelsByProvider":{"signal-discovery":"Start with LinkedIn posts","sales-nav":"Start with active LinkedIn profiles","prospeo":"Start with company/contact search"},"approvalState":"source_lane_approved","copyMustInclude":["Approve this plan for where to find buyers?","Start with LinkedIn posts"],"mustNotHappenBefore":["render_find_buyers_plan_inline"],"approvalStateRule":"Set source_lane_approved here after the visible plan; never from brief approval, generic approval, pre-plan provider state, or artifacts."},{"action":"persist_provider_search_step_before_search","tool":"update_campaign","requiredPrecondition":"source_lane_approved set after the visible Find Buyers Plan approval question","providerCurrentStepMap":{"signal-discovery":"signal-discovery","sales-nav":"sales-nav","prospeo":"prospeo"},"requiredValues":{"leadSourceType":"new","leadSourceProvider":"approved provider","currentStep":"provider-specific current step","watchNarration.stage":"find-leads","watchNarration.headline":"Searching the approved place","watchNarration.safety":"Looking only. No one is added yet."},"mustRunBefore":["search_signals","search_sales_nav","search_prospeo","fetch_post_engagers"]},{"action":"run_parent_thread_source_funnel","requiredPrecondition":"source_lane_approved after visible Find Buyers Plan approval","mode":"parent_thread_inline_sequential","sourceAgents":"forbidden in normal create-campaign","loadsOnlyActiveProviderPrompt":true,"defaultOrder":["signal-discovery","sales-nav-recent-active","sales-nav-general","prospeo"],"stopOnFirstViableUnlessComparisonRequested":true}],"requiredCampaignState":["campaignId","campaignBrief","currentStep"],"allowedTools":["get_provider_prompt","lookup_sales_nav_filter","search_sales_nav","search_prospeo","search_signals","select_promising_posts","fetch_post_engagers","fetch_company","fetch_linkedin_profile","load_csv_linkedin_leads","load_csv_domains","get_rows_minimal","update_campaign","AskUserQuestion","request_user_input","get_campaign_navigation_state"],"doNotAllow":["create_campaign","list_senders","save_rubrics","import_leads","confirm_lead_list","queue_cells","start_campaign","Task","spawn_agent"],"waitFor":["lead_review_ready","revise_brief","confirm_with_user"],"transitions":{"lead_review_ready":"lead-review","revise_brief":"brief-interview","confirm_with_user":"lead-review"}},{"id":"lead-review","label":"Start Import approval","onEnter":[{"action":"show_source_decision_card","requiredInlineFields":["primary source and exact filters/recipe","Start Import action awaiting approval","for Signal Discovery: compact Source Recommendation in plain language with selected posts, people to check, likely prospects, first review, and fallback","for Signal Discovery: recommended scrape post count and target people-to-check volume","first campaign review size, clearly separate from source sampling","runner-up and why it lost","raw volume","sampled people","sampled fits as n/N plus percentage/range","estimated usable prospects","cleanup risk","what happens after Start Import"],"copyMustAvoid":["lead-source scouting","source scouting","headline-fit","engagers needed","execution slice","ICP","good buyers","real buyers","This approval covers","It does not approve","does not approve filters","filters, messages, sender selection","sender selection, sequence, or sending","Watch link:","campaign-builder URL","repeat the watch URL"],"approvalBoundaryRule":"Use positive Start Import copy only. Include: \"After approval, I will build the source list, add it to the campaign, and review the first 15 leads before we scale.\" Never list future non-approvals.","linkPolicy":"Do not include another watch link in Source Recommendation; describe the Start Import decision only."},{"action":"ask_source_review_choice","uses":"request_user_input","choices":["Approve scraping N recommended LinkedIn posts","Start Import for the approved source list","Revise source","Pause here"],"approvalChoiceLabelsByProvider":{"signal-discovery":"Approve scraping {scrapePostCount} recommended LinkedIn posts?","sales-nav":"Start Import for the approved Sales Nav source list","prospeo":"Start Import for the approved Prospeo source list"},"postApprovalContract":{"singleUseApproval":true,"doNotRepeatAfterApproval":["Source Recommendation","show_source_decision_card","ask_source_review_choice"],"requiredNextActionAfterApproval":"ack once; call import_leads immediately","signalDiscoveryNextTool":"import_leads({ provider: \"signal-discovery\", targetEngagerCount, maxPostsToScrape, confirmed: true })"},"autoSelectIf":"yolo_mode and projected usable pool clears the source quality floor","copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"]}],"requiredCampaignState":["campaignId","campaignBrief","providerSearchAssociation"],"allowedTools":["AskUserQuestion","request_user_input","update_campaign"],"doNotAllow":["create_campaign","list_senders","save_rubrics","import_leads","confirm_lead_list","queue_cells","start_campaign"],"waitFor":["lead_review_confirmed","revise_leads","confirm_with_user","auto_continue"],"transitions":{"lead_review_confirmed":"auto-execute-leads","revise_leads":"find-leads","confirm_with_user":"auto-execute-leads","auto_continue":"auto-execute-leads"}},{"id":"auto-execute-leads","label":"Materialize confirmed source list","currentStepValue":"auto-execute-leads","reference":"references/step-13-import-leads.md","onEnter":[{"tool":"get_subskill_prompt","requiredValues":{"subskillName":"create-campaign-v2-tail"}},{"tool":"import_leads","requiredFields":["campaignOfferId","selected source/list","sourceListTarget or targetEngagerCount"],"requiredValues":{"salesNavProspeoDefaultSourceListTarget":1000,"signalDiscoveryDefaultEngagerTarget":1500},"modeAddHandshake":{"firstCallReturns":"needsModeSelection when adding to an existing campaign-attached list","requiredFollowup":"retry with mode=add after explicit user/source-state compatibility is confirmed"},"surfaceDedupRatio":true},{"tool":"wait_for_lead_list_ready","repeatUntil":"ready_true_or_cancelled_or_import_failed","requiredValues":{"requireComplete":true},"onImportStillRunning":"re-run wait_for_lead_list_ready; do not call confirm_lead_list just because rows exist","partialOverrideRule":"Only if the user explicitly asks to keep going early may the next confirm_lead_list call pass allowPartialSourceList: true."},{"tool":"confirm_lead_list","requiredFields":["campaignOfferId","selectedLeadListId","reviewBatchLimit"],"requiredValues":{"reviewBatchLimit":15},"capture":["workflowTableId","reviewBatchRowIds"],"requiredPrecondition":"wait_for_lead_list_ready returned ready:true, unless the user explicitly asked to keep going early with a partial list","partialOverrideField":"allowPartialSourceList:true only after explicit user early-continue instruction","mustNotRunWhen":["wait_for_lead_list_ready reason=import_still_running and no explicit early-continue instruction","wait_for_lead_list_ready reason=cancelled","wait_for_lead_list_ready reason=import_failed","missing import job metadata"]},{"tool":"wait_for_campaign_table_ready"},{"tool":"get_rows_minimal","requiredValues":{"tableId":"{workflowTableId}","limit":15},"capture":["reviewBatchRowHash"]},{"action":"summarize_review_batch_and_advance_to_filter_choice","maxCustomerCopyLines":4,"copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"],"linkPolicy":"Ask add filters vs skip filters without repeating the watch link.","purpose":"ask filter choice immediately"}],"requiredCampaignState":["campaignId","providerSearchAssociation","selectedLeadListId"],"allowedTools":["get_subskill_prompt","import_leads","wait_for_lead_list_ready","confirm_lead_list","wait_for_campaign_table_ready","get_rows_minimal","update_campaign","AskUserQuestion","request_user_input"],"doNotAllow":["get_post_find_leads_scout_registry","Task","spawn_agent","list_senders","queue_cells","start_campaign","enrich_with_prospeo","bulk_enrich_with_prospeo","save_rubrics"],"waitFor":"source_list_confirmed_and_review_sample_ready","transitions":{"source_list_confirmed_and_review_sample_ready":"filter-choice","escalation_triggered":"escalation"},"hardRules":["import_leads_then_repeated_wait_for_lead_list_ready_then_confirm_lead_list_then_wait_for_campaign_table_ready","partial_source_rows_do_not_unblock_confirm_lead_list_without_explicit_user_early_continue","cancelled_or_failed_source_import_stops_before_campaign_table_copy"]},{"id":"filter-choice","label":"Filter choice","currentStepValue":"filter-choice","onEnter":[{"tool":"update_campaign","requiredValues":{"currentStep":"filter-choice","watchNarration.stage":"fit-message"}},{"action":"ask_filter_choice","uses":"request_user_input","choices":["Use filters","Skip filters","Revise source"],"autoSelectIf":"yolo_mode; choose filters unless source is tightly curated or user directed otherwise","copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"],"copyMustInclude":["source rows are in the campaign","add fit filters before message drafting or skip filters"]}],"hardRules":["ask_filter_choice_immediately_after_review_batch_import","do_not_call_get_subskill_prompt_before_filter_choice","do_not_call_get_subskill_asset_before_filter_choice","do_not_call_get_post_find_leads_scout_registry_before_filter_choice","do_not_spawn_prospect_setup_before_filter_choice"],"requiredCampaignState":["campaignId","campaignBrief","selectedLeadListId","workflowTableId"],"allowedTools":["AskUserQuestion","request_user_input","update_campaign","get_campaign_navigation_state"],"doNotAllow":["get_subskill_prompt","get_subskill_asset","get_post_find_leads_scout_registry","Task","spawn_agent","create_campaign","list_senders","import_leads","confirm_lead_list","queue_cells","start_campaign","generate_messages"],"waitFor":["filters_enabled","filters_skipped","revise_leads"],"transitions":{"filters_enabled":"prospect-setup","filters_skipped":"message-generation","revise_leads":"find-leads"}},{"id":"prospect-setup","label":"Prospect setup","onEnter":[{"action":"persist_add_filters_approval","tool":"update_campaign","when":"filters_enabled","requiredFields":["campaignId","enableICPFilters","currentStep","watchNarration"],"requiredValues":{"enableICPFilters":true,"currentStep":"create-icp-rubric","watchNarration.stage":"fit-message","watchNarration.headline":"Create filter rules","watchNarration.visibleState":"Filter Rules is visible while Prospect Filters saves rubrics.","watchNarration.nextAction":"Review saved filter rules"},"mustRunBefore":["get_post_find_leads_scout_registry","launch_prospect_filters_after_filter_choice","launch_message_drafting_after_filter_choice","wait_for_prospect_filters_and_verify_save_rubrics","ask_filter_rubric_review_choice"]},{"tool":"get_post_find_leads_scout_registry","returns":["post-find-leads-filter-scout","post-find-leads-message-scout"],"rule":"normal filters path launches these two post-import workers only; skip-filter path launches Message Drafting only"},{"action":"launch_prospect_filters_after_filter_choice","mode":"background_agent_when_filters_enabled","target":"post-find-leads-filter-scout","when":"filters_enabled after filter-choice answer","doesNotQueueCells":true,"parentPriority":"wait_for_this_worker_first","inputs":["campaignId","campaignName","briefSummary/campaignBrief","sourceSummary","selectedLeadListId","workflowTableId","copiedRowCount","reviewBatchRows or count/hash","filterChoice"],"requiredOutput":["save_rubrics receipt or leadScoringRubrics for parent fallback","filter summary","pass/block examples","status"],"forbiddenSiblings":["source-scout-*"],"handoffCompression":"campaign_basis_plus_review_sample"},{"action":"launch_message_drafting_after_filter_choice","mode":"background_agent_when_host_supports_subagents","target":"post-find-leads-message-scout","when":"filters_enabled after Prospect Filters launched, or filters_skipped after filter-choice answer","doesNotQueueCells":true,"customerNarration":"Message Drafting is preparing while Prospect Filters saves filters.","inputs":["campaignId","campaignName","briefSummary","sourceSummary","selectedLeadListId","workflowTableId","copiedRowCount","reviewBatchCount/hash","filterChoice"],"forbiddenSiblings":["source-scout-*"],"runsInParallelWith":["post-find-leads-filter-scout when filters_enabled"],"handoffCompression":"compact_basis_no_full_row_ids_or_row_data"},{"action":"wait_for_prospect_filters_and_verify_save_rubrics","when":"filters_enabled","target":"post-find-leads-filter-scout","parentPriority":"do_this_before_waiting_for_message_drafting","requiredBefore":["ask_filter_rubric_review_choice","message-review"],"savePolicy":"Require saved rubrics. If worker saved them, verify. If it returned production rubrics, parent calls save_rubrics. Otherwise block before message review.","requiredStateAfter":["leadScoringRubrics"],"toolFallback":"save_rubrics"},{"action":"ask_filter_rubric_review_choice","uses":"request_user_input","choices":["Approve filters","Revise filters","Pause"],"copyMustInclude":["Filter rules saved for review","These rules prevent wasted sends before I score/import the list.","Recommended because of the researched ICP, source sample, and repeated false-positive patterns.","one pass example and one block example when available","approval is for the saved filter rules","Approve these filter rules."],"autoSelectIf":"yolo_mode and Prospect Filters saved production-shaped rubrics","copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"],"requiredPrecondition":"save_rubrics succeeded or active campaign rubrics verified"}],"requiredCampaignState":["campaignId","campaignBrief","selectedLeadListId","workflowTableId"],"allowedTools":["get_post_find_leads_scout_registry","save_rubrics","get_campaign","get_rows_minimal","update_campaign","get_campaign_navigation_state","AskUserQuestion","request_user_input","Task","spawn_agent"],"doNotAllow":["create_campaign","list_senders","import_leads","confirm_lead_list","queue_cells","start_campaign","check_rubric","generate_messages"],"waitFor":["filter_rubrics_approved","revise_leads","revise_rubric","revise_messaging"],"hardRules":["after_save_rubrics_currentStep_must_stay_create-icp-rubric_until_filter_approval","filter_approval_required_before_apply-icp-rubric_or_queue_campaign_cells","do_not_move_browser_to_messages_until_filter_leads_step_is_current_or_filters_are_explicitly_skipped","no_prospect_setup_worker_or_deep_prompt_before_filter_choice","prospect_filters_start_after_filter_choice_when_filters_enabled","message_drafting_after_filter_choice","message_drafting_no_cells","parent_waits_for_prospect_filters_before_message_join","save_rubrics_success_required_before_filter_approval","only_post_import_workers_may_spawn_agents"],"transitions":{"filter_rubrics_approved":"message-generation","revise_leads":"find-leads","revise_rubric":"filter-rubric","revise_messaging":"message-generation","confirm_with_user":"message-review"}},{"id":"filter-rubric","label":"Rubric revision","onEnter":[{"tool":"get_subskill_asset","requiredValues":{"subskillName":"create-campaign-v2","assetPath":"references/filter-leads.md"}},{"tool":"save_rubrics","requiredFields":["campaignOfferId","leadScoringRubrics"],"writesCampaignState":"leadScoringRubrics","requiredSideEffects":{"enableICPFilters":true,"currentStep":"create-icp-rubric","watchNarration.headline":"Filter rules saved for review"}},{"action":"ask_filter_rubric_review_choice","uses":"request_user_input","choices":["Approve filters","Revise filters","Pause"],"copyMustInclude":["These rules prevent wasted sends before I score/import the list.","approval object is filter rules only","approval is for the filter rules","Approve these filter rules."],"autoSelectIf":"yolo_mode and rubrics are production-shaped","copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"]}],"requiredCampaignState":["campaignId","workflowTableId"],"allowedTools":["get_subskill_asset","save_rubrics","AskUserQuestion","request_user_input"],"doNotAllow":["create_campaign","list_senders","import_leads","confirm_lead_list","update_campaign","queue_cells","start_campaign","check_rubric"],"waitFor":["filter_rubrics_approved","revise_leads","confirm_with_user","auto_continue"],"transitions":{"filter_rubrics_approved":"message-generation","revise_leads":"find-leads","confirm_with_user":"message-generation","auto_continue":"message-generation"}},{"id":"message-generation","label":"Message drafting","onEnter":[{"action":"set_message_review_visible_step_by_filter_choice","tool":"update_campaign","branchRules":["yes after filter approval: currentStep=apply-icp-rubric; wait on Filter Leads","no: currentStep=messages; message review"],"watchNarration.stage":"fit-message"},{"action":"join_or_recover_message_drafting","target":"post-find-leads-message-scout","toolCallRequiredBeforeDraft":["join already-running Message Drafting when prospect-setup launched it","run Message Drafting via post-find-leads-message-scout only if missing; yolo counts","handoff compact basis only: campaign/name, brief summary, source summary, selectedLeadListId, workflowTableId, copiedRows, reviewBatch count/hash, filterChoice","do not paste full reviewBatchRowIds, row data, or local debug artifacts into the spawn prompt","branch loads current brief/table rows, full generate-messages prompt, validation/QA assets","run validation/QA pass before concise review-ready output","if no yolo and permission missing, ask once before parent inline fallback","use generic gpt-5.5 xhigh Message Drafting agent if named unavailable"],"stateSource":"message branch-loaded live campaign/table state; selectedLeadListId, workflowTableId, reviewBatch count/hash","outputState":"messageDraftRecommendation with qaReceipt","revisionMode":{"when":"revise_messaging, message QA request, or latest user message requests template changes before approve-message","route":"send feedback/QA/rewrite request to post-find-leads-message-scout or generic gpt-5.5 xhigh Message Drafting branch","requiredInputs":["latest user feedback","current messageDraftRecommendation and basisToken/outputHash","compact campaign/table basis and brief summary; branch reloads live state"],"forbiddenParentActions":["rewrite template in parent","QA template in parent from memory","update_campaign_brief before approve-message"],"output":"revised messageDraftRecommendation rendered before request_user_input"},"onlyMessageBackgroundAgentOnSkipPath":true,"runsInParallelWith":["post-find-leads-filter-scout only during filters_enabled prospect setup"],"forbiddenSiblingAgents":["source-scout-*"],"handoffCompression":"compact_basis_only_no_full_row_id_list"}],"requiredCampaignState":["campaignId","campaignBrief","selectedLeadListId","workflowTableId"],"allowedTools":["get_subskill_prompt","get_campaign","get_rows_minimal","update_campaign","Task","spawn_agent"],"toolRules":["message_drafting_agent_first_no_silent_parent_fallback; yolo counts","on_filters_enabled_path_parent_must_not_enter_message_review_until_save_rubrics_success_and_filter_approval","brief.md, lead-review.md, and lead-sample.json are optional debug context only.","messageDraftRecommendation returns templateRecommendation, tokenFillRules, renderedSample, concerns, status, basisToken, outputAt, outputHash, and error/retry detail.","If campaign/source/table/execution-slice basis does not match, classify the output stale or blocked.","message_revision_feedback_routes_to_message_drafting_agent_not_parent","message_qa_routes_to_message_drafting_agent_not_parent","message_drafting_loads_validation_assets","message_drafting_handoff_uses_compact_basis_no_full_row_id_list","message_drafting_loads_current_campaign_brief_before_drafting","parent_may_load_generate_messages_only_for_explicitly_approved_inline_fallback"],"doNotAllow":["create_campaign","list_senders","save_rubrics","import_leads","confirm_lead_list","queue_cells","start_campaign","generate_messages","AskUserQuestion","request_user_input"],"waitFor":["message_validation_ready","revise_rubric","revise_messaging"],"transitions":{"message_validation_ready":"message-review","revise_rubric":"filter-rubric","revise_messaging":"message-generation"},"revisionLoop":{"trigger":"user gives copy feedback, asks for QA/update, or chooses revise-messaging","required":["route latest user feedback or QA request to Message Drafting with current recommendation and live campaign/table state","produce a new messageDraftRecommendation with revisionNotes or qaReceipt","render revised ## Message Template, ## Rendered Example, and What changed before asking approval"],"blocked":["asking whether an unseen new version is better","reusing the old template after acknowledging feedback","rewriting the template in the parent thread","update_campaign_brief before approve-message","request_user_input or AskUserQuestion during message-generation","QAing the template in the parent thread"],"outputState":"messageDraftRecommendation"},"hardRules":["message_revision_must_render_new_template_before_approval_question","message_revision_saves_only_after_approve_message","message_generation_must_not_call_request_user_input","message_generation_must_transition_to_message_review_only_after_renderable_draft_state","message_drafting_agent_first_no_silent_parent_fallback","message_revision_routes_to_message_drafting_agent","message_qa_routes_to_message_drafting_agent","filter_path_requires_saved_filters_before_message_review"]},{"id":"message-review","label":"Message review","onEnter":[{"action":"render_message_review_from_state","requiredState":["campaignBrief","selectedLeadListId","workflowTableId","messageDraftRecommendation"],"requiredVisibleLabels":["## Message Template","## Rendered Example","Good token fill:","My take:","Question: approve-message or revise-messaging?","Recommendation:","Message Drafting QA:"],"doNotShowByDefault":["Token Notes","Good omit / fallback","Bad fill to avoid","Token Adherence Table"],"internalPersistenceOnly":["Token Fill Rules","Token Fill Examples","fallback guidance","bad-fill avoidance notes"],"copyMustInclude":["QA receipt before approval","tokens resolved","company/person match","proof claim","prospect angle","language/tone when known","obvious bad fits","Would I take this call?","weak personalization can burn the sender's reputation","full list remains paused","Message Drafting QA receipt"],"copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"],"revisionRenderRule":"After revise_messaging, a message QA request, or user copy feedback, route feedback to Message Drafting and show revised template, rendered example, QA receipt, and What changed before ask_message_review_choice; never ask if an unseen version is better.","parentMustNot":["rewrite template from memory","invent QA receipt without Message Drafting output"]},{"action":"ask_message_review_choice","uses":"request_user_input","choices":["approve-message","revise-messaging"],"copyMustInclude":["approve this message template and continue","nothing sends from this approval"],"autoSelectIf":"yolo_mode and Recommendation is approve-message; revise autonomously when Recommendation is revise-messaging","copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"],"requiredPrecondition":"current or revised template and rendered example are visible in this turn","revisionPrecondition":"after revise_messaging, revised template is visible before this question"},{"action":"sync_approved_message_set_to_campaign_brief","tool":"update_campaign_brief","when":"after approve-message","requiredFields":["campaignId","approvedMessageTemplate","Token Fill Rules","Token Fill Examples"],"writesCampaignState":"approvedMessageTemplate","revisionApprovalRule":"If the user requested revisions, write only the latest Message Drafting-approved revised template and token rules after approve-message.","forbiddenBefore":"approve-message"}],"requiredCampaignState":["campaignId","workflowTableId","messageDraftRecommendation"],"allowedTools":["AskUserQuestion","request_user_input","update_campaign_brief","update_campaign","get_rows_minimal"],"doNotAllow":["create_campaign","list_senders","save_rubrics","import_leads","confirm_lead_list","start_campaign"],"waitFor":["message_approved","revise_messaging"],"transitions":{"message_approved":"validate-sample","revise_messaging":"message-generation"}},{"id":"validate-sample","label":"Validate campaign-table execution slice","currentStepValue":"apply-icp-rubric","reference":"references/sample-validation-loop.md","visibleStepRule":"Filter Leads is reached only after saved-filter approval; on approve-message, save the template and queue bounded Enrich Prospect cells through selector-based campaign processing.","onEnter":[{"tool":"update_campaign","requiredValues":{"currentStep":"apply-icp-rubric","watchNarration.stage":"fit-message"},"watchNarrationRule":"Say the template is saved and Filter Leads is now running the bounded enrichment and scoring pass."},{"tool":"queue_campaign_cells","requiredFields":["workflowTableId","columnRole","rowSelector"],"requiredValues":{"columnRole":"enrich","rowSelector":{"type":"reviewBatch"},"forceRerun":false},"targetCountSource":"WorkflowTable.config.mcp.reviewBatch.rowCount (default 15)"},{"tool":"wait_for_campaign_processing","requiredFields":["workflowTableId","minPassedCount"],"requiredValues":{"minPassedCount":1},"readVia":"stats_only_tool_result","timeoutGuidance":"If this times out, do not repoll identical args; use partial diagnostics to revise filters or surface blocked sample state.","customerSummaryPattern":["{checked} leads checked","{passed} passed fit scoring","{blocked} blocked before sending","full list remains paused until approval"]},{"action":"handle_partial_or_timeout_sample","customerStatus":"sample-needs-revision","rule":"Do not repeat waits indefinitely; surface partial status and route to revision.","copyMustInclude":"completed, passed, pending, blocked before sending, and full list remains paused"}],"requiredCampaignState":["campaignId","workflowTableId","approvedMessageTemplate","filterRubricsApproved when enableICPFilters=true"],"allowedTools":["get_subskill_asset","queue_campaign_cells","select_campaign_cells","get_campaign_table_schema","wait_for_campaign_processing","update_campaign","AskUserQuestion","request_user_input"],"doNotAllow":["import_leads","list_senders","start_campaign","enrich_with_prospeo","bulk_enrich_with_prospeo","check_rubric"],"hardRules":["campaign_processing_waits_are_stats_only_by_default","queue_review_batch_by_selector_never_fetch_rows_for_cell_ids","timeout_never_repeats_without_customer_handoff","timeout_or_underfloor_sample_never_advances_to_settings"],"waitFor":["sample_validated","sample_revision_required"],"transitions":{"sample_validated":"auto-execute-messaging","sample_revision_required":"lead-review","revise_leads":"find-leads","revise_rubric":"filter-rubric","escalation_triggered":"escalation"}},{"id":"auto-execute-messaging","label":"Generate initial campaign-row messages","currentStepValue":"auto-execute-messaging","references":["references/parallel-critique-protocol.md","references/thomas-variant-selection.md","references/thomas-revision-filters.md","references/step-15-re-cascade.md"],"onEnter":[{"tool":"queue_campaign_cells","batchSize":100,"when":"any passing row has pending, empty, or stale Generate Message cell","requiredFields":["workflowTableId","columnRole","rowSelector"],"requiredValues":{"columnRole":"generateMessage","rowSelector":{"type":"needsGeneratedMessage"},"forceRerun":false},"cellSource":"selector-engine needsGeneratedMessage rows using approved campaign brief template"},{"tool":"wait_for_campaign_processing","purpose":"wait_for_first_current_revision_generated_message","requiredValues":{"minGeneratedMessages":1,"templateRevision":"current"},"readVia":"stats_only_tool_result","invariant":"A generated message implies the pass gate; stale or wrong-revision messages are excluded from readiness."},{"action":"observe_generate_message_results","reference":"references/step-15-re-cascade.md"},{"action":"enforce_token_contract","modeFromConfig":"messaging.tokenContract"},{"action":"optional_critique_pass","enabledFromConfig":"messaging.critique.enabled","plan":"85-03","protocol":"references/parallel-critique-protocol.md","sampleSizeFromConfig":"messaging.critique.sampleSize","budgetUsdCapFromConfig":"messaging.critique.budgetUsdCap","perCriticTimeoutFromConfig":"messaging.critique.perCriticTimeoutSeconds","totalTimeoutFromConfig":"messaging.critique.totalTimeoutSeconds","criticsFromConfig":"messaging.critique.critics","enforceFinalizerPassFromConfig":"messaging.critique.synthesis.enforceFinalizerPass","rejectOnFakeProofFromConfig":"messaging.critique.rejectOnFakeProof","rejectOnUnsupportedTokenFromConfig":"messaging.critique.rejectOnUnsupportedToken","onBudgetTrip":"halt_critique_continue_plain_tail","onPerCriticTimeout":"drop_critic_proceed_with_remaining","onTotalTimeout":"persist_plain_message_for_row","onTokenContractViolation":"persist_plain_message_for_row","onFakeProof":"persist_plain_message_for_row"},{"action":"optional_opus_subset","enabledFromConfig":"messaging.critique.opus.enabled","selection":"references/thomas-variant-selection.md","maxMessagesPerPassFromConfig":"messaging.critique.opus.maxMessagesPerPass","budgetUsdCapFromConfig":"messaging.critique.opus.budgetUsdCap","onOpusBudgetTrip":"halt_opus_continue_non_opus","onOpusTokenContractViolation":"fallback_to_non_opus_rewrite"},{"tool":"update_campaign","requiredValues":{"currentStep":"auto-execute-messaging","watchNarration.stage":"review-ready"},"watchNarrationRule":"Say the first passing generated message is ready in Messages; next is review before Settings."},{"action":"ask_generated_message_review_choice","uses":"request_user_input","choices":["Approve reviewed draft rows and continue to Settings","Revise filters","Revise message template","Pause here"],"copyMustInclude":["approve the reviewed draft rows and continue to Settings","tokens resolved","company/person match","proof claim","prospect angle","language/tone when known","obvious bad fits","Would I take this call?","weak personalization can burn the sender's reputation","full list remains paused","nothing sends from this approval"],"autoSelectIf":"yolo_mode and the first passing generated message clears the quality floor","copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"]}],"allowedTools":["get_subskill_asset","get_campaign_table_schema","select_campaign_cells","queue_campaign_cells","wait_for_campaign_processing","revise_message_template_and_rerun","update_campaign","AskUserQuestion","request_user_input"],"doNotAllow":["import_leads","list_senders","start_campaign","generate_messages"],"hardRules":["generated_message_count_excludes_stale_or_wrong_revision_messages","message_template_revision_uses_brief_update_then_generate_message_rerun","do_not_overwrite_generated_message_cells_directly","critique_failure_never_escalates","critique_sample_size_bounded_by_config","first_passing_generated_message_unblocks_review","critics_fixed_at_targeting_copy_voice","synthesis_enforces_phase_84_token_contract","opus_reserved_for_highest_value_subset","proposed_token_never_persisted_in_rewrite"],"waitFor":["generated_messages_approved","sample_revision_required"],"transitions":{"generated_messages_approved":"awaiting-user-greenlight","revise_filters":"filter-rubric","revise_messaging":"message-generation","escalation_triggered":"escalation"}},{"id":"awaiting-user-greenlight","label":"Settings, sender, sequence, and greenlight","currentStepValue":"awaiting-user-greenlight","reference":"references/final-handoff-contract.md","onEnter":[{"tool":"get_campaign"},{"tool":"list_senders","purpose":"surface available connected senders only at Settings"},{"tool":"update_campaign","requiredValues":{"currentStep":"settings","watchNarration.stage":"review-ready"},"watchNarrationRule":"Say message review is complete and the app is on Settings for sender choice. Mention lead research/filtering already happened outside the user's LinkedIn account and nothing sends until Start."},{"action":"surface_sender_and_slack_handoff","requiredVisibleContent":["connect or select a LinkedIn sender","Lead research and filtering already happened outside your LinkedIn account.","This account is only used for approved sending after final launch.","Nothing sends until the final Start step.","Slack reply review","recommended sequence","final launch confirmation is still ahead"],"copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"]},{"action":"ask_sender_selection","uses":"request_user_input","singleChoice":true,"choices":["Use this connected sender","Connect a different sender in Settings","Pause here"],"autoSelectIf":"yolo_mode and exactly one safe connected sender is available","copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"]},{"action":"attach_selected_sender","tool":"update_campaign","when":"user selected an available connected sender","requiredValues":{"senderIds":["{selectedSenderId}"],"currentStep":"sequence","watchNarration.stage":"review-ready"},"watchNarrationRule":"Say the sender was attached and the app is moving to Sequence review; sequence remains editable and nothing sends until Start."},{"tool":"attach_recommended_sequence","when":"after senderIds are attached","requiredValues":{"campaignId":"{campaignId}","currentStep":"send","watchNarration.stage":"review-ready"},"watchNarrationRule":"The sequence tool owns this visible beat: say the recommended follow-up sequence is attached, the app is on final launch review, and it is still not sending until Start."},{"action":"ask_final_launch_greenlight","uses":"request_user_input","singleChoice":true,"choices":["Start campaign","Review campaign first","Pause here"],"copyMustInclude":["quality confidence means sample messages and prospect angle were checked","launch confidence means sender, sequence, and Start are ready","approved messages begin sending according to the sequence","replies and meetings follow connected settings","you can monitor and pause","no hidden extra approval disappears after clicking Start"],"onUserStart":"claude-greenlight","neverAutoSelectInYolo":true,"copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"]}],"allowedTools":["get_campaign","get_campaign_navigation_state","list_senders","update_campaign","attach_recommended_sequence","AskUserQuestion","request_user_input"],"doNotAllow":["start_campaign","import_leads"],"autoStart":false,"watchRequired":true,"waitFor":["sender_connection_required","sender_attached","sequence_attached","ready_to_launch","user_greenlight","ui_start_detected"],"transitions":{"sender_connection_required":"awaiting-user-greenlight","sender_attached":"awaiting-user-greenlight","sequence_attached":"awaiting-user-greenlight","ready_to_launch":"awaiting-user-greenlight","user_greenlight":"claude-greenlight","ui_start_detected":"running"}},{"id":"claude-greenlight","label":"Explicit launch","reference":"references/final-handoff-contract.md","onEnter":[{"tool":"get_campaign"},{"action":"verify_sequence_and_current_step_before_start","requiredState":["workflowTableId","senderIds","sequenceTemplate","at least one approved generated message","currentStep in awaiting-user-greenlight|claude-greenlight|send"]},{"tool":"start_campaign","requiredFields":["campaignId"],"persistsCurrentStep":"running","watchNarrationRule":"After start_campaign succeeds, the running state must say the final greenlight was accepted, the campaign is now live/running, and the user can watch progress from the campaign."}],"allowedTools":["get_campaign","attach_recommended_sequence","start_campaign","AskUserQuestion","request_user_input"],"watchRequired":true,"waitFor":"campaign_started","transitions":{"campaign_started":"running"}},{"id":"running","label":"Campaign is live","currentStepValue":"running","onEnter":[{"action":"surface_campaign_live_confirmation_without_watch_link","copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"]}],"allowedTools":["get_campaign","AskUserQuestion","request_user_input"],"doNotAllow":["start_campaign"],"terminal":true},{"id":"escalation","label":"Escalation","reference":"references/escalation-ladder.md","allowedTools":["AskUserQuestion","request_user_input"],"doNotAllow":["start_campaign","import_leads"],"transitions":{"revise_brief":"brief-interview","revise_leads":"find-leads","revise_rubric":"filter-rubric","revise_messaging":"message-generation","abort":"abort"}},{"id":"abort","label":"Abort","allowedTools":["AskUserQuestion","request_user_input"],"terminal":true}]}
@@ -36,7 +36,7 @@ Step 13 — Start Import + confirm initial campaign slice
36
36
 
37
37
  Post-import main thread
38
38
  launch Message Drafting after the filter-choice answer
39
- launch Prospect Filters only after user chooses filters
39
+ keep filter/rubric work inline in the parent thread when the user chooses filters
40
40
  save_rubrics({ campaignOfferId, leadScoringRubrics }) after the campaign table exists
41
41
  keep the watched app on Filter Leads after rubrics are saved
42
42
  while on Filter Leads, show the message template recommendation from background Message Drafting