@sellable/mcp 0.1.189 → 0.1.191

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -435,7 +435,7 @@ Sellable writing workflows.
435
435
 
436
436
  ### "Sellable not configured" Error
437
437
 
438
- Create `~/.sellable/config.json` with your token. Get one at https://app.sellable.dev/settings/integrations.
438
+ Create `~/.sellable/config.json` with your token. Get one at https://app.sellable.dev/settings?tab=integrations.
439
439
 
440
440
  ### Token Not Working
441
441
 
package/dist/auth.js CHANGED
@@ -72,7 +72,7 @@ export function getConfig() {
72
72
  "}\n\n" +
73
73
  "Config path resolution order:\n" +
74
74
  `${renderConfigPathOrder(configPathCandidates)}\n\n` +
75
- "Get your token at: https://app.sellable.dev/settings/integrations\n" +
75
+ "Get your token at: https://app.sellable.dev/settings?tab=integrations\n" +
76
76
  "Then run list_workspaces + set_active_workspace to select a workspace.");
77
77
  }
78
78
  try {
@@ -529,7 +529,7 @@ function buildSourceImportWatchNarration({ provider, selectedPostCount, estimate
529
529
  : "Apollo";
530
530
  const sourceDetail = provider === "signal-discovery"
531
531
  ? `${selectedPostCount ?? "selected"} approved post${selectedPostCount === 1 ? "" : "s"}${typeof estimatedEngagers === "number"
532
- ? ` with about ${estimatedEngagers.toLocaleString("en-US")} people to check`
532
+ ? ` with up to ${estimatedEngagers.toLocaleString("en-US")} people to check before top-level scrape adjustment`
533
533
  : ""}`
534
534
  : `the approved ${providerLabel} source`;
535
535
  const targetDetail = typeof targetLeadCount === "number"
@@ -806,8 +806,8 @@ function buildSignalDiscoverySourceRecommendation({ selectedPosts, targetEngager
806
806
  ? "the approved buyer-search target"
807
807
  : `the ${Math.round(defaultFitRate * 100)}% starting estimate`;
808
808
  const selectedPoolCopy = recommendedCount < selectedCount
809
- ? `**Posts considered:** ${selectedCount.toLocaleString("en-US")} selected posts with ${formatApproxInteger(totalVisibleEngagement)} public reactions/comments<br>\n**Recommended first set:** ${recommendedCount.toLocaleString("en-US")} post${recommendedCount === 1 ? "" : "s"} with ${formatApproxInteger(recommendedVisibleEngagement)} public reactions/comments and up to ${formatApproxInteger(scrapePlan.estimatedEngagers)} people to check<br>`
810
- : `**Public activity in the selected posts:** ${formatApproxInteger(totalVisibleEngagement)} reactions/comments<br>\n**People we can check from this set:** up to ${formatApproxInteger(scrapePlan.estimatedEngagers)}<br>`;
809
+ ? `**Posts considered:** ${selectedCount.toLocaleString("en-US")} selected posts with ${formatApproxInteger(totalVisibleEngagement)} visible public reactions/comments<br>\n**Recommended first set:** ${recommendedCount.toLocaleString("en-US")} post${recommendedCount === 1 ? "" : "s"} with ${formatApproxInteger(recommendedVisibleEngagement)} visible public reactions/comments and up to ${formatApproxInteger(scrapePlan.estimatedEngagers)} people to check before top-level comment and dedupe adjustment<br>`
810
+ : `**Visible public activity in the selected posts:** ${formatApproxInteger(totalVisibleEngagement)} reactions/comments<br>\n**People we can check from this set:** up to ${formatApproxInteger(scrapePlan.estimatedEngagers)} before top-level comment and dedupe adjustment<br>`;
811
811
  const message = `## Source Recommendation
812
812
 
813
813
  Start with LinkedIn posts.
@@ -821,7 +821,7 @@ I found ${recommendedCount.toLocaleString("en-US")} recommended LinkedIn post${r
821
821
 
822
822
  ### Recommended posts
823
823
 
824
- | Post | Why this post | Public activity | People we can check |
824
+ | Post | Why this post | Visible public activity | People we can check |
825
825
  |---|---|---:|---:|
826
826
  ${tableRows || "| Recommended posts | Campaign-matched public activity | - | - |"}
827
827
 
@@ -2579,7 +2579,7 @@ export async function importLeads(input) {
2579
2579
  maxPostsToScrape: normalizePositiveInteger(maxPostsToScrape) ?? null,
2580
2580
  limitedSelectedPosts: importSelection.limited,
2581
2581
  targetLeadCount: cappedTargetLeadCount ?? null,
2582
- message: `Started scraping ${postsToScrape.length} posts (~${result.estimatedEngagers} people to check). Leads will appear as scraping completes.${importSelection.limited
2582
+ message: `Started scraping ${postsToScrape.length} posts (up to ~${result.estimatedEngagers} people to check before top-level scrape adjustment). Leads will appear as scraping completes.${importSelection.limited
2583
2583
  ? ` Limited from ${uniqueSelectedPosts.length} selected posts by the approved source-capacity scrape plan.`
2584
2584
  : ""} The watched campaign has been moved to confirm-lead-list with import progress copy. Keep calling wait_for_lead_list_ready until it reports ready, failed, or cancelled before confirm_lead_list; only confirm a partial list with allowPartialSourceList after the user explicitly asks to keep going early. Do not call update_campaign to fix that step.`,
2585
2585
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sellable/mcp",
3
- "version": "0.1.189",
3
+ "version": "0.1.191",
4
4
  "type": "module",
5
5
  "description": "Sellable MCP server for Claude Code and Codex campaign workflows",
6
6
  "main": "dist/index.js",
@@ -132,9 +132,10 @@ label it explicitly as not estimated from this run.
132
132
  Before any provider prompt, search, source scout, or signal-discovery call, show
133
133
  one source-plan gate and ask for approval. Write this like a fifth grader could
134
134
  understand it: short sentences, no internal labels, and no GTM shorthand. The
135
- plan must be visible before the question, and this first approval only
136
- authorizes finding the best places to look for buyers. It does not add anyone
137
- to the campaign yet. The gate should say:
135
+ order is strict: first show the plan in chat, then open the approval question.
136
+ Do not open a generic approval first and reuse it after the plan. This first
137
+ approval only authorizes finding the best places to look for buyers. It does
138
+ not add anyone to the campaign yet. The gate should say:
138
139
 
139
140
  - the buyer groups or places we could check
140
141
  - the best place to start
@@ -183,9 +184,11 @@ instead and explain it in plain words once. Do not call `search_signals`,
183
184
  `fetch_post_engagers`, or provider-scoped subagents until the user approves this
184
185
  source plan or explicitly chooses a different source.
185
186
 
186
- If the user already answered a provider approval such as "Approve Prospeo plan",
187
- that answer satisfies the source-plan gate. Persist the approved provider and
188
- run the scouting/search next; do not ask a second source-plan approval question.
187
+ If the user answers a provider approval such as "Approve Prospeo plan" after
188
+ seeing the source plan, that answer satisfies the source-plan gate. Persist the
189
+ approved provider and run the scouting/search next; do not ask a second
190
+ source-plan approval question. A brief approval, generic "approve plan", or
191
+ provider state from before the visible source plan does not satisfy this gate.
189
192
 
190
193
  For hiring-led campaigns, do not default to Sales Nav just because the target is
191
194
  a role search. Prospeo is the primary lane when the brief asks for companies
@@ -187,10 +187,10 @@ when not hiring-led. For hiring-by-role signals, start with Prospeo because
187
187
  `company_job_posting_quantity`; Sales Nav does not provide hiring-by-role filters.
188
188
 
189
189
  Before any provider prompt/search/scout call, move the watched campaign to
190
- provider/source selection, then show one `## Find Buyers Plan` before
191
- `request_user_input`, without repeating the watch URL. The plan must appear in
192
- chat before the question. This approval only authorizes finding where to look.
193
- It does not add anyone yet. Write:
190
+ source selection, show `## Find Buyers Plan`, then open
191
+ `request_user_input`, without repeating the URL. The plan must appear
192
+ before the question. Never ask first and reuse that approval. This only
193
+ authorizes where to look; it does not add anyone yet. Write:
194
194
 
195
195
  - the buyer groups or places we could check
196
196
  - the best place to start
@@ -211,12 +211,12 @@ specific. If LinkedIn engagement is recommended, name the exact post themes in
211
211
  plain language. Avoid using
212
212
  "Signal Discovery", "lead-source scouting", "source scouting", "lane", "provider",
213
213
  "precision/scale tradeoff", "evidence quality", "pilot volume", "workflow
214
- pain", or "ICP" in customer-facing chat; those are internal labels. If the
214
+ pain", or "ICP" in chat; those are internal labels. If the
215
215
  campaign looks unlikely
216
- to have relevant public conversations, recommend the specific Sales Nav or
217
- Prospeo path instead and explain it in plain words once. An approval like
218
- "Approve Prospeo plan" satisfies this gate; save that approved path and search
219
- next. Do not ask a second source-plan question. Do not call `search_signals`,
216
+ to have public conversations, recommend the specific Sales Nav or
217
+ Prospeo path instead and explain it in plain words once. Only the approval
218
+ question shown after that visible plan, or an explicit source choice made after
219
+ seeing it, satisfies this gate. Do not ask a second source-plan question. Do not call `search_signals`,
220
220
  `search_sales_nav`,
221
221
  `search_prospeo`,
222
222
  `fetch_post_engagers`, or provider-scoped subagents until the user approves this
@@ -243,8 +243,9 @@ call, move the watched campaign to the provider/source page, then give the user
243
243
  a compact source plan in grade-5 language: "here are the places we could look, I
244
244
  recommend starting here, here is why, and here is where I'll look next if this
245
245
  is too thin." The order matters: show the plan in normal chat first, then open
246
- the approval question. Do not reprint the watch link during source/provider
247
- selection. Ask them to approve that plan or choose a different source. Use plain
246
+ the approval question. Do not reuse a brief approval, a generic "approve plan",
247
+ or provider state from before the user saw the plan. Do not reprint the watch
248
+ link during source/provider selection. Ask them to approve that plan or choose a different source. Use plain
248
249
  customer language for the boundary: "This only approves me to look for the best
249
250
  places to find buyers. I won't add anyone yet." After brief approval, say
250
251
  "Brief approved. Next, we'll choose where to find buyers. I won't add anyone
@@ -353,6 +354,12 @@ At the brief approval gate, do not add a negative list of future steps that are
353
354
  not approved. Ask approve/revise in plain language, then move to choosing where
354
355
  to find buyers.
355
356
 
357
+ At message review, if the user asks for edits, write the revised template and
358
+ examples before asking approval again. Do not ask whether an unseen revision is
359
+ better. Save to the campaign only after `approve-message`.
360
+ During message generation or revision, do not call `request_user_input` or
361
+ `AskUserQuestion`; render the revised copy first, then enter message review.
362
+
356
363
  ## What To Avoid
357
364
 
358
365
  - Do not mention MCP namespaces, prompt chunking, plugin cache paths, local skill
@@ -1 +1 @@
1
- {"version":"v2.1-compact","workflow":"create-campaign-v2","principle":"CampaignOffer state and watch link are canonical. Create the watched shell, approve source, materialize and confirm the source list, internally process the first campaign-table execution slice, save rubrics for approval, approve the message template, then run the bounded filter/message cascade before Settings, sequence, and explicit start.","normalCustomerPath":"Use campaign state, MCP responses, and concise watchNarration. Do not create, read, link, or surface local draft files. Print the watch URL only once at the initial brief handoff, using the exact tokenized create_campaign.watchUrl; after that, update currentStep/watchNarration and describe the visible app state without repeating the link.","legacyCompatibility":{"validationSubskill":"create-campaign-v2-validation","tailSubskill":"create-campaign-v2-tail","rule":"Legacy validation/rehearsal files are opt-in diagnostics only and are not active flow gates."},"commitGateChoices":["approve","revise-brief","revise-leads","revise-rubric","revise-messaging","abort"],"canonicalStateFields":["campaignId","watchUrl","campaignBrief","currentStep","watchNarration","interactionMode","providerSearchAssociation","selectedLeadListId","workflowTableId","filterChoice","leadScoringRubrics","messageDraftRecommendation","approvedMessageTemplate","senderIds","sequenceTemplate","runningState"],"watchNarrationTransitionContract":{"rule":"Every watched currentStep switch must include fresh watchNarration.","requiredFields":["stage","headline","visibleState","agentIntent","nextAction"],"copyMustInclude":["what just happened","current visible page/state","next user action"],"appliesToTools":["create_campaign","update_campaign","attach_recommended_sequence","start_campaign"],"oneTimeWatchLinkPolicy":"After the initial brief handoff, customer-facing progress and approval turns must not print another watch link unless the user explicitly asks for it or link recovery is required."},"lazyReferences":{"watch":["references/watch-link-handoff.md","references/watch-guide-narration.md"],"source":["references/lead-validation-preview.md","references/step-13-import-leads.md"],"filter":["references/filter-leads.md"],"message":[],"tail":["references/sample-validation-loop.md","references/step-15-re-cascade.md","references/final-handoff-contract.md"]},"safetyBoundaries":["Do not call list_senders before Settings after message approval.","Do not import leads until the source decision is approved.","Do not queue cells until confirmed source rows exist in the campaign and the message/filter gates are satisfied.","Do not call start_campaign until the user explicitly confirms launch.","Do not use local files as durable state in normal customer runs."],"yoloMode":{"triggerPhrases":["yolo","--yolo","mode=yolo","autopilot","use best guesses","use best estimates","answer for me","just run it"],"identityRequestOnlyWhenMissing":true,"operatorDirectionsRule":"Treat freeform directions supplied at invocation or later as durable operator directions; newest conflicting direction wins.","setInteractionModeAfterCampaignCreate":"autonomous","autoSelectsPreLaunchChoices":["campaign focus","brief approval","source plan","source review/import approval","filters vs skip filters","filter rubric approval","message template approval when recommendation is approve-message","generated message review when quality floor passes","sender selection when exactly one connected sender is safe"],"mustPauseFor":["missing LinkedIn profile URL or handle","missing credentials or required data","ambiguous choice with no reasonable estimate","source/message/filter quality floor failure","final live launch confirmation"],"neverAutoStart":true},"steps":[{"id":"bootstrap","label":"Bootstrap","onEnter":[{"tool":"bootstrap_create_campaign","requiredValues":{"flowVersion":"v2"}},{"tool":"get_subskill_prompt","requiredValues":{"subskillName":"create-campaign-v2"},"purpose":"load the compact entry prompt once"}],"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?\" If the user provides a bare handle like `csreyes92` or a `/in/...` path, normalize it to `https://www.linkedin.com/in/{handle}/`. If the user provides a non-profile URL, company page, or anything else, ask again for the person's LinkedIn profile URL or handle before strategy questions.","requiresLinkedInProfileUrl":true,"doNotAcceptAsIdentityInput":["non-profile URL","company page","company/name-only input without a profile handle"],"acceptsLinkedInProfileHandle":true,"normalizeLinkedInProfileHandleToUrl":true},{"action":"run_campaign_identity_research_before_strategy_packet","target":"research-sender","requiredCompletion":"complete_sender_research","allowedTools":["get_subskill_prompt","fetch_linkedin_profile","fetch_company","fetch_linkedin_posts","fetch_company_posts","WebFetch","WebSearch","complete_sender_research"],"requiredInput":"LinkedIn profile URL or handle"},{"action":"confirm_target_before_strategy_packet","uses":"request_user_input","singleChoice":true,"question":"Who should we target first?","options":["Use Sellable's researched recommendation","Choose a different buyer","I'm not sure yet"],"copyMustInclude":["research basis","recommendation is editable","who to target"],"requiredOption":"Other / custom","neverCollectOpenTextWithStructuredQuestion":true,"skipIf":"user already supplied a clear target or yolo_mode with any reasonable best-estimate target"},{"action":"confirm_current_offer_before_strategy_packet","uses":"request_user_input","question":"What should we pitch to prospects first?","options":["Use Sellable's researched recommendation","Use my current pitch","Shape the pitch together"],"copyMustInclude":["research basis","public LinkedIn research may be stale","recommendation is editable","pitch to prospects"],"skipIf":"user already supplied a clear current offer or yolo_mode with any reasonable researched recommendation"},{"action":"confirm_trust_proof_before_strategy_packet","uses":"request_user_input","singleChoice":true,"question":"What is the strongest credibility signal we can lead with?","options":["Use Sellable's recommended proof","Use customer proof or a case study","Use founder or company credibility"],"copyMustInclude":["build trust with prospects","recommendation is editable","proof to use or avoid"],"requiredOption":"Other / custom","skipIf":"user already supplied clear proof to use or avoid, or yolo_mode with any reasonable researched proof"},{"action":"choose_prospect_source_path_before_strategy_packet","uses":"request_user_input","singleChoice":true,"question":"How should we find prospects?","options":["Find prospects for me","Use a CSV of LinkedIn profiles","Use a CSV of company domains"],"copyMustInclude":["prospects","no one added yet","find prospects for me"],"skipIf":"user already supplied a source path or yolo_mode with any reasonable source path"},{"action":"infer_strategy_packet_in_yolo_mode","when":"yolo_mode","skipStructuredSetupQuestions":true,"inferFields":["buyer segment","offer/CTA","proof to use or avoid","lead source","filter choice","message direction"],"sourceOfTruth":"identity/company lookup plus operator directions","mustStateAssumptionsInBrief":true},{"action":"render_supplied_setup_receipt_before_brief_when_setup_questions_skip","when":"any setup question is skipped because the user already supplied or yolo mode inferred identity, target, offer, proof, or source","output":"normal chat receipt before the campaign brief or watch-link handoff","copyMustInclude":["Accepted LinkedIn input: normalized to the required LinkedIn profile URL.","Offer path: supplied current offer or Sellable's researched recommendation; you can replace it with your current offer."],"copyMustNotInclude":["Campaign Identity"]},{"action":"create_watchable_campaign_shell_with_v1_brief","tool":"create_campaign","requiredFields":["name","campaignBrief","clientProspectId or senderLinkedinUrl","currentStep","watchNarration"],"requiredValues":{"currentStep":"create-offer","watchNarration.stage":"brief"},"capture":["campaignId","watchUrl"],"canonicalStateWrites":["campaignId","watchUrl","campaignBrief","currentStep:create-offer"]},{"action":"surface_campaign_shell_watch_link","watchUrlSource":"create_campaign.watchUrl","requiredWatchUrlShape":"exact create_campaign.watchUrl; direct /campaign-builder/{campaignId}?mode={claude|codex} URL with workspaceId and token","copyMustInclude":["We'll keep building the campaign in this chat.","You can watch it being built in real time in the app here:","I'll ask for your approval whenever I need your expertise or taste before moving forward.","Send changes here."],"immediateNextMainChatLine":"Ask for brief approval now. After approval, say: \"Brief approved. Next, we'll choose where to find buyers. I won't add anyone yet.\"","codexBrowserHandoff":{"openWhenAvailable":false,"printWatchLinkOnly":true,"tellUserCommandEnterOrClick":false,"mustNotUseBrowserAutomation":true,"fallbackMustNotClaimInspection":true},"copyMustAvoid":["bare /campaign-builder/{campaignId} URL","derived watch URL","second Watch link:","Next, we'll choose where to find buyers"],"oneTimeOnly":true}],"allowedTools":["get_subskill_prompt","fetch_company","fetch_company_posts","fetch_linkedin_profile","fetch_linkedin_posts","complete_sender_research","create_campaign","AskUserQuestion","request_user_input"],"doNotAllow":["list_senders","save_rubrics","import_leads","confirm_lead_list","queue_cells","start_campaign"],"waitFor":["campaign_shell_created","brief_ready","confirm_with_user"],"transitions":{"campaign_shell_created":"brief-review","brief_ready":"brief-review","confirm_with_user":"brief-review"}},{"id":"brief-review","label":"Brief review","onEnter":[{"action":"render_brief_inline","minimumVisibleDetail":"campaign direction, buyer, offer, proof, source plan, safety gates","copyMustInclude":["This brief is based on Sellable's research and your answers.","If your site or LinkedIn is stale","Do you agree with this researched campaign direction?"],"copyMustNotInclude":["quoted first-message copy","Message Angle as final proof","This approval covers","does not approve adding people","does not approve scraping posts","selecting a sender","attaching a sequence","Campaign Identity","what should this campaign sell first","sell first","[from you]","[from LinkedIn]","[from website]","[from case study]","[Sellable recommendation]"],"messageHintRule":"If any message-shape hint remains, keep it as non-final bullets and say real examples come after leads are found and filtered.","approvalBoundaryRule":"Keep this gate positive and short. Do not list later steps this approval does not cover; ask approve/revise and then move to choosing where to find buyers.","sectionLabelRule":"Use ## Sender and Company for the person/company context; never render ## Campaign Identity.","skipIf":"the full brief was already rendered in the current shell-creation handoff before create_campaign returned","renderOnceRule":"The brief must be visible exactly once before approval. Do not render it again immediately after shell creation; append the one watch-link handoff and ask the approval question.","copyMustAvoid":["duplicate brief render after shell creation","second Watch link:"]},{"action":"ask_brief_choice","uses":"request_user_input","choices":["Approve brief","Revise brief","Pause here"],"skipIf":"yolo_mode; auto_continue after rendering assumptions unless brief quality floor fails"}],"requiredCampaignState":["campaignId","watchUrl","campaignBrief","currentStep"],"allowedTools":["AskUserQuestion","request_user_input","update_campaign"],"doNotAllow":["create_campaign","list_senders","import_leads","confirm_lead_list","queue_cells","start_campaign"],"waitFor":["user_brief_confirmed","revise_brief","auto_continue"],"transitions":{"user_brief_confirmed":"find-leads","revise_brief":"brief-interview","auto_continue":"find-leads"}},{"id":"find-leads","label":"Find leads","sourceSelectionFunnel":{"preScoutRecommendationGate":{"required":true,"label":"Find buyers plan approval","mustHappenBefore":["get_provider_prompt","search_signals","search_sales_nav","search_prospeo","fetch_post_engagers","source-scout dispatch"],"show":["buyer groups or places we could check","best place to start","why the right buyers are likely to be there","what signs the next search will check","where to look next if the first place is too thin","what approval authorizes"],"approvalAuthorizes":"looking for the best places to find buyers; no one added yet","explanationMustInclude":["where the right buyers are already talking","why that place fits this campaign","enough likely prospects","what counts as a good sign","where to look next if thin","I won't add anyone yet","choose where to find buyers"],"yoloMode":{"autoApproveRecommendedLane":true,"mustShowAssumedChoice":true,"pauseIfConfidenceLow":true},"customerLanguage":{"readability":"grade-5","requiredHeading":"Find Buyers Plan","prefer":["place to look","best place to start","people to check","prospects","I won't add anyone yet"],"avoid":["lane","source scouting","provider","precision/scale tradeoff","evidence quality","pilot volume","ICP","workflow pain","lead-source scouting","not importing leads"],"example":"Brief approved. Next, we'll choose where to find buyers. I won't add anyone yet. I recommend starting with people already talking on LinkedIn about [plain topic]. That should help us find people who already care about [plain problem]. If this is too thin, I'll try [plain fallback] next.","termRule":{"buyers":"target market","peopleToCheck":"raw reactions/comments before fit is known","prospects":"likely usable people after fit","leads":"campaign rows"}},"questionOrder":"update_campaign to pick-provider, render ## Find Buyers Plan in normal chat without repeating the watch link, then open the structured approval question","copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"]},"defaultWhenSourceUnspecified":["signal-discovery","sales-nav-recent-active","sales-nav-general","prospeo"],"userFacingFallbackOrder":["LinkedIn posts where the right buyers are already talking","people with the right titles who recently posted on LinkedIn","people with the right titles from a broader LinkedIn search","a broader company and contact search"],"parallelAllowedOnlyWhen":["user explicitly requested source comparison","resuming already-started parallel scouts","first viable source is borderline and one cheap fallback check is needed"]},"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."},"purpose":"show the visible source-plan approval checkpoint before provider lanes"},{"tool":"get_source_scout_registry","purpose":"load canonical source scout names before optional branch launch"},{"action":"render_find_buyers_plan_inline","oneShot":true,"requiredPrecondition":"currentStep=pick-provider has been saved with update_campaign","mustHappenAfter":["update_campaign currentStep=pick-provider"],"mustHappenBefore":["ask_find_buyers_plan_choice","get_provider_prompt","search_signals","search_sales_nav","search_prospeo","fetch_post_engagers","source-scout dispatch"],"output":"normal chat text before any request_user_input approval panel","requiredHeading":"Find Buyers Plan","requiredInlineFields":["plain-language buyer groups or places for this campaign","best place to start","why the right buyers are likely to be there","what signs the next search will check","where to look next if the first place is too thin","what approval authorizes"],"copyMustInclude":["Find Buyers Plan","best place to start","I won't add anyone","choose where to find buyers","I won't add anyone yet"],"copyMustAvoid":["lane","source scouting","provider","precision/scale tradeoff","evidence quality","pilot volume","ICP","workflow pain","lead-source scouting","not importing leads","Watch link:","campaign-builder URL","repeat the watch URL"],"approvalBoundary":"Explain what approval authorizes before asking: looking for the best places to find buyers; no one is added yet.","linkPolicy":"Do not include another watch link. The first brief handoff already gave the user the live app URL."},{"action":"ask_find_buyers_plan_choice","uses":"request_user_input","oneShot":true,"requiredPrecondition":"render_find_buyers_plan_inline completed in normal chat after pick-provider state was saved","skipIf":"approved or leadSourceProvider or yolo_mode auto-selected source_lane_approved","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"]},{"action":"persist_provider_search_step_before_search","tool":"update_campaign","requiredPrecondition":"source_lane_approved","providerCurrentStepMap":{"signal-discovery":"signal-discovery","sales-nav":"sales-nav","prospeo":"prospeo"},"requiredValues":{"leadSourceType":"new","leadSourceProvider":"approved provider","currentStep":"provider-specific current step","watchNarration.stage":"find-leads","watchNarration.headline":"Searching the approved place","watchNarration.safety":"Looking only. No one is added yet."},"mustRunBefore":["search_signals","search_sales_nav","search_prospeo","fetch_post_engagers"]},{"action":"run_sequential_source_funnel","requiredPrecondition":"source_lane_approved","defaultOrder":["source-scout-linkedin-engagement","source-scout-sales-nav","source-scout-prospeo-contact"],"campaignOfferIdRequired":true,"stopOnFirstViableUnlessComparisonRequested":true}],"requiredCampaignState":["campaignId","campaignBrief","currentStep"],"allowedTools":["get_source_scout_registry","get_provider_prompt","lookup_sales_nav_filter","search_sales_nav","search_prospeo","search_signals","select_promising_posts","fetch_post_engagers","fetch_company","fetch_linkedin_profile","load_csv_linkedin_leads","load_csv_domains","get_rows_minimal","update_campaign","AskUserQuestion","request_user_input","get_campaign_navigation_state"],"doNotAllow":["create_campaign","list_senders","save_rubrics","import_leads","confirm_lead_list","queue_cells","start_campaign"],"waitFor":["lead_review_ready","revise_brief","confirm_with_user"],"transitions":{"lead_review_ready":"lead-review","revise_brief":"brief-interview","confirm_with_user":"lead-review"}},{"id":"lead-review","label":"Source approval","onEnter":[{"action":"show_source_decision_card","requiredInlineFields":["primary source and exact filters/recipe","specific source 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 will happen after approval"],"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 source-action copy only. Include: \"After approval, I will build the source list, add it to the campaign, and review the first 15 leads before we scale.\" Never write \"This approval covers\" or \"It does not approve\" sentences.","linkPolicy":"Do not include another watch link in Source Recommendation; describe the source decision only."},{"action":"ask_source_review_choice","uses":"request_user_input","choices":["Approve scraping N recommended LinkedIn posts","Run the approved source import","Revise source","Pause here"],"approvalChoiceLabelsByProvider":{"signal-discovery":"Approve scraping {scrapePostCount} recommended LinkedIn posts?","sales-nav":"Import the approved Sales Nav source list","prospeo":"Import 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"},"purpose":"load tail contract before execution tools"},{"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","purpose":"poll until the source lead-list import reaches a terminal complete/failed/cancelled state before copying rows into the campaign table","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","purpose":"ask filter choice immediately; no post-lead registries, filter refs, or message prompts first","maxCustomerCopyLines":4,"copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"],"linkPolicy":"Ask add filters vs skip filters without repeating the watch link."}],"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 generation 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_post_lead_agents_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":"post-lead-workstreams","filters_skipped":"message-generation","revise_leads":"find-leads"}},{"id":"post-lead-workstreams","label":"Filter workstream","onEnter":[{"action":"persist_add_filters_approval","tool":"update_campaign","when":"filters_enabled","requiredFields":["campaignId","enableICPFilters","currentStep","watchNarration"],"requiredValues":{"enableICPFilters":true,"currentStep":"create-icp-rubric","watchNarration.stage":"fit-message","watchNarration.headline":"Create filter rules","watchNarration.visibleState":"Filters are enabled and the browser is showing Filter Rules while Codex defines the rubric.","watchNarration.nextAction":"Review saved filter rules"},"mustRunBefore":["get_post_find_leads_scout_registry","launch_lead_fit_builder_after_filter_choice","save_rubrics"]},{"tool":"get_post_find_leads_scout_registry","purpose":"load canonical post-lead worker names only after the user has chosen filters"},{"action":"launch_lead_fit_builder_after_filter_choice","mode":"parallel_when_host_supports_subagents","target":"post-find-leads-filter-scout","inputs":["campaignId","campaignBrief","selectedLeadListId","workflowTableId","reviewBatchRowIds/hash","filterChoice"],"stateSource":"live campaign/table","debugFilesOptionalOnly":true},{"action":"launch_message_draft_builder_after_filter_decision","mode":"parallel_when_host_supports_subagents","target":"post-find-leads-message-scout","when":"filters_enabled","doesNotQueueCells":true,"customerNarration":"Say message agent is drafting."},{"action":"save_filter_rubrics_to_campaign","tool":"save_rubrics","when":"filters_enabled and rubrics are production-shaped","requiredFields":["campaignOfferId","leadScoringRubrics"],"writesCampaignState":"leadScoringRubrics","requiredSideEffects":{"enableICPFilters":true,"currentStep":"create-icp-rubric","watchNarration.headline":"Filter rules saved for review"}},{"action":"ask_filter_rubric_review_choice","uses":"request_user_input","choices":["Approve filters","Revise filters","Pause"],"purpose":"let the user read saved rubrics before Filter Leads or enrichment","copyMustInclude":["These rules prevent wasted sends before I score/import the list.","Recommended because of the researched ICP, source sample, and repeated false-positive patterns.","one pass example and one block example when available","approval is for the filter rules","Approve these filter rules."],"autoSelectIf":"yolo_mode and rubrics are production-shaped","copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"]}],"requiredCampaignState":["campaignId","campaignBrief","selectedLeadListId","workflowTableId"],"allowedTools":["get_subskill_prompt","get_subskill_asset","get_post_find_leads_scout_registry","save_rubrics","get_campaign","get_rows_minimal","update_campaign","get_campaign_navigation_state","AskUserQuestion","request_user_input","Task","spawn_agent"],"doNotAllow":["create_campaign","list_senders","import_leads","confirm_lead_list","queue_cells","start_campaign","check_rubric","generate_messages"],"waitFor":["filter_rubrics_approved","revise_leads","revise_rubric","revise_messaging"],"hardRules":["after_save_rubrics_currentStep_must_stay_create-icp-rubric_until_filter_approval","filter_approval_required_before_apply-icp-rubric_or_queue_cells","do_not_move_browser_to_messages_until_filter_leads_step_is_current_or_filters_are_explicitly_skipped","no_post_lead_worker_or_deep_prompt_before_filter_choice","lead_fit_builder_starts_only_after_filters_enabled","msg_draft_after_filter_choice","msg_draft_no_cells"],"transitions":{"filter_rubrics_approved":"message-generation","revise_leads":"find-leads","revise_rubric":"filter-rubric","revise_messaging":"message-generation","confirm_with_user":"message-review"}},{"id":"filter-rubric","label":"Rubric revision","onEnter":[{"tool":"get_subskill_asset","requiredValues":{"subskillName":"create-campaign-v2","assetPath":"references/filter-leads.md"}},{"tool":"save_rubrics","requiredFields":["campaignOfferId","leadScoringRubrics"],"writesCampaignState":"leadScoringRubrics","requiredSideEffects":{"enableICPFilters":true,"currentStep":"create-icp-rubric","watchNarration.headline":"Filter rules saved for review"}},{"action":"ask_filter_rubric_review_choice","uses":"request_user_input","choices":["Approve filters","Revise filters","Pause"],"copyMustInclude":["These rules prevent wasted sends before I score/import the list.","approval object is filter rules only","approval is for the filter rules","Approve these filter rules."],"autoSelectIf":"yolo_mode and rubrics are production-shaped","copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"]}],"requiredCampaignState":["campaignId","workflowTableId"],"allowedTools":["get_subskill_asset","save_rubrics","AskUserQuestion","request_user_input"],"doNotAllow":["create_campaign","list_senders","import_leads","confirm_lead_list","update_campaign","queue_cells","start_campaign","check_rubric"],"waitFor":["filter_rubrics_approved","revise_leads","confirm_with_user","auto_continue"],"transitions":{"filter_rubrics_approved":"message-generation","revise_leads":"find-leads","confirm_with_user":"message-generation","auto_continue":"message-generation"}},{"id":"message-generation","label":"Message generation","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":["run background post-find-leads-message-scout when available","get_subskill_prompt({ subskillName: \"generate-messages\", offset, limit }) until hasMore=false"],"stateSource":"campaignBrief, source, selectedLeadListId, workflowTableId, execution-slice row ids/hash","outputState":"messageDraftRecommendation"}],"requiredCampaignState":["campaignId","campaignBrief","selectedLeadListId","workflowTableId"],"allowedTools":["get_subskill_prompt","get_campaign","get_rows_minimal","update_campaign","AskUserQuestion","request_user_input"],"toolRules":["Run post-find-leads-message-scout when available; otherwise parent fallback must load get_subskill_prompt({ subskillName: \"generate-messages\" }) from live state.","brief.md, lead-review.md, and lead-sample.json are optional debug context only.","messageDraftRecommendation returns templateRecommendation, tokenFillRules, renderedSample, concerns, status, basisToken, outputAt, outputHash, and error/retry detail.","If campaign/source/table/execution-slice basis does not match, classify the output stale or blocked."],"doNotAllow":["create_campaign","list_senders","save_rubrics","import_leads","confirm_lead_list","queue_cells","start_campaign","generate_messages"],"waitFor":["message_validation_ready","revise_rubric","revise_messaging","confirm_with_user","auto_continue"],"transitions":{"message_validation_ready":"message-review","revise_rubric":"filter-rubric","revise_messaging":"message-generation","confirm_with_user":"message-review","auto_continue":"message-review"}},{"id":"message-review","label":"Message review","onEnter":[{"action":"render_message_review_from_state","requiredState":["campaignBrief","selectedLeadListId","workflowTableId","messageDraftRecommendation"],"requiredVisibleLabels":["## Message Template","## Rendered Example","Good token fill:","My take:","Question: approve-message or revise-messaging?","Recommendation:"],"doNotShowByDefault":["Token Notes","Good omit / fallback","Bad fill to avoid","Token Adherence Table"],"internalPersistenceOnly":["Token Fill Rules","Token Fill Examples","fallback guidance","bad-fill avoidance notes"],"copyMustInclude":["QA receipt before approval","tokens resolved","company/person match","proof claim","prospect angle","language/tone when known","obvious bad fits","Would I take this call?","weak personalization can burn the sender's reputation","full list remains paused"],"copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"]},{"action":"ask_message_review_choice","uses":"request_user_input","choices":["approve-message","revise-messaging"],"copyMustInclude":["approve this message template and continue","nothing sends from this approval"],"autoSelectIf":"yolo_mode and Recommendation is approve-message; revise autonomously when Recommendation is revise-messaging","copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"]},{"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"}],"requiredCampaignState":["campaignId","workflowTableId","messageDraftRecommendation"],"allowedTools":["AskUserQuestion","request_user_input","update_campaign_brief","update_campaign","get_rows_minimal"],"doNotAllow":["create_campaign","list_senders","save_rubrics","import_leads","confirm_lead_list","start_campaign"],"waitFor":["message_approved","revise_messaging"],"transitions":{"message_approved":"validate-sample","revise_messaging":"message-generation"}},{"id":"validate-sample","label":"Validate campaign-table execution slice","currentStepValue":"apply-icp-rubric","reference":"references/sample-validation-loop.md","visibleStepRule":"Filter Leads is reached only after saved-filter approval. On approve-message, save the template, refresh apply-icp-rubric narration, then queue bounded Enrich Prospect cells.","onEnter":[{"tool":"update_campaign","requiredValues":{"currentStep":"apply-icp-rubric","watchNarration.stage":"fit-message"},"purpose":"keep the watched UI on Filter Leads while the approved template starts the bounded cascade","watchNarrationRule":"Say the template is saved and Filter Leads is now running the bounded enrichment and scoring pass."},{"tool":"queue_cells","purpose":"queue ICP/enrichment cells for the internal campaign-table execution slice only","requiredFields":["workflowTableId","reviewBatchRowIds","cellTypes"],"targetCountSource":"reviewBatchRowIds.length (default 15)"},{"tool":"wait_for_campaign_table_ready","purpose":"wait_for_review_batch_scoring_cells"},{"tool":"get_rows_minimal","requiredValues":{"includeRows":false},"purpose":"read compact row status only"},{"tool":"wait_for_rubric_results","requiredFields":["workflowTableId","targetCount","minPassedCount"],"requiredValues":{"includeRows":false,"minPassedCount":1},"minPassedCountSource":"firstPassingRowForMessageStart","readVia":"stats_only_tool_result","customerSummaryPattern":["{checked} leads checked","{passed} passed and have draft messages","{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_cells","wait_for_campaign_table_ready","get_rows_minimal","wait_for_rubric_results","update_campaign","AskUserQuestion","request_user_input"],"doNotAllow":["import_leads","list_senders","start_campaign","enrich_with_prospeo","bulk_enrich_with_prospeo","check_rubric"],"hardRules":["wait_for_rubric_results_never_retain_rows_payload_in_tail_context","wait_for_rubric_results_targetCount_always_explicit","timeout_never_repeats_without_customer_handoff","timeout_or_underfloor_sample_never_advances_to_settings"],"waitFor":["sample_validated","sample_revision_required"],"transitions":{"sample_validated":"auto-execute-messaging","sample_revision_required":"lead-review","revise_leads":"find-leads","revise_rubric":"filter-rubric","escalation_triggered":"escalation"}},{"id":"auto-execute-messaging","label":"Generate initial campaign-row messages","currentStepValue":"auto-execute-messaging","references":["references/parallel-critique-protocol.md","references/thomas-variant-selection.md","references/thomas-revision-filters.md","references/step-15-re-cascade.md"],"onEnter":[{"tool":"queue_cells","batchSize":100,"when":"any passing row has pending or empty Generate Message cell","cellSource":"pending_generate_message_cells_for_passing_rows using approved_campaign_brief_template"},{"tool":"wait_for_rubric_results","purpose":"wait_for_first_passing_generated_message","requiredValues":{"includeRows":false,"minPassedCount":1,"minMessagesCount":1},"readVia":"stats_only_tool_result"},{"action":"observe_generate_message_results","reference":"references/step-15-re-cascade.md"},{"action":"enforce_token_contract","modeFromConfig":"messaging.tokenContract"},{"action":"optional_critique_pass","enabledFromConfig":"messaging.critique.enabled","plan":"85-03","protocol":"references/parallel-critique-protocol.md","sampleSizeFromConfig":"messaging.critique.sampleSize","budgetUsdCapFromConfig":"messaging.critique.budgetUsdCap","perCriticTimeoutFromConfig":"messaging.critique.perCriticTimeoutSeconds","totalTimeoutFromConfig":"messaging.critique.totalTimeoutSeconds","criticsFromConfig":"messaging.critique.critics","enforceFinalizerPassFromConfig":"messaging.critique.synthesis.enforceFinalizerPass","rejectOnFakeProofFromConfig":"messaging.critique.rejectOnFakeProof","rejectOnUnsupportedTokenFromConfig":"messaging.critique.rejectOnUnsupportedToken","onBudgetTrip":"halt_critique_continue_plain_tail","onPerCriticTimeout":"drop_critic_proceed_with_remaining","onTotalTimeout":"persist_plain_message_for_row","onTokenContractViolation":"persist_plain_message_for_row","onFakeProof":"persist_plain_message_for_row"},{"action":"optional_opus_subset","enabledFromConfig":"messaging.critique.opus.enabled","selection":"references/thomas-variant-selection.md","maxMessagesPerPassFromConfig":"messaging.critique.opus.maxMessagesPerPass","budgetUsdCapFromConfig":"messaging.critique.opus.budgetUsdCap","onOpusBudgetTrip":"halt_opus_continue_non_opus","onOpusTokenContractViolation":"fallback_to_non_opus_rewrite"},{"tool":"update_campaign","requiredValues":{"currentStep":"auto-execute-messaging","watchNarration.stage":"review-ready"},"watchNarrationRule":"Say the first passing generated message is ready in Messages; next is review before Settings."},{"action":"ask_generated_message_review_choice","uses":"request_user_input","choices":["Approve reviewed draft rows and continue to Settings","Revise filters","Revise message template","Pause here"],"copyMustInclude":["approve the reviewed draft rows and continue to Settings","tokens resolved","company/person match","proof claim","prospect angle","language/tone when known","obvious bad fits","Would I take this call?","weak personalization can burn the sender's reputation","full list remains paused","nothing sends from this approval"],"autoSelectIf":"yolo_mode and the first passing generated message clears the quality floor","copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"]}],"allowedTools":["get_subskill_asset","get_rows_minimal","queue_cells","wait_for_rubric_results","update_campaign","AskUserQuestion","request_user_input"],"doNotAllow":["import_leads","list_senders","start_campaign","generate_messages"],"hardRules":["critique_failure_never_escalates","critique_sample_size_bounded_by_config","first_passing_generated_message_unblocks_review","critics_fixed_at_targeting_copy_voice","synthesis_enforces_phase_84_token_contract","opus_reserved_for_highest_value_subset","proposed_token_never_persisted_in_rewrite"],"waitFor":["generated_messages_approved","sample_revision_required"],"transitions":{"generated_messages_approved":"awaiting-user-greenlight","revise_filters":"filter-rubric","revise_messaging":"message-generation","escalation_triggered":"escalation"}},{"id":"awaiting-user-greenlight","label":"Settings, sender, sequence, and greenlight","currentStepValue":"awaiting-user-greenlight","reference":"references/final-handoff-contract.md","onEnter":[{"tool":"get_campaign","purpose":"verify senderIds and sequence state before final handoff"},{"tool":"list_senders","purpose":"surface available connected senders only at Settings"},{"tool":"update_campaign","requiredValues":{"currentStep":"settings","watchNarration.stage":"review-ready"},"purpose":"park the watched UI on Settings before sender selection","watchNarrationRule":"Say message review is complete and the browser is now on Settings so the user can connect or choose a sender. Include that lead research and filtering already happened outside the user's LinkedIn account, this sender is only used for approved sending after final launch, and no launch happens from Settings."},{"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 browser is moving to Sequence review. Include that the sequence is editable before launch and still not sending until Start."},{"tool":"attach_recommended_sequence","when":"after senderIds are attached","requiredValues":{"campaignId":"{campaignId}","currentStep":"send","watchNarration.stage":"review-ready"},"watchNarrationRule":"The sequence tool owns the visible beat: say the recommended follow-up sequence is attached, the browser is now on final launch review, and the next action is final launch greenlight. Include that it is still not sending until Start. Do not follow with a separate step-only update_campaign fixup."},{"action":"ask_final_launch_greenlight","uses":"request_user_input","singleChoice":true,"choices":["Start campaign","Review campaign first","Pause here"],"copyMustInclude":["quality confidence means sample messages and prospect angle were checked","launch confidence means sender, sequence, and Start are ready","approved messages begin sending according to the sequence","replies and meetings follow connected settings","you can monitor and pause","no hidden extra approval disappears after clicking Start"],"onUserStart":"claude-greenlight","neverAutoSelectInYolo":true,"copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"]}],"allowedTools":["get_campaign","get_campaign_navigation_state","list_senders","update_campaign","attach_recommended_sequence","AskUserQuestion","request_user_input"],"doNotAllow":["start_campaign","import_leads"],"autoStart":false,"watchRequired":true,"waitFor":["sender_connection_required","sender_attached","sequence_attached","ready_to_launch","user_greenlight","ui_start_detected"],"transitions":{"sender_connection_required":"awaiting-user-greenlight","sender_attached":"awaiting-user-greenlight","sequence_attached":"awaiting-user-greenlight","ready_to_launch":"awaiting-user-greenlight","user_greenlight":"claude-greenlight","ui_start_detected":"running"}},{"id":"claude-greenlight","label":"Explicit launch","reference":"references/final-handoff-contract.md","onEnter":[{"tool":"get_campaign","purpose":"detect_already_running"},{"action":"verify_sequence_and_current_step_before_start","requiredState":["workflowTableId","senderIds","sequenceTemplate","at least one approved generated message","currentStep in awaiting-user-greenlight|claude-greenlight|send"]},{"tool":"start_campaign","requiredFields":["campaignId"],"persistsCurrentStep":"running","watchNarrationRule":"After start_campaign succeeds, the running state must say the final greenlight was accepted, the campaign is now live/running, and the user can watch progress from the campaign."}],"allowedTools":["get_campaign","attach_recommended_sequence","start_campaign","AskUserQuestion","request_user_input"],"watchRequired":true,"waitFor":"campaign_started","transitions":{"campaign_started":"running"}},{"id":"running","label":"Campaign is live","currentStepValue":"running","onEnter":[{"action":"surface_campaign_live_confirmation_without_watch_link","copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"]}],"allowedTools":["get_campaign","AskUserQuestion","request_user_input"],"doNotAllow":["start_campaign"],"terminal":true},{"id":"escalation","label":"Escalation","reference":"references/escalation-ladder.md","allowedTools":["AskUserQuestion","request_user_input"],"doNotAllow":["start_campaign","import_leads"],"transitions":{"revise_brief":"brief-interview","revise_leads":"find-leads","revise_rubric":"filter-rubric","revise_messaging":"message-generation","abort":"abort"}},{"id":"abort","label":"Abort","allowedTools":["AskUserQuestion","request_user_input"],"terminal":true}]}
1
+ {"version":"v2.1-compact","workflow":"create-campaign-v2","principle":"CampaignOffer state and watch link are canonical. Create the watched shell, approve source, materialize and confirm the source list, internally process the first campaign-table execution slice, save rubrics for approval, approve the message template, then run the bounded filter/message cascade before Settings, sequence, and explicit start.","normalCustomerPath":"Use campaign state, MCP responses, and concise watchNarration. Do not create, read, link, or surface local draft files. Print the watch URL only once at the initial brief handoff, using the exact tokenized create_campaign.watchUrl; after that, update currentStep/watchNarration and describe the visible app state without repeating the link.","legacyCompatibility":{"validationSubskill":"create-campaign-v2-validation","tailSubskill":"create-campaign-v2-tail","rule":"Legacy validation/rehearsal files are opt-in diagnostics only and are not active flow gates."},"commitGateChoices":["approve","revise-brief","revise-leads","revise-rubric","revise-messaging","abort"],"canonicalStateFields":["campaignId","watchUrl","campaignBrief","currentStep","watchNarration","interactionMode","providerSearchAssociation","selectedLeadListId","workflowTableId","filterChoice","leadScoringRubrics","messageDraftRecommendation","approvedMessageTemplate","senderIds","sequenceTemplate","runningState"],"watchNarrationTransitionContract":{"rule":"Every watched currentStep switch must include fresh watchNarration.","requiredFields":["stage","headline","visibleState","agentIntent","nextAction"],"copyMustInclude":["what just happened","current visible page/state","next user action"],"appliesToTools":["create_campaign","update_campaign","attach_recommended_sequence","start_campaign"],"oneTimeWatchLinkPolicy":"After the initial brief handoff, customer-facing progress and approval turns must not print another watch link unless the user explicitly asks for it or link recovery is required."},"lazyReferences":{"watch":["references/watch-link-handoff.md","references/watch-guide-narration.md"],"source":["references/lead-validation-preview.md","references/step-13-import-leads.md"],"filter":["references/filter-leads.md"],"message":[],"tail":["references/sample-validation-loop.md","references/step-15-re-cascade.md","references/final-handoff-contract.md"]},"safetyBoundaries":["Do not call list_senders before Settings after message approval.","Do not import leads until the source decision is approved.","Do not queue cells until confirmed source rows exist in the campaign and the message/filter gates are satisfied.","Do not call start_campaign until the user explicitly confirms launch.","Do not use local files as durable state in normal customer runs."],"yoloMode":{"triggerPhrases":["yolo","--yolo","mode=yolo","autopilot","use best guesses","use best estimates","answer for me","just run it"],"identityRequestOnlyWhenMissing":true,"operatorDirectionsRule":"Treat freeform directions supplied at invocation or later as durable operator directions; newest conflicting direction wins.","setInteractionModeAfterCampaignCreate":"autonomous","autoSelectsPreLaunchChoices":["campaign focus","brief approval","source plan","source review/import approval","filters vs skip filters","filter rubric approval","message template approval when recommendation is approve-message","generated message review when quality floor passes","sender selection when exactly one connected sender is safe"],"mustPauseFor":["missing LinkedIn profile URL or handle","missing credentials or required data","ambiguous choice with no reasonable estimate","source/message/filter quality floor failure","final live launch confirmation"],"neverAutoStart":true},"steps":[{"id":"bootstrap","label":"Bootstrap","onEnter":[{"tool":"bootstrap_create_campaign","requiredValues":{"flowVersion":"v2"}},{"tool":"get_subskill_prompt","requiredValues":{"subskillName":"create-campaign-v2"},"purpose":"load the compact entry prompt once"}],"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?\" If the user provides a bare handle like `csreyes92` or a `/in/...` path, normalize it to `https://www.linkedin.com/in/{handle}/`. If the user provides a non-profile URL, company page, or anything else, ask again for the person's LinkedIn profile URL or handle before strategy questions.","requiresLinkedInProfileUrl":true,"doNotAcceptAsIdentityInput":["non-profile URL","company page","company/name-only input without a profile handle"],"acceptsLinkedInProfileHandle":true,"normalizeLinkedInProfileHandleToUrl":true},{"action":"run_campaign_identity_research_before_strategy_packet","target":"research-sender","requiredCompletion":"complete_sender_research","allowedTools":["get_subskill_prompt","fetch_linkedin_profile","fetch_company","fetch_linkedin_posts","fetch_company_posts","WebFetch","WebSearch","complete_sender_research"],"requiredInput":"LinkedIn profile URL or handle"},{"action":"confirm_target_before_strategy_packet","uses":"request_user_input","singleChoice":true,"question":"Who should we target first?","options":["Use Sellable's researched recommendation","Choose a different buyer","I'm not sure yet"],"copyMustInclude":["research basis","recommendation is editable","who to target"],"requiredOption":"Other / custom","neverCollectOpenTextWithStructuredQuestion":true,"skipIf":"user already supplied a clear target or yolo_mode with any reasonable best-estimate target"},{"action":"confirm_current_offer_before_strategy_packet","uses":"request_user_input","question":"What should we pitch to prospects first?","options":["Use Sellable's researched recommendation","Use my current pitch","Shape the pitch together"],"copyMustInclude":["research basis","public LinkedIn research may be stale","recommendation is editable","pitch to prospects"],"skipIf":"user already supplied a clear current offer or yolo_mode with any reasonable researched recommendation"},{"action":"confirm_trust_proof_before_strategy_packet","uses":"request_user_input","singleChoice":true,"question":"What is the strongest credibility signal we can lead with?","options":["Use Sellable's recommended proof","Use customer proof or a case study","Use founder or company credibility"],"copyMustInclude":["build trust with prospects","recommendation is editable","proof to use or avoid"],"requiredOption":"Other / custom","skipIf":"user already supplied clear proof to use or avoid, or yolo_mode with any reasonable researched proof"},{"action":"choose_prospect_source_path_before_strategy_packet","uses":"request_user_input","singleChoice":true,"question":"How should we find prospects?","options":["Find prospects for me","Use a CSV of LinkedIn profiles","Use a CSV of company domains"],"copyMustInclude":["prospects","no one added yet","find prospects for me"],"skipIf":"user already supplied a source path or yolo_mode with any reasonable source path"},{"action":"infer_strategy_packet_in_yolo_mode","when":"yolo_mode","skipStructuredSetupQuestions":true,"inferFields":["buyer segment","offer/CTA","proof to use or avoid","lead source","filter choice","message direction"],"sourceOfTruth":"identity/company lookup plus operator directions","mustStateAssumptionsInBrief":true},{"action":"render_supplied_setup_receipt_before_brief_when_setup_questions_skip","when":"any setup question is skipped because the user already supplied or yolo mode inferred identity, target, offer, proof, or source","output":"normal chat receipt before the campaign brief or watch-link handoff","copyMustInclude":["Accepted LinkedIn input: normalized to the required LinkedIn profile URL.","Offer path: supplied current offer or Sellable's researched recommendation; you can replace it with your current offer."],"copyMustNotInclude":["Campaign Identity"]},{"action":"create_watchable_campaign_shell_with_v1_brief","tool":"create_campaign","requiredFields":["name","campaignBrief","clientProspectId or senderLinkedinUrl","currentStep","watchNarration"],"requiredValues":{"currentStep":"create-offer","watchNarration.stage":"brief"},"capture":["campaignId","watchUrl"],"canonicalStateWrites":["campaignId","watchUrl","campaignBrief","currentStep:create-offer"]},{"action":"surface_campaign_shell_watch_link","watchUrlSource":"create_campaign.watchUrl","requiredWatchUrlShape":"exact create_campaign.watchUrl; direct /campaign-builder/{campaignId}?mode={claude|codex} URL with workspaceId and token","copyMustInclude":["We'll keep building the campaign in this chat.","You can watch it being built in real time in the app here:","I'll ask for your approval whenever I need your expertise or taste before moving forward.","Send changes here."],"immediateNextMainChatLine":"Ask for brief approval now. After approval, say: \"Brief approved. Next, we'll choose where to find buyers. I won't add anyone yet.\"","codexBrowserHandoff":{"openWhenAvailable":false,"printWatchLinkOnly":true,"tellUserCommandEnterOrClick":false,"mustNotUseBrowserAutomation":true,"fallbackMustNotClaimInspection":true},"copyMustAvoid":["bare /campaign-builder/{campaignId} URL","derived watch URL","second Watch link:","Next, we'll choose where to find buyers"],"oneTimeOnly":true}],"allowedTools":["get_subskill_prompt","fetch_company","fetch_company_posts","fetch_linkedin_profile","fetch_linkedin_posts","complete_sender_research","create_campaign","AskUserQuestion","request_user_input"],"doNotAllow":["list_senders","save_rubrics","import_leads","confirm_lead_list","queue_cells","start_campaign"],"waitFor":["campaign_shell_created","brief_ready","confirm_with_user"],"transitions":{"campaign_shell_created":"brief-review","brief_ready":"brief-review","confirm_with_user":"brief-review"}},{"id":"brief-review","label":"Brief review","onEnter":[{"action":"render_brief_inline","minimumVisibleDetail":"campaign direction, buyer, offer, proof, source plan, safety gates","copyMustInclude":["This brief is based on Sellable's research and your answers.","If your site or LinkedIn is stale","Do you agree with this researched campaign direction?"],"copyMustNotInclude":["quoted first-message copy","Message Angle as final proof","This approval covers","does not approve adding people","does not approve scraping posts","selecting a sender","attaching a sequence","Campaign Identity","what should this campaign sell first","sell first","[from you]","[from LinkedIn]","[from website]","[from case study]","[Sellable recommendation]"],"messageHintRule":"If any message-shape hint remains, keep it as non-final bullets and say real examples come after leads are found and filtered.","approvalBoundaryRule":"Keep this gate positive and short. Do not list later steps this approval does not cover; ask approve/revise and then move to choosing where to find buyers.","sectionLabelRule":"Use ## Sender and Company for the person/company context; never render ## Campaign Identity.","skipIf":"the full brief was already rendered in the current shell-creation handoff before create_campaign returned","renderOnceRule":"The brief must be visible exactly once before approval. Do not render it again immediately after shell creation; append the one watch-link handoff and ask the approval question.","copyMustAvoid":["duplicate brief render after shell creation","second Watch link:"]},{"action":"ask_brief_choice","uses":"request_user_input","choices":["Approve brief","Revise brief","Pause here"],"skipIf":"yolo_mode; auto_continue after rendering assumptions unless brief quality floor fails"}],"requiredCampaignState":["campaignId","watchUrl","campaignBrief","currentStep"],"allowedTools":["AskUserQuestion","request_user_input","update_campaign"],"doNotAllow":["create_campaign","list_senders","import_leads","confirm_lead_list","queue_cells","start_campaign"],"waitFor":["user_brief_confirmed","revise_brief","auto_continue"],"transitions":{"user_brief_confirmed":"find-leads","revise_brief":"brief-interview","auto_continue":"find-leads"}},{"id":"find-leads","label":"Find leads","sourceSelectionFunnel":{"preScoutRecommendationGate":{"required":true,"label":"Find buyers plan approval","mustHappenBefore":["get_provider_prompt","search_signals","search_sales_nav","search_prospeo","fetch_post_engagers","source-scout dispatch"],"show":["buyer groups or places we could check","best place to start","why the right buyers are likely to be there","what signs the next search will check","where to look next if the first place is too thin","what approval authorizes"],"approvalAuthorizes":"looking for the best places to find buyers; no one added yet","explanationMustInclude":["where the right buyers are already talking","why that place fits this campaign","enough likely prospects","what counts as a good sign","where to look next if thin","I won't add anyone yet","choose where to find buyers"],"yoloMode":{"autoApproveRecommendedLane":true,"mustShowAssumedChoice":true,"pauseIfConfidenceLow":true},"customerLanguage":{"readability":"grade-5","requiredHeading":"Find Buyers Plan","prefer":["place to look","best place to start","people to check","prospects","I won't add anyone yet"],"avoid":["lane","source scouting","provider","precision/scale tradeoff","evidence quality","pilot volume","ICP","workflow pain","lead-source scouting","not importing leads"],"example":"Brief approved. Next, we'll choose where to find buyers. I won't add anyone yet. I recommend starting with people already talking on LinkedIn about [plain topic]. That should help us find people who already care about [plain problem]. If this is too thin, I'll try [plain fallback] next.","termRule":{"buyers":"target market","peopleToCheck":"raw reactions/comments before fit is known","prospects":"likely usable people after fit","leads":"campaign rows"}},"questionOrder":"update_campaign to pick-provider, render ## Find Buyers Plan in normal chat without repeating the watch link, then open the structured approval question; never ask first or show the plan after approval","copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"],"approvalFreshnessRule":"Only an approval question opened after the visible Find Buyers Plan, or an explicit source choice made after that plan, can set source_lane_approved. Do not reuse brief approval, a generic approve-plan answer, or pre-plan provider state."},"defaultWhenSourceUnspecified":["signal-discovery","sales-nav-recent-active","sales-nav-general","prospeo"],"userFacingFallbackOrder":["LinkedIn posts where the right buyers are already talking","people with the right titles who recently posted on LinkedIn","people with the right titles from a broader LinkedIn search","a broader company and contact search"],"parallelAllowedOnlyWhen":["user explicitly requested source comparison","resuming already-started parallel scouts","first viable source is borderline and one cheap fallback check is needed"]},"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."},"purpose":"show the visible source-plan approval checkpoint before provider lanes"},{"tool":"get_source_scout_registry","purpose":"load canonical source scout names before optional branch launch"},{"action":"render_find_buyers_plan_inline","oneShot":true,"requiredPrecondition":"currentStep=pick-provider has been saved with update_campaign","mustHappenAfter":["update_campaign currentStep=pick-provider"],"mustHappenBefore":["ask_find_buyers_plan_choice","get_provider_prompt","search_signals","search_sales_nav","search_prospeo","fetch_post_engagers","source-scout dispatch"],"output":"normal chat text before any request_user_input approval panel","requiredHeading":"Find Buyers Plan","requiredInlineFields":["plain-language buyer groups or places for this campaign","best place to start","why the right buyers are likely to be there","what signs the next search will check","where to look next if the first place is too thin","what approval authorizes"],"copyMustInclude":["Find Buyers Plan","best place to start","I won't add anyone","choose where to find buyers","I won't add anyone yet"],"copyMustAvoid":["lane","source scouting","provider","precision/scale tradeoff","evidence quality","pilot volume","ICP","workflow pain","lead-source scouting","not importing leads","Watch link:","campaign-builder URL","repeat the watch URL"],"approvalBoundary":"Explain what approval authorizes before asking: looking for the best places to find buyers; no one is added yet.","linkPolicy":"Do not include another watch link. The first brief handoff already gave the user the live app URL."},{"action":"ask_find_buyers_plan_choice","uses":"request_user_input","oneShot":true,"requiredPrecondition":"render_find_buyers_plan_inline completed in normal chat after pick-provider state was saved; no earlier approval can satisfy this","skipIf":"source_lane_approved was set by ask_find_buyers_plan_choice after the visible Find Buyers Plan, leadSourceProvider was explicitly chosen by the user after that plan, or yolo_mode auto-selected source_lane_approved after showing the assumed choice","choices":["Start with LinkedIn posts","Start with active LinkedIn profiles","Start with company/contact search","Choose a different place","Pause here"],"approvalChoiceLabelsByProvider":{"signal-discovery":"Start with LinkedIn posts","sales-nav":"Start with active LinkedIn profiles","prospeo":"Start with company/contact search"},"approvalState":"source_lane_approved","copyMustInclude":["Approve this plan for where to find buyers?","Start with LinkedIn posts"],"mustNotHappenBefore":["render_find_buyers_plan_inline"],"approvalStateRule":"Set source_lane_approved only from this question after the visible plan, never from brief approval, generic Approve plan, or pre-plan provider state."},{"action":"persist_provider_search_step_before_search","tool":"update_campaign","requiredPrecondition":"source_lane_approved set after the visible Find Buyers Plan approval question","providerCurrentStepMap":{"signal-discovery":"signal-discovery","sales-nav":"sales-nav","prospeo":"prospeo"},"requiredValues":{"leadSourceType":"new","leadSourceProvider":"approved provider","currentStep":"provider-specific current step","watchNarration.stage":"find-leads","watchNarration.headline":"Searching the approved place","watchNarration.safety":"Looking only. No one is added yet."},"mustRunBefore":["search_signals","search_sales_nav","search_prospeo","fetch_post_engagers"]},{"action":"run_sequential_source_funnel","requiredPrecondition":"source_lane_approved set after the visible Find Buyers Plan approval question","defaultOrder":["source-scout-linkedin-engagement","source-scout-sales-nav","source-scout-prospeo-contact"],"campaignOfferIdRequired":true,"stopOnFirstViableUnlessComparisonRequested":true}],"requiredCampaignState":["campaignId","campaignBrief","currentStep"],"allowedTools":["get_source_scout_registry","get_provider_prompt","lookup_sales_nav_filter","search_sales_nav","search_prospeo","search_signals","select_promising_posts","fetch_post_engagers","fetch_company","fetch_linkedin_profile","load_csv_linkedin_leads","load_csv_domains","get_rows_minimal","update_campaign","AskUserQuestion","request_user_input","get_campaign_navigation_state"],"doNotAllow":["create_campaign","list_senders","save_rubrics","import_leads","confirm_lead_list","queue_cells","start_campaign"],"waitFor":["lead_review_ready","revise_brief","confirm_with_user"],"transitions":{"lead_review_ready":"lead-review","revise_brief":"brief-interview","confirm_with_user":"lead-review"}},{"id":"lead-review","label":"Source approval","onEnter":[{"action":"show_source_decision_card","requiredInlineFields":["primary source and exact filters/recipe","specific source 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 will happen after approval"],"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 source-action copy only. Include: \"After approval, I will build the source list, add it to the campaign, and review the first 15 leads before we scale.\" Never write \"This approval covers\" or \"It does not approve\" sentences.","linkPolicy":"Do not include another watch link in Source Recommendation; describe the source decision only."},{"action":"ask_source_review_choice","uses":"request_user_input","choices":["Approve scraping N recommended LinkedIn posts","Run the approved source import","Revise source","Pause here"],"approvalChoiceLabelsByProvider":{"signal-discovery":"Approve scraping {scrapePostCount} recommended LinkedIn posts?","sales-nav":"Import the approved Sales Nav source list","prospeo":"Import 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"},"purpose":"load tail contract before execution tools"},{"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","purpose":"poll until the source lead-list import reaches a terminal complete/failed/cancelled state before copying rows into the campaign table","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","purpose":"ask filter choice immediately; no post-lead registries, filter refs, or message prompts first","maxCustomerCopyLines":4,"copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"],"linkPolicy":"Ask add filters vs skip filters without repeating the watch link."}],"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 generation 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_post_lead_agents_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":"post-lead-workstreams","filters_skipped":"message-generation","revise_leads":"find-leads"}},{"id":"post-lead-workstreams","label":"Filter workstream","onEnter":[{"action":"persist_add_filters_approval","tool":"update_campaign","when":"filters_enabled","requiredFields":["campaignId","enableICPFilters","currentStep","watchNarration"],"requiredValues":{"enableICPFilters":true,"currentStep":"create-icp-rubric","watchNarration.stage":"fit-message","watchNarration.headline":"Create filter rules","watchNarration.visibleState":"Filters are enabled and the browser is showing Filter Rules while Codex defines the rubric.","watchNarration.nextAction":"Review saved filter rules"},"mustRunBefore":["get_post_find_leads_scout_registry","launch_lead_fit_builder_after_filter_choice","save_rubrics"]},{"tool":"get_post_find_leads_scout_registry","purpose":"load canonical post-lead worker names only after the user has chosen filters"},{"action":"launch_lead_fit_builder_after_filter_choice","mode":"parallel_when_host_supports_subagents","target":"post-find-leads-filter-scout","inputs":["campaignId","campaignBrief","selectedLeadListId","workflowTableId","reviewBatchRowIds/hash","filterChoice"],"stateSource":"live campaign/table","debugFilesOptionalOnly":true},{"action":"launch_message_draft_builder_after_filter_decision","mode":"parallel_when_host_supports_subagents","target":"post-find-leads-message-scout","when":"filters_enabled","doesNotQueueCells":true,"customerNarration":"Say message agent is drafting."},{"action":"save_filter_rubrics_to_campaign","tool":"save_rubrics","when":"filters_enabled and rubrics are production-shaped","requiredFields":["campaignOfferId","leadScoringRubrics"],"writesCampaignState":"leadScoringRubrics","requiredSideEffects":{"enableICPFilters":true,"currentStep":"create-icp-rubric","watchNarration.headline":"Filter rules saved for review"}},{"action":"ask_filter_rubric_review_choice","uses":"request_user_input","choices":["Approve filters","Revise filters","Pause"],"purpose":"let the user read saved rubrics before Filter Leads or enrichment","copyMustInclude":["These rules prevent wasted sends before I score/import the list.","Recommended because of the researched ICP, source sample, and repeated false-positive patterns.","one pass example and one block example when available","approval is for the filter rules","Approve these filter rules."],"autoSelectIf":"yolo_mode and rubrics are production-shaped","copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"]}],"requiredCampaignState":["campaignId","campaignBrief","selectedLeadListId","workflowTableId"],"allowedTools":["get_subskill_prompt","get_subskill_asset","get_post_find_leads_scout_registry","save_rubrics","get_campaign","get_rows_minimal","update_campaign","get_campaign_navigation_state","AskUserQuestion","request_user_input","Task","spawn_agent"],"doNotAllow":["create_campaign","list_senders","import_leads","confirm_lead_list","queue_cells","start_campaign","check_rubric","generate_messages"],"waitFor":["filter_rubrics_approved","revise_leads","revise_rubric","revise_messaging"],"hardRules":["after_save_rubrics_currentStep_must_stay_create-icp-rubric_until_filter_approval","filter_approval_required_before_apply-icp-rubric_or_queue_cells","do_not_move_browser_to_messages_until_filter_leads_step_is_current_or_filters_are_explicitly_skipped","no_post_lead_worker_or_deep_prompt_before_filter_choice","lead_fit_builder_starts_only_after_filters_enabled","msg_draft_after_filter_choice","msg_draft_no_cells"],"transitions":{"filter_rubrics_approved":"message-generation","revise_leads":"find-leads","revise_rubric":"filter-rubric","revise_messaging":"message-generation","confirm_with_user":"message-review"}},{"id":"filter-rubric","label":"Rubric revision","onEnter":[{"tool":"get_subskill_asset","requiredValues":{"subskillName":"create-campaign-v2","assetPath":"references/filter-leads.md"}},{"tool":"save_rubrics","requiredFields":["campaignOfferId","leadScoringRubrics"],"writesCampaignState":"leadScoringRubrics","requiredSideEffects":{"enableICPFilters":true,"currentStep":"create-icp-rubric","watchNarration.headline":"Filter rules saved for review"}},{"action":"ask_filter_rubric_review_choice","uses":"request_user_input","choices":["Approve filters","Revise filters","Pause"],"copyMustInclude":["These rules prevent wasted sends before I score/import the list.","approval object is filter rules only","approval is for the filter rules","Approve these filter rules."],"autoSelectIf":"yolo_mode and rubrics are production-shaped","copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"]}],"requiredCampaignState":["campaignId","workflowTableId"],"allowedTools":["get_subskill_asset","save_rubrics","AskUserQuestion","request_user_input"],"doNotAllow":["create_campaign","list_senders","import_leads","confirm_lead_list","update_campaign","queue_cells","start_campaign","check_rubric"],"waitFor":["filter_rubrics_approved","revise_leads","confirm_with_user","auto_continue"],"transitions":{"filter_rubrics_approved":"message-generation","revise_leads":"find-leads","confirm_with_user":"message-generation","auto_continue":"message-generation"}},{"id":"message-generation","label":"Message generation","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":["run background post-find-leads-message-scout when available","get_subskill_prompt({ subskillName: \"generate-messages\", offset, limit }) until hasMore=false"],"stateSource":"campaignBrief, source, selectedLeadListId, workflowTableId, execution-slice row ids/hash","outputState":"messageDraftRecommendation","revisionMode":{"when":"revise_messaging or latest user message requests template changes","requiredInputs":["latest user feedback","current messageDraftRecommendation","campaign/table execution-slice state"],"output":"revised messageDraftRecommendation rendered before request_user_input"}}],"requiredCampaignState":["campaignId","campaignBrief","selectedLeadListId","workflowTableId"],"allowedTools":["get_subskill_prompt","get_campaign","get_rows_minimal","update_campaign"],"toolRules":["Run post-find-leads-message-scout when available; otherwise parent fallback must load get_subskill_prompt({ subskillName: \"generate-messages\" }) from live state.","brief.md, lead-review.md, and lead-sample.json are optional debug context only.","messageDraftRecommendation returns templateRecommendation, tokenFillRules, renderedSample, concerns, status, basisToken, outputAt, outputHash, and error/retry detail.","If campaign/source/table/execution-slice basis does not match, classify the output stale or blocked."],"doNotAllow":["create_campaign","list_senders","save_rubrics","import_leads","confirm_lead_list","queue_cells","start_campaign","generate_messages","AskUserQuestion","request_user_input"],"waitFor":["message_validation_ready","revise_rubric","revise_messaging"],"transitions":{"message_validation_ready":"message-review","revise_rubric":"filter-rubric","revise_messaging":"message-generation"},"revisionLoop":{"trigger":"user gives copy feedback, asks for an update, or chooses revise-messaging","required":["apply latest user feedback to current messageDraftRecommendation and live campaign/table state","produce a new messageDraftRecommendation with revisionNotes","render revised ## Message Template, ## Rendered Example, and What changed before asking approval"],"blocked":["asking whether an unseen new version is better","reusing the old template after acknowledging feedback","update_campaign_brief before approve-message","request_user_input or AskUserQuestion during message-generation"],"outputState":"messageDraftRecommendation"},"hardRules":["message_revision_must_render_new_template_before_approval_question","message_revision_saves_only_after_approve_message","message_generation_must_not_call_request_user_input","message_generation_must_transition_to_message_review_only_after_renderable_draft_state"]},{"id":"message-review","label":"Message review","onEnter":[{"action":"render_message_review_from_state","requiredState":["campaignBrief","selectedLeadListId","workflowTableId","messageDraftRecommendation"],"requiredVisibleLabels":["## Message Template","## Rendered Example","Good token fill:","My take:","Question: approve-message or revise-messaging?","Recommendation:"],"doNotShowByDefault":["Token Notes","Good omit / fallback","Bad fill to avoid","Token Adherence Table"],"internalPersistenceOnly":["Token Fill Rules","Token Fill Examples","fallback guidance","bad-fill avoidance notes"],"copyMustInclude":["QA receipt before approval","tokens resolved","company/person match","proof claim","prospect angle","language/tone when known","obvious bad fits","Would I take this call?","weak personalization can burn the sender's reputation","full list remains paused"],"copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"],"revisionRenderRule":"After revise_messaging, show revised template, rendered example, and What changed before ask_message_review_choice; never ask if an unseen version is better."},{"action":"ask_message_review_choice","uses":"request_user_input","choices":["approve-message","revise-messaging"],"copyMustInclude":["approve this message template and continue","nothing sends from this approval"],"autoSelectIf":"yolo_mode and Recommendation is approve-message; revise autonomously when Recommendation is revise-messaging","copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"],"requiredPrecondition":"current or revised template and rendered example are visible in this turn","revisionPrecondition":"after revise_messaging, revised template is visible before this question"},{"action":"sync_approved_message_set_to_campaign_brief","tool":"update_campaign_brief","when":"after approve-message","requiredFields":["campaignId","approvedMessageTemplate","Token Fill Rules","Token Fill Examples"],"writesCampaignState":"approvedMessageTemplate","revisionApprovalRule":"If the user requested revisions, write only the latest approved revised template and token rules to campaignBrief."}],"requiredCampaignState":["campaignId","workflowTableId","messageDraftRecommendation"],"allowedTools":["AskUserQuestion","request_user_input","update_campaign_brief","update_campaign","get_rows_minimal"],"doNotAllow":["create_campaign","list_senders","save_rubrics","import_leads","confirm_lead_list","start_campaign"],"waitFor":["message_approved","revise_messaging"],"transitions":{"message_approved":"validate-sample","revise_messaging":"message-generation"}},{"id":"validate-sample","label":"Validate campaign-table execution slice","currentStepValue":"apply-icp-rubric","reference":"references/sample-validation-loop.md","visibleStepRule":"Filter Leads is reached only after saved-filter approval. On approve-message, save the template, refresh apply-icp-rubric narration, then queue bounded Enrich Prospect cells.","onEnter":[{"tool":"update_campaign","requiredValues":{"currentStep":"apply-icp-rubric","watchNarration.stage":"fit-message"},"purpose":"keep the watched UI on Filter Leads while the approved template starts the bounded cascade","watchNarrationRule":"Say the template is saved and Filter Leads is now running the bounded enrichment and scoring pass."},{"tool":"queue_cells","purpose":"queue ICP/enrichment cells for the internal campaign-table execution slice only","requiredFields":["workflowTableId","reviewBatchRowIds","cellTypes"],"targetCountSource":"reviewBatchRowIds.length (default 15)"},{"tool":"wait_for_campaign_table_ready","purpose":"wait_for_review_batch_scoring_cells"},{"tool":"get_rows_minimal","requiredValues":{"includeRows":false},"purpose":"read compact row status only"},{"tool":"wait_for_rubric_results","requiredFields":["workflowTableId","targetCount","minPassedCount"],"requiredValues":{"includeRows":false,"minPassedCount":1},"minPassedCountSource":"firstPassingRowForMessageStart","readVia":"stats_only_tool_result","customerSummaryPattern":["{checked} leads checked","{passed} passed and have draft messages","{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_cells","wait_for_campaign_table_ready","get_rows_minimal","wait_for_rubric_results","update_campaign","AskUserQuestion","request_user_input"],"doNotAllow":["import_leads","list_senders","start_campaign","enrich_with_prospeo","bulk_enrich_with_prospeo","check_rubric"],"hardRules":["wait_for_rubric_results_never_retain_rows_payload_in_tail_context","wait_for_rubric_results_targetCount_always_explicit","timeout_never_repeats_without_customer_handoff","timeout_or_underfloor_sample_never_advances_to_settings"],"waitFor":["sample_validated","sample_revision_required"],"transitions":{"sample_validated":"auto-execute-messaging","sample_revision_required":"lead-review","revise_leads":"find-leads","revise_rubric":"filter-rubric","escalation_triggered":"escalation"}},{"id":"auto-execute-messaging","label":"Generate initial campaign-row messages","currentStepValue":"auto-execute-messaging","references":["references/parallel-critique-protocol.md","references/thomas-variant-selection.md","references/thomas-revision-filters.md","references/step-15-re-cascade.md"],"onEnter":[{"tool":"queue_cells","batchSize":100,"when":"any passing row has pending or empty Generate Message cell","cellSource":"pending_generate_message_cells_for_passing_rows using approved_campaign_brief_template"},{"tool":"wait_for_rubric_results","purpose":"wait_for_first_passing_generated_message","requiredValues":{"includeRows":false,"minPassedCount":1,"minMessagesCount":1},"readVia":"stats_only_tool_result"},{"action":"observe_generate_message_results","reference":"references/step-15-re-cascade.md"},{"action":"enforce_token_contract","modeFromConfig":"messaging.tokenContract"},{"action":"optional_critique_pass","enabledFromConfig":"messaging.critique.enabled","plan":"85-03","protocol":"references/parallel-critique-protocol.md","sampleSizeFromConfig":"messaging.critique.sampleSize","budgetUsdCapFromConfig":"messaging.critique.budgetUsdCap","perCriticTimeoutFromConfig":"messaging.critique.perCriticTimeoutSeconds","totalTimeoutFromConfig":"messaging.critique.totalTimeoutSeconds","criticsFromConfig":"messaging.critique.critics","enforceFinalizerPassFromConfig":"messaging.critique.synthesis.enforceFinalizerPass","rejectOnFakeProofFromConfig":"messaging.critique.rejectOnFakeProof","rejectOnUnsupportedTokenFromConfig":"messaging.critique.rejectOnUnsupportedToken","onBudgetTrip":"halt_critique_continue_plain_tail","onPerCriticTimeout":"drop_critic_proceed_with_remaining","onTotalTimeout":"persist_plain_message_for_row","onTokenContractViolation":"persist_plain_message_for_row","onFakeProof":"persist_plain_message_for_row"},{"action":"optional_opus_subset","enabledFromConfig":"messaging.critique.opus.enabled","selection":"references/thomas-variant-selection.md","maxMessagesPerPassFromConfig":"messaging.critique.opus.maxMessagesPerPass","budgetUsdCapFromConfig":"messaging.critique.opus.budgetUsdCap","onOpusBudgetTrip":"halt_opus_continue_non_opus","onOpusTokenContractViolation":"fallback_to_non_opus_rewrite"},{"tool":"update_campaign","requiredValues":{"currentStep":"auto-execute-messaging","watchNarration.stage":"review-ready"},"watchNarrationRule":"Say the first passing generated message is ready in Messages; next is review before Settings."},{"action":"ask_generated_message_review_choice","uses":"request_user_input","choices":["Approve reviewed draft rows and continue to Settings","Revise filters","Revise message template","Pause here"],"copyMustInclude":["approve the reviewed draft rows and continue to Settings","tokens resolved","company/person match","proof claim","prospect angle","language/tone when known","obvious bad fits","Would I take this call?","weak personalization can burn the sender's reputation","full list remains paused","nothing sends from this approval"],"autoSelectIf":"yolo_mode and the first passing generated message clears the quality floor","copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"]}],"allowedTools":["get_subskill_asset","get_rows_minimal","queue_cells","wait_for_rubric_results","update_campaign","AskUserQuestion","request_user_input"],"doNotAllow":["import_leads","list_senders","start_campaign","generate_messages"],"hardRules":["critique_failure_never_escalates","critique_sample_size_bounded_by_config","first_passing_generated_message_unblocks_review","critics_fixed_at_targeting_copy_voice","synthesis_enforces_phase_84_token_contract","opus_reserved_for_highest_value_subset","proposed_token_never_persisted_in_rewrite"],"waitFor":["generated_messages_approved","sample_revision_required"],"transitions":{"generated_messages_approved":"awaiting-user-greenlight","revise_filters":"filter-rubric","revise_messaging":"message-generation","escalation_triggered":"escalation"}},{"id":"awaiting-user-greenlight","label":"Settings, sender, sequence, and greenlight","currentStepValue":"awaiting-user-greenlight","reference":"references/final-handoff-contract.md","onEnter":[{"tool":"get_campaign","purpose":"verify senderIds and sequence state before final handoff"},{"tool":"list_senders","purpose":"surface available connected senders only at Settings"},{"tool":"update_campaign","requiredValues":{"currentStep":"settings","watchNarration.stage":"review-ready"},"purpose":"park the watched UI on Settings before sender selection","watchNarrationRule":"Say message review is complete and the browser is now on Settings so the user can connect or choose a sender. Include that lead research and filtering already happened outside the user's LinkedIn account, this sender is only used for approved sending after final launch, and no launch happens from Settings."},{"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 browser is moving to Sequence review. Include that the sequence is editable before launch and still not sending until Start."},{"tool":"attach_recommended_sequence","when":"after senderIds are attached","requiredValues":{"campaignId":"{campaignId}","currentStep":"send","watchNarration.stage":"review-ready"},"watchNarrationRule":"The sequence tool owns the visible beat: say the recommended follow-up sequence is attached, the browser is now on final launch review, and the next action is final launch greenlight. Include that it is still not sending until Start. Do not follow with a separate step-only update_campaign fixup."},{"action":"ask_final_launch_greenlight","uses":"request_user_input","singleChoice":true,"choices":["Start campaign","Review campaign first","Pause here"],"copyMustInclude":["quality confidence means sample messages and prospect angle were checked","launch confidence means sender, sequence, and Start are ready","approved messages begin sending according to the sequence","replies and meetings follow connected settings","you can monitor and pause","no hidden extra approval disappears after clicking Start"],"onUserStart":"claude-greenlight","neverAutoSelectInYolo":true,"copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"]}],"allowedTools":["get_campaign","get_campaign_navigation_state","list_senders","update_campaign","attach_recommended_sequence","AskUserQuestion","request_user_input"],"doNotAllow":["start_campaign","import_leads"],"autoStart":false,"watchRequired":true,"waitFor":["sender_connection_required","sender_attached","sequence_attached","ready_to_launch","user_greenlight","ui_start_detected"],"transitions":{"sender_connection_required":"awaiting-user-greenlight","sender_attached":"awaiting-user-greenlight","sequence_attached":"awaiting-user-greenlight","ready_to_launch":"awaiting-user-greenlight","user_greenlight":"claude-greenlight","ui_start_detected":"running"}},{"id":"claude-greenlight","label":"Explicit launch","reference":"references/final-handoff-contract.md","onEnter":[{"tool":"get_campaign","purpose":"detect_already_running"},{"action":"verify_sequence_and_current_step_before_start","requiredState":["workflowTableId","senderIds","sequenceTemplate","at least one approved generated message","currentStep in awaiting-user-greenlight|claude-greenlight|send"]},{"tool":"start_campaign","requiredFields":["campaignId"],"persistsCurrentStep":"running","watchNarrationRule":"After start_campaign succeeds, the running state must say the final greenlight was accepted, the campaign is now live/running, and the user can watch progress from the campaign."}],"allowedTools":["get_campaign","attach_recommended_sequence","start_campaign","AskUserQuestion","request_user_input"],"watchRequired":true,"waitFor":"campaign_started","transitions":{"campaign_started":"running"}},{"id":"running","label":"Campaign is live","currentStepValue":"running","onEnter":[{"action":"surface_campaign_live_confirmation_without_watch_link","copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"]}],"allowedTools":["get_campaign","AskUserQuestion","request_user_input"],"doNotAllow":["start_campaign"],"terminal":true},{"id":"escalation","label":"Escalation","reference":"references/escalation-ladder.md","allowedTools":["AskUserQuestion","request_user_input"],"doNotAllow":["start_campaign","import_leads"],"transitions":{"revise_brief":"brief-interview","revise_leads":"find-leads","revise_rubric":"filter-rubric","revise_messaging":"message-generation","abort":"abort"}},{"id":"abort","label":"Abort","allowedTools":["AskUserQuestion","request_user_input"],"terminal":true}]}
@@ -135,7 +135,8 @@ The kickoff doc is the resume surface. Re-open it before repeating discovery wor
135
135
  place to start, why the right buyers are likely to be there, what signs the
