@sellable/mcp 0.1.207 → 0.1.208
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 +16 -28
- package/agents/post-find-leads-message-scout.md +41 -23
- package/dist/tools/prompts.d.ts +1 -1
- package/dist/tools/prompts.js +19 -16
- package/package.json +1 -1
- package/skills/create-campaign/SKILL.md +47 -62
- package/skills/create-campaign-v2/SKILL.md +27 -21
- package/skills/create-campaign-v2/SOUL.md +30 -30
- package/skills/create-campaign-v2/core/flow.v2.json +1 -1
- package/skills/create-campaign-v2-tail/SKILL.md +1 -1
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
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
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
|
-
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
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
|
|
|
@@ -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` /
|
|
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
|
|
29
|
-
|
|
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.
|
|
42
|
-
|
|
43
|
-
|
|
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
|
|
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
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
116
|
-
treat it as
|
|
117
|
-
`messageDraftRecommendation`, basis token/hash,
|
|
118
|
-
latest user feedback as inputs. Load or reuse the full
|
|
119
|
-
`generate-messages` contract, then return a revised
|
|
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
|
|
package/dist/tools/prompts.d.ts
CHANGED
|
@@ -86,7 +86,7 @@ export interface SourceScoutRegistryResponse {
|
|
|
86
86
|
}
|
|
87
87
|
export interface PostFindLeadsScoutRegistryResponse {
|
|
88
88
|
version: number;
|
|
89
|
-
trigger: "
|
|
89
|
+
trigger: "message_drafting_after_confirm_lead_list";
|
|
90
90
|
requiredInputs?: string[];
|
|
91
91
|
scouts: Array<{
|
|
92
92
|
id: string;
|
package/dist/tools/prompts.js
CHANGED
|
@@ -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
|
|
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
|
|
160
|
+
description: "Return the canonical Sellable Message Drafting worker name after Start Import copies confirmed source rows. Prospect filters stay inline in the parent thread in normal create-campaign runs.",
|
|
161
161
|
inputSchema: {
|
|
162
162
|
type: "object",
|
|
163
163
|
properties: {},
|
|
@@ -257,25 +257,26 @@ export function getSourceScoutRegistry() {
|
|
|
257
257
|
legacy: agent.legacy,
|
|
258
258
|
})),
|
|
259
259
|
usage: {
|
|
260
|
-
codex: "Source finding is
|
|
261
|
-
claude: "Source finding is
|
|
262
|
-
parentThreadRule: "
|
|
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
|
}
|
|
266
266
|
export function getPostFindLeadsScoutRegistry() {
|
|
267
267
|
const registry = readAgentRegistry();
|
|
268
|
-
const scouts = registry.agents.filter((agent) => String(agent.kind || "") === "post-find-leads-scout"
|
|
268
|
+
const scouts = registry.agents.filter((agent) => String(agent.kind || "") === "post-find-leads-scout" &&
|
|
269
|
+
String(agent.target || agent.id || "") === "generate-messages");
|
|
269
270
|
return {
|
|
270
271
|
version: registry.version,
|
|
271
|
-
trigger: "
|
|
272
|
+
trigger: "message_drafting_after_confirm_lead_list",
|
|
272
273
|
requiredInputs: [
|
|
273
274
|
"campaignId",
|
|
274
275
|
"campaign revision or campaignUpdatedAt",
|
|
275
276
|
"campaignBrief",
|
|
276
277
|
"source decision and selectedLeadList/source state",
|
|
277
278
|
"workflowTableId",
|
|
278
|
-
"
|
|
279
|
+
"compact review-batch row count and basis/hash; branch reads rows through scoped tools",
|
|
279
280
|
"filter choice and filter/rubric basis when present",
|
|
280
281
|
],
|
|
281
282
|
scouts: scouts.map((agent) => ({
|
|
@@ -305,10 +306,10 @@ export function getPostFindLeadsScoutRegistry() {
|
|
|
305
306
|
})),
|
|
306
307
|
joinGate: {
|
|
307
308
|
afterAllComplete: true,
|
|
308
|
-
requiredState: ["
|
|
309
|
+
requiredState: ["messageDraftRecommendation"],
|
|
309
310
|
noFilterRequiredState: ["messageDraftRecommendation"],
|
|
310
311
|
skipFilterRule: "leadScoringRubrics may be absent only when the user explicitly skips filters with enableICPFilters=false; messageDraftRecommendation is still required before template review.",
|
|
311
|
-
show: ["
|
|
312
|
+
show: ["sample_message_for_approval", "message_qa_receipt"],
|
|
312
313
|
nextStage: "message-review",
|
|
313
314
|
},
|
|
314
315
|
messageDraftBranchContract: {
|
|
@@ -328,15 +329,16 @@ export function getPostFindLeadsScoutRegistry() {
|
|
|
328
329
|
"basisToken",
|
|
329
330
|
"basis.selectedLeadListId",
|
|
330
331
|
"basis.workflowTableId",
|
|
331
|
-
"basis.
|
|
332
|
+
"basis.reviewSampleRowCount",
|
|
333
|
+
"basis.reviewSampleRowHash",
|
|
332
334
|
],
|
|
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
|
|
335
|
+
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
336
|
basisFields: [
|
|
335
337
|
"campaign revision or updatedAt",
|
|
336
338
|
"brief hash",
|
|
337
339
|
"selectedLeadListId",
|
|
338
340
|
"workflowTableId",
|
|
339
|
-
"initial campaign-table execution slice row
|
|
341
|
+
"compact initial campaign-table execution slice row count/hash",
|
|
340
342
|
"filter choice",
|
|
341
343
|
"filter/rubric basis when present",
|
|
342
344
|
],
|
|
@@ -349,6 +351,7 @@ export function getPostFindLeadsScoutRegistry() {
|
|
|
349
351
|
"status",
|
|
350
352
|
"basisStatus",
|
|
351
353
|
"basisToken",
|
|
354
|
+
"qaReceipt or validationNotes",
|
|
352
355
|
"outputAt",
|
|
353
356
|
"outputHash",
|
|
354
357
|
"error or retry detail",
|
|
@@ -356,9 +359,9 @@ export function getPostFindLeadsScoutRegistry() {
|
|
|
356
359
|
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
360
|
},
|
|
358
361
|
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
|
|
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
|
|
361
|
-
parentThreadRule: 'Message
|
|
362
|
+
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. Once the user answers, spawn only Message Drafting from the same campaign/table basis whenever Codex agent-launch policy is satisfied. Treat YOLO/autonomous mode as campaign-scoped permission for this single message background agent. 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 Message Drafting background agent with the same campaign/table basis. If filters are chosen, do filter/rubric work inline in the parent thread while Message Drafting runs, 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.",
|
|
363
|
+
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. Once the user answers, invoke only Message Drafting from the same campaign/table basis. If filters are chosen, do filter/rubric work inline in the parent thread while Message Drafting runs, 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.",
|
|
364
|
+
parentThreadRule: 'Message Drafting is the only normal create-campaign background agent. If post-find-leads-message-scout is available, run that compatibility agent after the filter-choice answer so it works while the parent thread handles filter/rubric setup inline. In Codex, YOLO/autonomous mode counts as campaign-scoped permission for this single message background agent; 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 Message Drafting background agent with the same campaign/table basis. Do not spawn source scouts or Prospect Filters in the normal create-campaign flow. 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 spawn prompt; the branch must load the current campaign brief, campaign/table state, review rows, full generate-messages prompt, and validation/QA assets through scoped tools. 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 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
365
|
},
|
|
363
366
|
};
|
|
364
367
|
}
|
package/package.json
CHANGED
|
@@ -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
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
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
|
-
|
|
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,
|
|
297
|
-
|
|
298
|
-
approval. After approval, move to
|
|
299
|
-
|
|
300
|
-
skips filters, move to
|
|
301
|
-
|
|
302
|
-
opt-out from the template
|
|
303
|
-
|
|
304
|
-
|
|
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,19 @@ 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`:
|
|
436
|
-
|
|
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
|
|
440
|
-
- `
|
|
441
|
-
|
|
442
|
-
|
|
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 Message Drafting only.
|
|
425
|
+
- `launch_message_drafting`: Claude Code uses `Task` with `subagent_type`
|
|
426
|
+
`post-find-leads-message-scout` when listed; Codex uses the returned
|
|
427
|
+
compatibility agent or a generic `gpt-5.5` / `xhigh` Message Drafting agent.
|
|
450
428
|
|
|
451
429
|
If a required interactive question function or MCP loader is missing, stop and
|
|
452
|
-
explain the Sellable install/reload problem.
|
|
453
|
-
|
|
454
|
-
|
|
430
|
+
explain the Sellable install/reload problem. Source and filter work use
|
|
431
|
+
product-native MCP orchestration in the parent; the only normal background
|
|
432
|
+
branch is Message Drafting.
|
|
455
433
|
|
|
456
434
|
Never narrate local draft housekeeping to the user. If you create directories,
|
|
457
435
|
save drafts, write artifacts, or persist intermediate state, translate it into
|
|
@@ -773,7 +751,7 @@ updates.
|
|
|
773
751
|
and the current host policy allows agent launch. The worker must load
|
|
774
752
|
`mcp__sellable__get_subskill_prompt({ subskillName: "generate-messages" })`.
|
|
775
753
|
In Codex, YOLO/autonomous mode counts as campaign-scoped permission to use
|
|
776
|
-
|
|
754
|
+
this single Message Drafting background agent. If the user has not enabled
|
|
777
755
|
YOLO and has not explicitly asked for background agents, subagents, parallel
|
|
778
756
|
agents, delegation, or a message/bg agent in this campaign, ask once for
|
|
779
757
|
permission before loading the long message prompt in the parent. If
|
|
@@ -783,17 +761,24 @@ updates.
|
|
|
783
761
|
silently fall back to parent-thread message drafting; parent fallback is
|
|
784
762
|
allowed only when the user explicitly says to continue without a background
|
|
785
763
|
agent.
|
|
786
|
-
Do not use any alternate or examples-only message prompt. Message review
|
|
764
|
+
Do not use any alternate or examples-only message prompt. Message review and
|
|
765
|
+
message QA require Message Drafting output:
|
|
787
766
|
do not draft from a checklist, local markdown artifact, or parent-thread
|
|
788
767
|
intuition. Use campaign state, campaign brief content, selected source state, and
|
|
789
768
|
initial campaign-table execution slice rows as the source of truth; do not read stale local
|
|
790
769
|
markdown such as `message-validation.md`, inspect the database directly, or
|
|
791
|
-
synthesize local validation artifacts from general knowledge.
|
|
770
|
+
synthesize local validation artifacts from general knowledge. The handoff to
|
|
771
|
+
Message Drafting should pass compact basis only, not a long row-id list; the
|
|
772
|
+
branch loads current brief/table rows and validation assets itself.
|
|
792
773
|
5. Create the campaign shell early with the v1 brief so the user can open the
|
|
793
774
|
watch link and see useful setup state immediately. Materialize the approved
|
|
794
775
|
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
|
|
796
|
-
|
|
776
|
+
first campaign-table execution slice after the source is attached to the
|
|
777
|
+
campaign; do not load prospect-setup registries/prompts before asking add
|
|
778
|
+
filters vs skip filters. Once the user answers, launch only Message Drafting
|
|
779
|
+
from the same campaign/table basis. If filters are approved, do filter/rubric
|
|
780
|
+
work inline in the parent thread. Do not queue workflow cells, attach a
|
|
781
|
+
sequence, or start until saved filters and the
|
|
797
782
|
message template/token rules are approved. When filters are chosen, immediately
|
|
798
783
|
call `mcp__sellable__update_campaign({ campaignId, enableICPFilters: true, currentStep: "create-icp-rubric", watchNarration })`
|
|
799
784
|
so the watched app moves to Filter Rules while rubrics are drafted/saved.
|
|
@@ -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
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
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,16 +265,23 @@ 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
|
|
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
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
273
|
+
Once filter choice is known, start only Message Drafting in the background from
|
|
274
|
+
the same campaign/table basis. If the user chooses filters, the parent thread
|
|
275
|
+
continues inline: move to Filter Rules, load the filter reference, draft/save
|
|
276
|
+
rubrics, and ask for filter approval while Message Drafting runs. If filters are
|
|
277
|
+
skipped, move the watched app to Messages and run Message Drafting there. Debug
|
|
278
|
+
markdown/json artifacts are optional only.
|
|
279
|
+
|
|
280
|
+
Keep the Message Drafting handoff compact: campaign identity/name,
|
|
281
|
+
`selectedLeadListId`, `workflowTableId`, copied-row count, review-batch
|
|
282
|
+
count/hash, filter choice, source summary, and a concise brief summary. Do not
|
|
283
|
+
paste the full row ID list or row data. The branch loads the current brief,
|
|
284
|
+
table state, and review rows through Sellable tools before drafting.
|
|
281
285
|
|
|
282
286
|
When the user chooses filters, immediately call
|
|
283
287
|
`update_campaign({ campaignId, enableICPFilters: true, currentStep: "create-icp-rubric", watchNarration })`
|
|
@@ -289,23 +293,25 @@ Do not queue enrichment/filtering/Generate Message cells until message
|
|
|
289
293
|
approval. Tell the user Message Drafting is preparing the template in the
|
|
290
294
|
background.
|
|
291
295
|
|
|
292
|
-
|
|
293
|
-
are enabled.
|
|
294
|
-
`lead-sample.json`.
|
|
296
|
+
The parent thread persists production rubrics with `save_rubrics` when filters
|
|
297
|
+
are enabled. Do not spawn Prospect Filters in normal create-campaign runs.
|
|
295
298
|
|
|
296
299
|
Run compatibility agent `post-find-leads-message-scout` as Message Drafting
|
|
297
300
|
when allowed. Keep the long prompt and refs there.
|
|
298
301
|
|
|
299
|
-
Codex: YOLO counts as
|
|
300
|
-
after filter choice. Use generic `gpt-5.5` / `xhigh`
|
|
301
|
-
parent silently.
|
|
302
|
+
Codex: YOLO counts as permission for the single Message Drafting bg-agent. If no
|
|
303
|
+
permission, ask once after filter choice. Use generic `gpt-5.5` / `xhigh`
|
|
304
|
+
Message Drafting if needed. Never draft in parent silently.
|
|
302
305
|
|
|
303
306
|
If the user critiques or edits the template before `approve-message`, route the
|
|
304
307
|
feedback back to Message Drafting with current `messageDraftRecommendation`,
|
|
305
308
|
basis token, campaign/table basis, and latest user text. Do not rewrite copy in
|
|
306
309
|
the parent or call `update_campaign_brief`. Message Drafting must load/use
|
|
307
310
|
`generate-messages` and return a revised `messageDraftRecommendation`; parent
|
|
308
|
-
renders it and asks approval.
|
|
311
|
+
renders it and asks approval. Message QA and validation before approval also
|
|
312
|
+
belong to Message Drafting; the parent renders its QA receipt, not a fresh
|
|
313
|
+
parent-thread critique. The branch must load the normal validation/QA rules and
|
|
314
|
+
run a QA pass before returning its concise review-ready recommendation.
|
|
309
315
|
|
|
310
316
|
Load:
|
|
311
317
|
|
|
@@ -275,40 +275,31 @@ 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
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
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.
|
|
292
|
-
|
|
293
|
-
|
|
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
297
|
sample, ask add-filters vs skip-filters
|
|
307
298
|
immediately. Once the user answers, start Message Drafting via
|
|
308
|
-
`post-find-leads-message-scout` from the same campaign/table basis.
|
|
309
|
-
chooses filters,
|
|
310
|
-
|
|
311
|
-
then ask for filter approval. After approval, keep the
|
|
299
|
+
`post-find-leads-message-scout` from the same campaign/table basis. This is the
|
|
300
|
+
only normal background agent. If the user chooses filters, the parent thread
|
|
301
|
+
keeps working inline: move the browser to Filter Rules, load the filter
|
|
302
|
+
reference, save rubrics, then ask for filter approval. After approval, keep the
|
|
312
303
|
browser on Filter Leads and show `Filters saved + waiting for message approval`
|
|
313
304
|
while the message recommendation is reviewed. If the user skips filters, move
|
|
314
305
|
the browser to Messages/message review. The join gate is saved-filter approval or
|
|
@@ -316,21 +307,30 @@ a resolved skip-filter choice, plus a message recommendation grounded in the sam
|
|
|
316
307
|
campaign/table basis. Enrichment, filtering, and Generate Message cells wait for
|
|
317
308
|
template approval on the Use Template path.
|
|
318
309
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
310
|
+
Keep the Message Drafting handoff compact: campaign identity, campaign name,
|
|
311
|
+
selected lead list ID, workflow table ID, copied source row count, review-batch
|
|
312
|
+
row count, review-batch basis/hash, filter choice, source summary, and a concise
|
|
313
|
+
campaign brief summary. Do not paste the full review-batch row ID list or row
|
|
314
|
+
data into the spawn prompt. Message Drafting loads the current campaign brief,
|
|
315
|
+
campaign/table state, review rows, full generate-messages prompt, and
|
|
316
|
+
validation/QA rules inside its own branch before returning review-ready output.
|
|
317
|
+
|
|
318
|
+
Only promise background work for Message Drafting when it actually started. If
|
|
319
|
+
the host cannot or should not launch that branch, do not silently move the long
|
|
320
|
+
message prompt into the parent thread. In Codex, ask once for permission to use
|
|
321
|
+
background Message Drafting. If the user declines or asks to continue without
|
|
322
|
+
background agents, say the real sequence:
|
|
324
323
|
|
|
325
324
|
```text
|
|
326
325
|
I’ll tighten the filter first, then run message generation from the same sample.
|
|
327
326
|
```
|
|
328
327
|
|
|
329
|
-
Do not say `kicking off two workstreams`, `in parallel`, or `background`
|
|
330
|
-
|
|
328
|
+
Do not say `kicking off two workstreams`, `in parallel`, or `background` for
|
|
329
|
+
source or filter work. Only Message Drafting may be described as background.
|
|
331
330
|
|
|
332
331
|
Call the post-filter message stage `message generation` in chat.
|
|
333
|
-
Message validation is
|
|
332
|
+
Message validation/QA is owned by Message Drafting; the parent renders its
|
|
333
|
+
approval proof, not a fresh critique from memory.
|
|
334
334
|
|
|
335
335
|
Be explicit about what has and has not happened:
|
|
336
336
|
|
|
@@ -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":"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_message_drafting_after_filter_choice","get_subskill_asset references/filter-leads.md","save_rubrics"]},{"tool":"get_post_find_leads_scout_registry","returnsOnly":"post-find-leads-message-scout"},{"action":"launch_message_drafting_after_filter_choice","mode":"single_background_agent_when_host_supports_subagents","target":"post-find-leads-message-scout","when":"filters_enabled after filter-choice answer","doesNotQueueCells":true,"customerNarration":"Say Message Drafting is preparing while filters run here.","onlyBackgroundAgent":true,"inputs":["campaignId","campaignName","briefSummary","sourceSummary","selectedLeadListId","workflowTableId","copiedRowCount","reviewBatchCount","reviewBatchHash","filterChoice"],"forbiddenSiblings":["source-scout-*","post-find-leads-filter-scout"],"handoffCompression":"compact_basis_no_full_row_ids_or_row_data"},{"action":"load_filter_reference_for_parent_inline_rubrics","tool":"get_subskill_asset","requiredValues":{"subskillName":"create-campaign-v2","assetPath":"references/filter-leads.md"},"mode":"parent_thread_inline_filter_design"},{"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_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","message_drafting_after_filter_choice","message_drafting_no_cells","filters_are_parent_thread_inline","do_not_spawn_prospect_filters","only_message_drafting_may_spawn_agent"],"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","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","use generic gpt-5.5 xhigh Message Drafting agent if named unavailable"],"stateSource":"briefSummary plus 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"},"onlyBackgroundAgent":true,"forbiddenSiblingAgents":["source-scout-*","post-find-leads-filter-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","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","only_message_drafting_may_spawn_agent","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"],"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"]},{"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
|
-
|
|
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
|