@sellable/mcp 0.1.285 → 0.1.286

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/dist/index-dev.js CHANGED
File without changes
package/dist/index.js CHANGED
File without changes
@@ -29,13 +29,19 @@ export declare const campaignMessagePreparationToolDefinitions: ({
29
29
  };
30
30
  targetPreparedMessages: {
31
31
  type: string;
32
+ minimum: number;
33
+ maximum: number;
32
34
  };
33
35
  maxRowsToCheck: {
34
36
  type: string;
37
+ minimum: number;
38
+ maximum: number;
35
39
  description: string;
36
40
  };
37
41
  batchSize: {
38
42
  type: string;
43
+ minimum: number;
44
+ maximum: number;
39
45
  description: string;
40
46
  };
41
47
  approvalMode: {
@@ -6,7 +6,7 @@ async function postPrepareMessages(body) {
6
6
  export const campaignMessagePreparationToolDefinitions = [
7
7
  {
8
8
  name: "start_campaign_message_preparation",
9
- description: 'Start a bounded campaign message preparation job for a CampaignOffer campaignId. Use this after lead/message approval only when the user asks for a target like "prepare 100 messages" or "schedule 250 sends"; default approvalMode is mark_ready and it never starts the campaign. Omit maxRowsToCheck and batchSize for the adaptive default: row cap min(2500,max(300,target*5)), 100-row sample, then batches up to 250.',
9
+ description: 'Start a bounded campaign message preparation job for a CampaignOffer campaignId. Use this after lead/message approval only when the user asks for a target like "prepare 100 messages" or "schedule 250 sends"; default approvalMode is mark_ready and it never starts the campaign. Omit maxRowsToCheck and batchSize for the enforced adaptive default: first calibrate on at least 100 rows, estimate the row budget from observed rubric/pass yield, cap rows at 2500, then use batches up to 250 once the sample is strong enough.',
10
10
  inputSchema: {
11
11
  type: "object",
12
12
  properties: {
@@ -15,19 +15,23 @@ export const campaignMessagePreparationToolDefinitions = [
15
15
  description: "CampaignOffer.id for the campaign whose workflow table should prepare messages.",
16
16
  },
17
17
  tableId: { type: "string" },
18
- targetPreparedMessages: { type: "number" },
18
+ targetPreparedMessages: { type: "number", minimum: 1, maximum: 500 },
19
19
  maxRowsToCheck: {
20
20
  type: "number",
21
- description: "Optional override. By default the backend uses min(2500,max(300,targetPreparedMessages*5)).",
21
+ minimum: 1,
22
+ maximum: 2500,
23
+ description: "Optional override capped by the backend at 2500. Omit this for adaptive sample-based row budgeting.",
22
24
  },
23
25
  batchSize: {
24
26
  type: "number",
25
- description: "Optional first-batch override. By default the backend samples up to 100 rows, then adapts batches up to 250.",
27
+ minimum: 1,
28
+ maximum: 250,
29
+ description: "Optional first-batch override capped by the backend at 250. Omit this to sample 100 rows before larger batches.",
26
30
  },
27
31
  approvalMode: {
28
32
  type: "string",
29
33
  enum: ["mark_ready", "approve"],
30
- description: 'Defaults to mark_ready. Use approve only when the user explicitly asks to flip Approved cells.',
34
+ description: "Defaults to mark_ready. Use approve only when the user explicitly asks to flip Approved cells.",
31
35
  },
32
36
  autoContinue: { type: "boolean" },
33
37
  },
@@ -37,7 +41,7 @@ export const campaignMessagePreparationToolDefinitions = [
37
41
  },
38
42
  {
39
43
  name: "get_campaign_message_preparation_status",
40
- description: "Poll compact campaign message preparation job progress: checked rows, prepared/approved count, target, remaining row budget, warnings, and stop reason. Does not return raw rows or message bodies.",
44
+ description: "Poll compact campaign message preparation job progress: checked rows, pass/prepared/approved count, target, estimated row budget, adaptive batch plan, warnings, and stop reason. Does not return raw rows or message bodies.",
41
45
  inputSchema: {
42
46
  type: "object",
43
47
  properties: {
@@ -50,7 +54,7 @@ export const campaignMessagePreparationToolDefinitions = [
50
54
  },
51
55
  {
52
56
  name: "cancel_campaign_message_preparation",
53
- description: "Cancel an active campaign message preparation job. Cancellation is cooperative and launch remains a separate explicit start_campaign step.",
57
+ description: "Cancel an active campaign message preparation job and cancel the same pending workflow-table cells that the UI Cancel Pending Cells button would cancel. Launch remains a separate explicit start_campaign step.",
54
58
  inputSchema: {
55
59
  type: "object",
56
60
  properties: {
@@ -366,7 +366,7 @@ export function getPostFindLeadsScoutRegistry() {
366
366
  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. After the answer, launch only Message Drafting whenever Codex agent-launch policy is satisfied. The registry lookup is not a launch: after get_post_find_leads_scout_registry, immediately invoke Task/spawn_agent or the host background-agent mechanism before loading filter-leads.md or saving rubrics. If filters are chosen, the parent stays on Filter Rules and drafts/saves rubrics with MCP tools while Message Drafting runs in the background. If filters are skipped, move to Messages/message review after Message Drafting is ready. Treat YOLO/autonomous mode as campaign-scoped permission for this single post-import worker; do not ask for another permission click in YOLO. If the user has not enabled YOLO and has not explicitly asked for background agents/subagents/parallel agents/delegation/message bg agent in this campaign, ask once before loading the long message prompt in the parent. If permission is granted and the named Message Drafting custom agent is unavailable, spawn a generic gpt-5.5 xhigh Message Drafting background agent with the same lean campaign/table basis. If no background-agent tool is callable, start the same full message branch inline before filter drafting, or return blocked/retry-needed; do not wait until filters are saved and then call the registry.",
367
367
  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. After the answer, invoke only Message Drafting. If filters are chosen, parent drafts/saves rubrics with MCP tools while Message Drafting runs, asks filter approval, then joins Message Drafting. If filters are skipped, invoke only Message Drafting and move to Messages/message review.",
368
368
  parentThreadRule: 'Named agents are optional acceleration, but message drafting is not optional. The only normal background worker is Message Drafting. YOLO/autonomous mode counts as campaign-scoped permission for this single post-import worker; do not ask for another permission click in YOLO. If a named agent is unavailable after permission, use a generic gpt-5.5 xhigh Message Drafting background agent. source work and filter work stay in the parent thread with MCP tools. If post-find-leads-message-scout is available, run it as the background Message Draft Builder after the filter-choice answer. The registry lookup is not a launch: get_post_find_leads_scout_registry only identifies the worker, and Message Drafting counts as started only after Task/spawn_agent or the host background-agent tool is invoked, or after the parent begins the same full message branch inline because no background-agent tool is callable. This launch must happen before loading filter-leads.md, save_rubrics, or filter approval. If post-find-leads-message-scout is absent, do not customer-surface install status. Do not silently fall back to parent-thread message drafting; the main thread must execute the same message branch from CampaignOffer state, selected source state, workflowTableId, and initial campaign-table execution slice rows. If no background-agent tool is callable, start that same full message branch inline before filter drafting, or return blocked/retry-needed; do not wait until filters are saved and then call the registry. The Message Drafting handoff must be lean. Do not paste copied row counts, brief hashes, review-batch hashes, full reviewBatchRowIds, broad row data, or local debug artifacts into the spawn prompt. Local markdown/json files are not normal-path inputs. The filter-choice question is the first post-import user gate; do not load post-lead registries or filter references before it. Message drafting starts after the filter-choice answer, must load get_subskill_prompt({ subskillName: "generate-messages" }), and must load every required message asset named by generate-messages Mode 0 through get_subskill_asset before drafting. Reference Asset Loading means loading the required pre-draft reference pack before drafting; return blocked/retry-needed if required assets cannot be loaded; load ai-tells.md because it is never optional. The branch loads the full generate-messages prompt and every referenced asset through get_subskill_asset. After generating/revising the candidate and before returning ready, must load get_subskill_prompt({ subskillName: "create-campaign-v2-validation" }) as the final internal validation gate, must read live campaign table state through scoped MCP/product tools, and must reject mismatched selectedLeadListId/workflowTableId/campaign/workspace input. Do not block when filters were chosen but leadScoringRubrics are not yet visible in the branch read; the parent owns save_rubrics and filter approval in parallel, so Message Drafting should return status ready with basisStatus usable_initial when the campaign/list/table and non-empty execution slice match. Do not use any alternate, local-artifact, 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, lean 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. The worker validates internally and returns only templateRecommendation, tokenFillRules, renderedGoodSample, status, approveOrReviseRecommendation, validationStatus, outputAt, outputHash, and blocked/retry detail. Do not render renderedFallbackSample, risk notes, or a qaReceipt on the normal happy path. On the filter path, save_rubrics 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 after Message Drafting is ready 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, required message assets, and validation gate 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. Handoff and recommendation output are Markdown with labeled fields, not raw JSON.',
369
- prepareMessagesRule: 'Default create-campaign stays on the existing reviewBatchLimit:15 first campaign-table execution slice. Only call start_campaign_message_preparation when the user explicitly asks for more prepared messages or a send count. For "prepare/generate X messages", set targetPreparedMessages:X, let the backend derive maxRowsToCheck:min(2500,max(300,X*5)), and use approvalMode:mark_ready. The backend samples up to 100 rows first, then adapts later batches up to 250 rows while recalculating yield. Poll get_campaign_message_preparation_status and summarize preparation-job status: checked rows, prepared/approved count, target, rows remaining, and stop reason. For "approve X messages", use approvalMode:approve but still do not launch. For "schedule X sends", use approvalMode:approve to approve exactly the bounded X-message cohort during preparation, then continue through sender, sequence, and final launch greenlight; final launch must verify that bounded cohort and must not broad approve-all. campaignId is CampaignOffer.id. If the user asks to stop preparation, the target is wrong, or status shows the wrong campaign/table, call cancel_campaign_message_preparation; otherwise do not cancel a healthy prepare run. Low-level selectors are diagnostics and recovery only for this lane. start_campaign remains forbidden until final launch greenlight.',
369
+ prepareMessagesRule: 'Default create-campaign stays on the existing reviewBatchLimit:15 first campaign-table execution slice. Only call start_campaign_message_preparation when the user explicitly asks for more prepared messages or a send count. For "prepare/generate X messages", set targetPreparedMessages:X, omit maxRowsToCheck so the backend calibrates on at least 100 rows, estimates the row budget from observed rubric/pass yield, caps maxRowsToCheck at 2500, and use approvalMode:mark_ready. After the calibration sample settles, the backend adapts later batches up to 250 rows while recalculating yield. Poll get_campaign_message_preparation_status and summarize preparation-job status: checked rows, passed/prepared/approved count, target, estimated row budget remaining, and stop reason. For "approve X messages", use approvalMode:approve but still do not launch. For "schedule X sends", use approvalMode:approve to approve exactly the bounded X-message cohort during preparation, then continue through sender, sequence, and final launch greenlight; final launch must verify that bounded cohort and must not broad approve-all. campaignId is CampaignOffer.id. If the user asks to stop preparation, the target is wrong, or status shows the wrong campaign/table, call cancel_campaign_message_preparation; otherwise do not cancel a healthy prepare run. cancel_campaign_message_preparation cancels the same pending workflow-table cells as the UI Cancel Pending Cells action. Low-level selectors are diagnostics and recovery only for this lane. start_campaign remains forbidden until final launch greenlight.',
370
370
  },
371
371
  };
372
372
  }
@@ -674,13 +674,19 @@ export declare const allTools: ({
674
674
  };
675
675
  targetPreparedMessages: {
676
676
  type: string;
677
+ minimum: number;
678
+ maximum: number;
677
679
  };
678
680
  maxRowsToCheck: {
679
681
  type: string;
682
+ minimum: number;
683
+ maximum: number;
680
684
  description: string;
681
685
  };
682
686
  batchSize: {
683
687
  type: string;
688
+ minimum: number;
689
+ maximum: number;
684
690
  description: string;
685
691
  };
686
692
  approvalMode: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sellable/mcp",
3
- "version": "0.1.285",
3
+ "version": "0.1.286",
4
4
  "type": "module",
5
5
  "description": "Sellable MCP server for Claude Code and Codex campaign workflows",
6
6
  "main": "dist/index.js",
@@ -97,17 +97,19 @@ review the normal `reviewBatchLimit:15`, approve reviewed draft rows, then move
97
97
  to Settings/sequence/final greenlight. Only call
98
98
  `start_campaign_message_preparation` when the user explicitly asks for more
99
99
  prepared messages or a send count. If the user says "prepare/generate X
100
- messages", set `targetPreparedMessages:X`, let the backend derive
101
- `maxRowsToCheck:min(2500,max(300,X*5))`, and keep
102
- `approvalMode:"mark_ready"`. The backend samples up to 100 rows first, then
103
- adapts later batches up to 250 rows while recalculating yield. Poll
100
+ messages", set `targetPreparedMessages:X`, omit `maxRowsToCheck`, and keep
101
+ `approvalMode:"mark_ready"`. The backend calibrates on at least 100 rows,
102
+ estimates the row budget from observed rubric/pass yield, caps
103
+ `maxRowsToCheck` at 2500, then adapts later batches up to 250 rows while
104
+ recalculating yield. Poll
104
105
  `get_campaign_message_preparation_status` for preparation-job status: checked
105
- rows, prepared/approved count, target, row budget remaining, and stop reason. If
106
- the user says "approve X messages", use `approvalMode:"approve"` but still do
107
- not launch. If the user says "schedule X sends", use `approvalMode:"approve"`
108
- to approve exactly the bounded X-message cohort during preparation, then
109
- continue through sender, sequence, and final launch greenlight; the launch path
110
- must verify that bounded cohort and must not broad approve-all.
106
+ rows, passed/prepared/approved count, target, estimated row budget remaining,
107
+ and stop reason. If the user says "approve X messages", use
108
+ `approvalMode:"approve"` but still do not launch. If the user says "schedule X
109
+ sends", use `approvalMode:"approve"` to approve exactly the bounded X-message
110
+ cohort during preparation, then continue through sender, sequence, and final
111
+ launch greenlight; the launch path must verify that bounded cohort and must not
112
+ broad approve-all.
111
113
  Treat `campaignId` as `CampaignOffer.id`. Low level selector/queue tools are
112
114
  diagnostics and recovery only for this lane. If the user asks to stop
113
115
  preparation, the target is wrong, or status shows the wrong campaign/table, call
@@ -480,14 +482,14 @@ automation just because a watch link exists. If `create_campaign` returns
480
482
  `watchHandoff.markdown`, print that exact value once, directly before the brief
481
483
  approval question. It will use the URL mode to say Codex or Claude Code:
482
484
 
483
- ````markdown
485
+ ```markdown
484
486
  > **WATCH CODEX BUILD THE CAMPAIGN LIVE**
485
487
  >
486
488
  > [Open live campaign builder]({watchUrl})
487
489
  >
488
490
  > Keep this chat open. I'll ask approval questions here before making decisions
489
491
  > that need your judgment.
490
- ````
492
+ ```
491
493
 
492
494
  The rendered callout is intentional: rendered chat clients highlight the
493
495
  headline, link, and note without treating it as a code block, and plain terminals
@@ -969,7 +971,7 @@ updates.
969
971
  is ready.
970
972
  Do not ask the user to approve the brief before shell creation unless they
971
973
  explicitly requested a no-write draft; the shell itself is the review surface.
972
- 6. The main thread owns watch navigation. Call
974
+ 7. The main thread owns watch navigation. Call
973
975
  `mcp__sellable__update_campaign({ campaignId, currentStep })` before major
974
976
  visible work so the user can watch progress in the app: `create-offer` for
975
977
  the brief, `pick-provider` or the selected provider step while sourcing,
@@ -984,9 +986,9 @@ updates.
984
986
  job has reported compact checked/prepared/stop status, `settings` for sender
985
987
  selection, `sequence` after sender attach, and `send` once the recommended
986
988
  sequence is attached. Do not advance the step backward.
987
- 7. Keep `selectedLeadListId` as the source list and `workflowTableId` as the
989
+ 8. Keep `selectedLeadListId` as the source list and `workflowTableId` as the
988
990
  campaign table. Do not use disk files as the post-mint source of truth.
989
- 8. Do not ask the user to run another command.
991
+ 9. Do not ask the user to run another command.
990
992
 
991
993
  ## Fallback
992
994
 
@@ -1 +1 @@
1
- {"version":"v2.1-compact","workflow":"create-campaign-v2","principle":"CampaignOffer/watch canonical; require brief/source/import, filters/message, Settings, explicit Start.","normalCustomerPath":"Use campaign state/MCP/watchNarration. Do not create, read, link, or surface local files. Print watch link once.","legacyCompatibility":{"validationSubskill":"create-campaign-v2-validation","tailSubskill":"create-campaign-v2-tail","rule":"Legacy diagnostics are opt-in, not active 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":"Watched currentStep switches need fresh watchNarration.","requiredFields":["stage","headline","visibleState","agentIntent","nextAction"],"copyMustInclude":["what just happened","visible page/state","next user action"],"appliesToTools":["create_campaign","update_campaign","attach_recommended_sequence","start_campaign"],"oneTimeWatchLinkPolicy":"must not print another watch-link handoff unless asked or recovering link."},"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":["No list_senders before Settings post-approval.","No import before Start Import.","No queueing until rows exist and gates pass.","No start_campaign without launch confirmation.","No local files as durable state."],"yoloMode":{"triggerPhrases":["yolo","--yolo","mode=yolo","autopilot","use best guesses","use best estimates","answer for me","just run it","yolo autopilot"],"identityRequestOnlyWhenMissing":true,"operatorDirectionsRule":"Operator directions persist; 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/data","ambiguous choice with no reasonable estimate","source/message/filter quality 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 handles to profile URLs; if not a profile, ask again 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":["WATCH CODEX BUILD","Open live campaign builder","Keep this chat open.","approval questions 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 handoff","shell open command","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","Does this brief look right, and how should Sellable continue?"],"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: ask approve/revise, 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":"Brief visible once before approval. If shown before shell creation, do not repeat after; append one watch-link handoff, then ask.","copyMustAvoid":["duplicate brief render after shell creation","second Watch link:"]},{"action":"ask_brief_choice","uses":"request_user_input","choices":["Approve brief + activate YOLO mode (Recommended)","Approve brief + build campaign with AI, step by step","Revise brief"],"skipIf":"yolo_mode; auto_continue after rendering assumptions unless brief quality floor fails","question":"Does this brief look right, and how should Sellable continue?","autonomousChoice":"Approve brief + activate YOLO mode (Recommended)","choiceDescriptions":{"Approve brief + activate YOLO mode (Recommended)":"Sellable will auto-decide the remaining setup choices with best guesses, including sources, filters, and messaging. It still stops before final launch.","Approve brief + build campaign with AI, step by step":"Approve this direction, then work with the AI through each major setup decision before it continues.","Revise brief":"Change the target, offer, proof, or campaign direction before continuing."}}],"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_companies","confirm_prospeo_company_accounts","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 without repeating the watch link; never ask first.","copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL","watch-link handoff"],"approvalFreshnessRule":"Only post-plan source approval sets source_lane_approved. Do not reuse brief approval."},"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_companies","confirm_prospeo_company_accounts","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","watch-link handoff"],"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 after visible Find Buyers Plan, explicit post-plan leadSourceProvider, or yolo 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_companies","confirm_prospeo_company_accounts","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_companies","confirm_prospeo_company_accounts","search_prospeo","search_signals","select_promising_posts","fetch_post_engagers","fetch_company","fetch_linkedin_profile","load_csv_dnc_entries","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: after approval build source list, add to campaign, review first 15 leads; 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":"Filter setup + message drafting","onEnter":[{"action":"persist_add_filters_approval","tool":"update_campaign","when":"filters_enabled","requiredFields":["campaignId","enableICPFilters","currentStep","watchNarration"],"requiredValues":{"enableICPFilters":true,"currentStep":"create-icp-rubric","watchNarration.stage":"fit-message","watchNarration.headline":"Create filter rules","watchNarration.visibleState":"Filter Rules is visible while Codex drafts and saves filters in the parent thread.","watchNarration.nextAction":"Review saved filter rules"},"mustRunBefore":["get_post_find_leads_scout_registry","launch_message_drafting_after_filter_choice","load_filter_reference_in_parent","save_rubrics","ask_filter_rubric_review_choice"]},{"tool":"get_post_find_leads_scout_registry","returns":["post-find-leads-message-scout"],"rule":"normal post-import registry exposes only Message Drafting; filters are parent-thread MCP work","mustRunBefore":["launch_message_drafting_after_filter_choice","get_subskill_asset:references/filter-leads.md","save_rubrics","ask_filter_rubric_review_choice"],"registryOnlyIsNotLaunch":true},{"action":"launch_message_drafting_after_filter_choice","mode":"background_agent_when_host_supports_subagents","target":"post-find-leads-message-scout","when":"filters_enabled or filters_skipped after filter-choice answer","doesNotQueueCells":true,"customerNarration":"Message Drafting is preparing.","inputs":["campaignId","workflowTableId","brief/source summary","3-5 sample workflow-table rows","optional campaignName/selectedLeadListId/filterChoice"],"forbiddenSiblings":["source/filter workers"],"handoffCompression":"lean_message_drafting_handoff_no_hashes_no_counts_no_full_row_data","requiredConcreteToolCall":"Task or spawn_agent when host supports background agents","registryOnlyIsNotLaunch":true,"mustRunBefore":["get_subskill_asset:references/filter-leads.md","load_filter_reference_in_parent","save_rubrics","ask_filter_rubric_review_choice"],"yoloPermissionRule":"do not ask the user for worker permission in YOLO.","startProof":"Message Drafting counts as started only after Task/spawn_agent.","noBackgroundFallback":"Start the same full message branch inline before filter reference or block.","blockedIfSkipped":"Do not proceed to get_subskill_asset(filter-leads), save_rubrics."},{"tool":"get_subskill_asset","action":"load_filter_reference_in_parent","when":"filters_enabled","requiredValues":{"subskillName":"create-campaign-v2","assetPath":"references/filter-leads.md"},"rule":"Parent thread uses this reference to draft production rubrics; do not spawn a filter worker."},{"tool":"save_rubrics","when":"filters_enabled","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":["Filter rules saved for review","These rules prevent wasted sends before I score/import the list.","Recommended because of the researched ICP, source sample, and repeated false-positive patterns.","one pass example and one block example when available","approval is for the saved filter rules","Approve these filter rules."],"autoSelectIf":"yolo_mode and parent saved production-shaped rubrics","copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"],"requiredPrecondition":"save_rubrics succeeded or active campaign rubrics verified"}],"requiredCampaignState":["campaignId","campaignBrief","selectedLeadListId","workflowTableId"],"allowedTools":["get_post_find_leads_scout_registry","get_subskill_asset","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","only_message_drafting_may_spawn_background_agent","filters_are_parent_thread_mcp_work","no_post_find_leads_filter_scout_in_normal_path","registry_call_is_not_message_drafting_launch","yolo_must_not_ask_permission_for_message_drafting_worker","message_drafting_must_start_before_filter_reference_or_save_rubrics","if_background_agent_unavailable_start_full_message_branch_inline_before_filters_or_block"],"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_draft_builder","target":"post-find-leads-message-scout","toolCallRequiredBeforeDraft":["join already-running Message Drafting when prospect-setup launched it","run Message Drafting via post-find-leads-message-scout only if missing; yolo counts","handoff lean basis only: campaignId, workflowTableId, brief summary, source summary/source-use rule, 3-5 sample rows","do not paste copied row counts, brief hashes, review-batch hashes, full reviewBatchRowIds, broad row data, or local debug artifacts","branch loads current brief/context, full generate-messages prompt, and every required message asset referenced by generate-messages Mode 0","return labeled Markdown: templateRecommendation, tokenFillRules, renderedGoodSample, status, recommendation, validationStatus, outputHash, retry detail","do not return renderedFallbackSample, risk notes, or qaReceipt on the normal happy path","if no yolo and permission missing, ask once before parent inline fallback","use generic gpt-5.5 xhigh Message Drafting agent if named unavailable"],"stateSource":"message branch-loaded live campaign/table state plus parent-provided sample rows","outputState":"messageDraftRecommendation with internal validation passed","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","lean 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"},"onlyMessageBackgroundAgent":true,"forbiddenSiblingAgents":["source-scout-*","any filter background worker"],"handoffCompression":"lean_message_drafting_handoff_no_hashes_no_counts_no_full_row_data","finalGateRequiredBeforeReady":"Post-generation final gate: After candidate generation/revision, load get_subskill_prompt({ subskillName: \"create-campaign-v2-validation\" })."}],"requiredCampaignState":["campaignId","campaignBrief","selectedLeadListId","workflowTableId"],"allowedTools":["get_subskill_prompt","get_campaign","get_rows_minimal","update_campaign","Task","spawn_agent"],"toolRules":["message_drafting_agent_first_no_silent_parent_fallback; yolo counts","on_filters_enabled_path_parent_must_not_enter_message_review_until_save_rubrics_success_and_filter_approval","brief.md, lead-review.md, and lead-sample.json are optional debug context only.","messageDraftRecommendation returns templateRecommendation, tokenFillRules, renderedGoodSample, status, recommendation, validationStatus, outputHash, retry detail.","messageDraftRecommendation does not return renderedFallbackSample, risk notes, or qaReceipt on the normal happy path.","If campaign/table/sample basis does not match, classify the output stale or blocked.","message_revision_feedback_routes_to_message_drafting_agent_not_parent","message_qa_routes_to_message_drafting_agent_not_parent","message_drafting_loads_full_generate_messages_prompt_all_chunks","message_drafting_loads_all_reference_assets_via_get_subskill_asset","message_drafting_loads_create_campaign_v2_validation_prompt_before_return","message_drafting_handoff_uses_lean_basis_no_hashes_no_counts_no_full_row_id_list","message_drafting_loads_current_campaign_brief_before_drafting","parent_may_load_generate_messages_only_for_explicitly_approved_inline_fallback","before return branch loads create-campaign-v2-validation prompt and runs internal validation"],"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 validationStatus","render revised ## Message Template, ## Rendered Example, and What changed before asking approval"],"blocked":["asking whether an unseen new version is better","reusing the old template after acknowledging feedback","rewriting the template in the parent thread","update_campaign_brief before approve-message","request_user_input or AskUserQuestion during message-generation","QAing the template in the parent thread"],"outputState":"messageDraftRecommendation"},"hardRules":["message_revision_must_render_new_template_before_approval_question","message_revision_saves_only_after_approve_message","message_generation_must_not_call_request_user_input","message_generation_must_transition_to_message_review_only_after_renderable_draft_state","message_drafting_agent_first_no_silent_parent_fallback","message_revision_routes_to_message_drafting_agent","message_qa_routes_to_message_drafting_agent","filter_path_requires_saved_filters_before_message_review","only_message_drafting_may_spawn_background_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":["Rendered fallback sample","Concerns","QA receipt","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","validation notes","QA details"],"copyMustInclude":["one recommended template","one rendered sample","token rules and fallbacks","full list remains paused"],"copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"],"revisionRenderRule":"After revise_messaging, QA, or copy feedback, route feedback to Message Drafting; show revised template/example; never ask if an unseen version is better.","parentMustNot":["rewrite template from memory","invent validation summary without Message Drafting output","render qaReceipt, risk notes, or renderedFallbackSample on the normal happy path"]},{"action":"ask_message_review_choice","uses":"request_user_input","choices":["approve-message","revise-messaging"],"copyMustInclude":["approve this message template and continue","template approval only locks the draft direction; final Start still controls sending"],"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":"After saved-filter approval and approve-message, save the template and queue bounded Enrich Prospect cells by selector.","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/sellable-cleanup-rules.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","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","this approves reviewed draft rows for setup; final Start still controls sending"],"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_message_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":"start_campaign_message_preparation","requiredValues":{"targetPreparedMessages":"{requestedMessageCount}","maxRowsToCheck":"{min(max(300,X*5),2500)}","approvalMode":"{scheduleOrApproveIntent ? 'approve' : 'mark_ready'}"},"intentMapping":{"scheduleSends":"approve the exact requested send count"},"when":"explicit_count_or_schedule","batchStrategy":"auto"},{"tool":"get_campaign_message_preparation_status","copyMustInclude":["preparation job status","checked rows","target","stop reason"],"when":"prep_job_started"},{"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, Settings is open, lead research/filtering happened outside LinkedIn, 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":"Say the recommended follow-up sequence is attached, final launch review is open, and nothing sends 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":["cancel_campaign_message_preparation","get_campaign_message_preparation_status","start_campaign_message_preparation","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"],"boundedLaunchRule":"preparation job approved X; no broad approve-all."},{"tool":"start_campaign","requiredFields":["campaignId"],"persistsCurrentStep":"running","watchNarrationRule":"After start_campaign succeeds, say final greenlight was accepted, campaign is live/running, and progress can be watched."}],"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/watch canonical; require brief/source/import, filters/message, Settings, explicit Start.","normalCustomerPath":"Use campaign state/MCP/watchNarration. Do not create, read, link, or surface local files. Print watch link once.","legacyCompatibility":{"validationSubskill":"create-campaign-v2-validation","tailSubskill":"create-campaign-v2-tail","rule":"Legacy diagnostics are opt-in, not active 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":"Watched currentStep switches need fresh watchNarration.","requiredFields":["stage","headline","visibleState","agentIntent","nextAction"],"copyMustInclude":["what just happened","visible page/state","next user action"],"appliesToTools":["create_campaign","update_campaign","attach_recommended_sequence","start_campaign"],"oneTimeWatchLinkPolicy":"must not print another watch-link handoff unless asked or recovering link."},"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":["No list_senders before Settings post-approval.","No import before Start Import.","No queueing until rows exist and gates pass.","No start_campaign without launch confirmation.","No local files as durable state."],"yoloMode":{"triggerPhrases":["yolo","--yolo","mode=yolo","autopilot","use best guesses","use best estimates","answer for me","just run it","yolo autopilot"],"identityRequestOnlyWhenMissing":true,"operatorDirectionsRule":"Operator directions persist; 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/data","ambiguous choice with no reasonable estimate","source/message/filter quality 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 handles to profile URLs; if not a profile, ask again 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":["WATCH CODEX BUILD","Open live campaign builder","Keep this chat open.","approval questions 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 handoff","shell open command","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","Does this brief look right, and how should Sellable continue?"],"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: ask approve/revise, 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":"Brief visible once before approval. If shown before shell creation, do not repeat after; append one watch-link handoff, then ask.","copyMustAvoid":["duplicate brief render after shell creation","second Watch link:"]},{"action":"ask_brief_choice","uses":"request_user_input","choices":["Approve brief + activate YOLO mode (Recommended)","Approve brief + build campaign with AI, step by step","Revise brief"],"skipIf":"yolo_mode; auto_continue after rendering assumptions unless brief quality floor fails","question":"Does this brief look right, and how should Sellable continue?","autonomousChoice":"Approve brief + activate YOLO mode (Recommended)","choiceDescriptions":{"Approve brief + activate YOLO mode (Recommended)":"Sellable will auto-decide the remaining setup choices with best guesses, including sources, filters, and messaging. It still stops before final launch.","Approve brief + build campaign with AI, step by step":"Approve this direction, then work with the AI through each major setup decision before it continues.","Revise brief":"Change the target, offer, proof, or campaign direction before continuing."}}],"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_companies","confirm_prospeo_company_accounts","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 without repeating the watch link; never ask first.","copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL","watch-link handoff"],"approvalFreshnessRule":"Only post-plan source approval sets source_lane_approved. Do not reuse brief approval."},"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_companies","confirm_prospeo_company_accounts","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","watch-link handoff"],"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 after visible Find Buyers Plan, explicit post-plan leadSourceProvider, or yolo 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_companies","confirm_prospeo_company_accounts","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_companies","confirm_prospeo_company_accounts","search_prospeo","search_signals","select_promising_posts","fetch_post_engagers","fetch_company","fetch_linkedin_profile","load_csv_dnc_entries","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: after approval build source list, add to campaign, review first 15 leads; 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":"Filter setup + message drafting","onEnter":[{"action":"persist_add_filters_approval","tool":"update_campaign","when":"filters_enabled","requiredFields":["campaignId","enableICPFilters","currentStep","watchNarration"],"requiredValues":{"enableICPFilters":true,"currentStep":"create-icp-rubric","watchNarration.stage":"fit-message","watchNarration.headline":"Create filter rules","watchNarration.visibleState":"Filter Rules is visible while Codex drafts and saves filters in the parent thread.","watchNarration.nextAction":"Review saved filter rules"},"mustRunBefore":["get_post_find_leads_scout_registry","launch_message_drafting_after_filter_choice","load_filter_reference_in_parent","save_rubrics","ask_filter_rubric_review_choice"]},{"tool":"get_post_find_leads_scout_registry","returns":["post-find-leads-message-scout"],"rule":"normal post-import registry exposes only Message Drafting; filters are parent-thread MCP work","mustRunBefore":["launch_message_drafting_after_filter_choice","get_subskill_asset:references/filter-leads.md","save_rubrics","ask_filter_rubric_review_choice"],"registryOnlyIsNotLaunch":true},{"action":"launch_message_drafting_after_filter_choice","mode":"background_agent_when_host_supports_subagents","target":"post-find-leads-message-scout","when":"filters_enabled or filters_skipped after filter-choice answer","doesNotQueueCells":true,"customerNarration":"Message Drafting is preparing.","inputs":["campaignId","workflowTableId","brief/source summary","3-5 sample workflow-table rows","optional campaignName/selectedLeadListId/filterChoice"],"forbiddenSiblings":["source/filter workers"],"handoffCompression":"lean_message_drafting_handoff_no_hashes_no_counts_no_full_row_data","requiredConcreteToolCall":"Task or spawn_agent when host supports background agents","registryOnlyIsNotLaunch":true,"mustRunBefore":["get_subskill_asset:references/filter-leads.md","load_filter_reference_in_parent","save_rubrics","ask_filter_rubric_review_choice"],"yoloPermissionRule":"do not ask the user for worker permission in YOLO.","startProof":"Message Drafting counts as started only after Task/spawn_agent.","noBackgroundFallback":"Start the same full message branch inline before filter reference or block.","blockedIfSkipped":"Do not proceed to get_subskill_asset(filter-leads), save_rubrics."},{"tool":"get_subskill_asset","action":"load_filter_reference_in_parent","when":"filters_enabled","requiredValues":{"subskillName":"create-campaign-v2","assetPath":"references/filter-leads.md"},"rule":"Parent thread uses this reference to draft production rubrics; do not spawn a filter worker."},{"tool":"save_rubrics","when":"filters_enabled","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":["Filter rules saved for review","These rules prevent wasted sends before I score/import the list.","Recommended because of the researched ICP, source sample, and repeated false-positive patterns.","one pass example and one block example when available","approval is for the saved filter rules","Approve these filter rules."],"autoSelectIf":"yolo_mode and parent saved production-shaped rubrics","copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"],"requiredPrecondition":"save_rubrics succeeded or active campaign rubrics verified"}],"requiredCampaignState":["campaignId","campaignBrief","selectedLeadListId","workflowTableId"],"allowedTools":["get_post_find_leads_scout_registry","get_subskill_asset","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","only_message_drafting_may_spawn_background_agent","filters_are_parent_thread_mcp_work","no_post_find_leads_filter_scout_in_normal_path","registry_call_is_not_message_drafting_launch","yolo_must_not_ask_permission_for_message_drafting_worker","message_drafting_must_start_before_filter_reference_or_save_rubrics","if_background_agent_unavailable_start_full_message_branch_inline_before_filters_or_block"],"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_draft_builder","target":"post-find-leads-message-scout","toolCallRequiredBeforeDraft":["join already-running Message Drafting when prospect-setup launched it","run Message Drafting via post-find-leads-message-scout only if missing; yolo counts","handoff lean basis only: campaignId, workflowTableId, brief summary, source summary/source-use rule, 3-5 sample rows","do not paste copied row counts, brief hashes, review-batch hashes, full reviewBatchRowIds, broad row data, or local debug artifacts","branch loads current brief/context, full generate-messages prompt, and every required message asset referenced by generate-messages Mode 0","return labeled Markdown: templateRecommendation, tokenFillRules, renderedGoodSample, status, recommendation, validationStatus, outputHash, retry detail","do not return renderedFallbackSample, risk notes, or qaReceipt on the normal happy path","if no yolo and permission missing, ask once before parent inline fallback","use generic gpt-5.5 xhigh Message Drafting agent if named unavailable"],"stateSource":"message branch-loaded live campaign/table state plus parent-provided sample rows","outputState":"messageDraftRecommendation with internal validation passed","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","lean 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"},"onlyMessageBackgroundAgent":true,"forbiddenSiblingAgents":["source-scout-*","any filter background worker"],"handoffCompression":"lean_message_drafting_handoff_no_hashes_no_counts_no_full_row_data","finalGateRequiredBeforeReady":"Post-generation final gate: After candidate generation/revision, load get_subskill_prompt({ subskillName: \"create-campaign-v2-validation\" })."}],"requiredCampaignState":["campaignId","campaignBrief","selectedLeadListId","workflowTableId"],"allowedTools":["get_subskill_prompt","get_campaign","get_rows_minimal","update_campaign","Task","spawn_agent"],"toolRules":["message_drafting_agent_first_no_silent_parent_fallback; yolo counts","on_filters_enabled_path_parent_must_not_enter_message_review_until_save_rubrics_success_and_filter_approval","brief.md, lead-review.md, and lead-sample.json are optional debug context only.","messageDraftRecommendation returns templateRecommendation, tokenFillRules, renderedGoodSample, status, recommendation, validationStatus, outputHash, retry detail.","messageDraftRecommendation does not return renderedFallbackSample, risk notes, or qaReceipt on the normal happy path.","If campaign/table/sample basis does not match, classify the output stale or blocked.","message_revision_feedback_routes_to_message_drafting_agent_not_parent","message_qa_routes_to_message_drafting_agent_not_parent","message_drafting_loads_full_generate_messages_prompt_all_chunks","message_drafting_loads_all_reference_assets_via_get_subskill_asset","message_drafting_loads_create_campaign_v2_validation_prompt_before_return","message_drafting_handoff_uses_lean_basis_no_hashes_no_counts_no_full_row_id_list","message_drafting_loads_current_campaign_brief_before_drafting","parent_may_load_generate_messages_only_for_explicitly_approved_inline_fallback","before return branch loads create-campaign-v2-validation prompt and runs internal validation"],"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 validationStatus","render revised ## Message Template, ## Rendered Example, and What changed before asking approval"],"blocked":["asking whether an unseen new version is better","reusing the old template after acknowledging feedback","rewriting the template in the parent thread","update_campaign_brief before approve-message","request_user_input or AskUserQuestion during message-generation","QAing the template in the parent thread"],"outputState":"messageDraftRecommendation"},"hardRules":["message_revision_must_render_new_template_before_approval_question","message_revision_saves_only_after_approve_message","message_generation_must_not_call_request_user_input","message_generation_must_transition_to_message_review_only_after_renderable_draft_state","message_drafting_agent_first_no_silent_parent_fallback","message_revision_routes_to_message_drafting_agent","message_qa_routes_to_message_drafting_agent","filter_path_requires_saved_filters_before_message_review","only_message_drafting_may_spawn_background_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":["Rendered fallback sample","Concerns","QA receipt","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","validation notes","QA details"],"copyMustInclude":["one recommended template","one rendered sample","token rules and fallbacks","full list remains paused"],"copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"],"revisionRenderRule":"After revise_messaging, QA, or copy feedback, route feedback to Message Drafting; show revised template/example; never ask if an unseen version is better.","parentMustNot":["rewrite template from memory","invent validation summary without Message Drafting output","render qaReceipt, risk notes, or renderedFallbackSample on the normal happy path"]},{"action":"ask_message_review_choice","uses":"request_user_input","choices":["approve-message","revise-messaging"],"copyMustInclude":["approve this message template and continue","template approval only locks the draft direction; final Start still controls sending"],"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":"After saved-filter approval and approve-message, save the template and queue bounded Enrich Prospect cells by selector.","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/sellable-cleanup-rules.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","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","this approves reviewed draft rows for setup; final Start still controls sending"],"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_message_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":"start_campaign_message_preparation","requiredValues":{"targetPreparedMessages":"{requestedMessageCount}","approvalMode":"{scheduleOrApproveIntent ? 'approve' : 'mark_ready'}"},"intentMapping":{"scheduleSends":"approve the exact requested send count"},"when":"explicit_count_or_schedule","batchStrategy":"max250","rowBudgetStrategy":"sample100_cap2500"},{"tool":"get_campaign_message_preparation_status","copyMustInclude":["preparation job status","checked rows","target","stop reason"],"when":"prep_job_started"},{"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, Settings is open, lead research/filtering happened outside LinkedIn, 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":"Say the recommended follow-up sequence is attached, final launch review is open, and nothing sends 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":["cancel_campaign_message_preparation","get_campaign_message_preparation_status","start_campaign_message_preparation","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"],"boundedLaunchRule":"preparation job approved X; no broad approve-all."},{"tool":"start_campaign","requiredFields":["campaignId"],"persistsCurrentStep":"running","watchNarrationRule":"After start_campaign succeeds, say final greenlight was accepted, campaign is live/running, and progress can be watched."}],"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,9 +0,0 @@
1
- {
2
- "parallelMode": "wide",
3
- "agentCount": 6,
4
- "maxToolCallsPerAgent": 2,
5
- "senderMaxAgents": 2,
6
- "senderMaxToolCallsPerAgent": 3,
7
- "progressMode": true,
8
- "debugMode": true
9
- }