136
136
  next search will check, where you'll look next if the first place is too thin,
137
137
  and that approval only lets you look for the best places to find buyers. No
138
- one is added to the campaign yet.
138
+ one is added to the campaign yet. The approval must come after the visible
139
+ plan; do not reuse a prior brief approval or generic approval panel.
139
140
  - When enough context exists, try 1-2 alternate hypotheses if the first lane is too weak or noisy.
140
141
  - Directional preview does not require a sender, campaign, or selected lead list. Start with count/sample exploration first; only attach searches to a campaign when the user is ready to import.
141
142
  - If the user already has a LinkedIn-profile CSV, treat that as a direct lead-list path and skip discovery.
@@ -1112,6 +1112,17 @@ unit, either compress it into that first send unit or omit it. Do not move it to
1112
1112
  a later message. Preserve the idea as an internal `Findings` note only if it is
1113
1113
  useful after campaign mint.
1114
1114
 
1115
+ ### Message Review Revision Loop
1116
+
1117
+ When the user gives edits to an already rendered template, treat that feedback
1118
+ as the next drafting input. Produce a revised selected template, rendered
1119
+ example, and short change summary before asking for approval again. Do not only
1120
+ acknowledge the feedback or ask whether a new version is better without showing
1121
+ the new version. In live campaign mode, save with `update_campaign_brief` only
1122
+ after the user approves the revised template.
1123
+ Do not call `request_user_input` or `AskUserQuestion` from the drafting or
1124
+ revision step; approval questions belong after the revised copy is visible.
1125
+
1115
1126
  Before returning `Status: confirmed`, run this explicit check:
1116
1127
 
1117
1128
  `Single send unit: PASS | BLOCKED — reason`
@@ -40,8 +40,14 @@ When the user asks to find posts or start searching, **IMMEDIATELY begin Round 1
40
40
 
41
41
  Production Signal Discovery uses Harvest for post comments and reactions.
42
42
  Harvest returns about 100 comments or reactions per page. The source scrape
43
- pages through Harvest up to the configured per-post caps, currently 1,500
44
- reactions and 300 comments per selected post.
43
+ pages through Harvest up to the configured per-post caps, currently 1,000
44
+ reactions and 1,000 top-level comments per selected post.
45
+
46
+ LinkedIn search/post-detail counts are visible public counts. LinkedIn can
47
+ include replies in the visible comment number, while Signal Discovery imports
48
+ top-level commenters only. Treat pre-import people-to-check math as an upper
49
+ bound. The exact unique people count is known only after the comments/reactions
50
+ scrape completes and duplicate people are merged.
45
51
 
46
52
  Important interpretation rule: a source list row count is the number of
47
53
  headline-passing candidates inserted after scraping and filtering, not the raw
@@ -243,7 +249,7 @@ Examples of BAD keywords:
243
249
 
244
250
  Look for posts that:
245
251
 
246
- - Have meaningful engagement (minimum 30 likes OR 15 comments)
252
+ - Have meaningful visible engagement (minimum 30 likes OR 15 visible comments)
247
253
  - Are from authors in your ICP's community
248
254
  - Discuss specific, tactical topics (not generic thought leadership)
249
255
  - Generate discussion, not just likes