@sellable/mcp 0.1.140 → 0.1.142

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.
@@ -0,0 +1,59 @@
1
+ import { authToolDefinitions } from "./auth.js";
2
+ import { blueprintCommitToolDefinitions } from "./blueprint-commit.js";
3
+ import { bootstrapToolDefinitions } from "./bootstrap.js";
4
+ import { campaignToolDefinitions } from "./campaigns.js";
5
+ import { cellToolDefinitions } from "./cells.js";
6
+ import { startCliLoginToolDef, waitForCliLoginToolDef } from "./cli-login.js";
7
+ import { contextToolDefinitions } from "./context.js";
8
+ import { directCampaignToolDefinitions } from "./direct-campaigns.js";
9
+ import { engageBootstrapToolDefinitions } from "./engage-bootstrap.js";
10
+ import { engageDiscoveryToolDefinitions } from "./engage-discovery.js";
11
+ import { engageMemoryToolDefinitions } from "./engage-memory.js";
12
+ import { engageStateToolDefinitions } from "./engage-state.js";
13
+ import { enrichmentToolDefinitions } from "./enrichment.js";
14
+ import { frameworkToolDefinitions } from "./framework.js";
15
+ import { leadToolDefinitions } from "./leads.js";
16
+ import { linkedinToolDefinitions } from "./linkedin.js";
17
+ import { navigationToolDefinitions } from "./navigation.js";
18
+ import { onDemandToolDefinitions } from "./one-off.js";
19
+ import { processingToolDefinitions } from "./processing.js";
20
+ import { promptToolDefinitions } from "./prompts.js";
21
+ import { readinessToolDefinitions } from "./readiness.js";
22
+ import { rowToolDefinitions } from "./rows.js";
23
+ import { rubricToolDefinitions } from "./rubrics.js";
24
+ import { senderToolDefinitions } from "./senders.js";
25
+ import { sequencerToolDefinitions } from "./sequencer.js";
26
+ import { tableToolDefinitions } from "./tables.js";
27
+ import { verifyRowToolDefinitions } from "./verify-row.js";
28
+ import { workspaceToolDefinitions } from "./workspaces.js";
29
+ export const allTools = [
30
+ ...campaignToolDefinitions,
31
+ ...authToolDefinitions,
32
+ startCliLoginToolDef,
33
+ waitForCliLoginToolDef,
34
+ ...bootstrapToolDefinitions,
35
+ ...engageBootstrapToolDefinitions,
36
+ ...engageDiscoveryToolDefinitions,
37
+ ...workspaceToolDefinitions,
38
+ ...frameworkToolDefinitions,
39
+ ...contextToolDefinitions,
40
+ ...navigationToolDefinitions,
41
+ ...leadToolDefinitions,
42
+ ...enrichmentToolDefinitions,
43
+ ...processingToolDefinitions,
44
+ ...rubricToolDefinitions,
45
+ ...readinessToolDefinitions,
46
+ ...rowToolDefinitions,
47
+ ...cellToolDefinitions,
48
+ ...promptToolDefinitions,
49
+ ...linkedinToolDefinitions,
50
+ ...onDemandToolDefinitions,
51
+ ...directCampaignToolDefinitions,
52
+ ...senderToolDefinitions,
53
+ ...engageStateToolDefinitions,
54
+ ...engageMemoryToolDefinitions,
55
+ ...sequencerToolDefinitions,
56
+ ...tableToolDefinitions,
57
+ ...blueprintCommitToolDefinitions,
58
+ ...verifyRowToolDefinitions,
59
+ ];
@@ -89,16 +89,16 @@ async function fetchCampaignOffer(campaignOfferId) {
89
89
  leadScoringRubrics: v3.rubrics,
90
90
  };
91
91
  }
92
- const filterRulesEntryWatchNarration = {
92
+ const filterLeadsReadyWatchNarration = {
93
93
  stage: "fit-message",
94
- headline: "Create filter rules",
95
- visibleState: "Filters are enabled and the browser is showing Filter Rules while the rubric is saved.",
96
- agentIntent: "Codex is defining fit rules before any enrichment, filtering, or message cells run.",
97
- nextAction: "Review saved filter rules",
94
+ headline: "Filter Leads is ready",
95
+ visibleState: "Filters are enabled, rubrics are saved, and the browser is showing Filter Leads.",
96
+ agentIntent: "Codex is waiting for the approved message template before running enrichment, filtering, or message cells.",
97
+ nextAction: "Review or approve the message template before filtering",
98
98
  progressLabel: "Fit + message",
99
- safety: "No enrichment, filtering, Generate Message cells, sequence setup, or sending starts from this step.",
99
+ safety: "Saved rules do not start enrichment, filtering, Generate Message cells, sequence setup, or sending.",
100
100
  };
101
- function shouldMoveToFilterRules(currentStep) {
101
+ function shouldMoveToFilterLeads(currentStep) {
102
102
  if (!currentStep)
103
103
  return true;
104
104
  return [
@@ -106,15 +106,16 @@ function shouldMoveToFilterRules(currentStep) {
106
106
  "filter-choice",
107
107
  "create-icp-rubric",
108
108
  "filter-rules",
109
+ "apply-icp-rubric",
109
110
  ].includes(currentStep);
110
111
  }
111
112
  function buildEnableIcpFiltersPayload(currentStep) {
112
113
  const payload = {
113
114
  enableICPFilters: true,
114
115
  };
115
- if (shouldMoveToFilterRules(currentStep)) {
116
- payload.currentStep = "create-icp-rubric";
117
- payload.watchNarration = filterRulesEntryWatchNarration;
116
+ if (shouldMoveToFilterLeads(currentStep)) {
117
+ payload.currentStep = "apply-icp-rubric";
118
+ payload.watchNarration = filterLeadsReadyWatchNarration;
118
119
  }
119
120
  return payload;
120
121
  }
@@ -234,7 +235,7 @@ export const rubricToolDefinitions = [
234
235
  },
235
236
  {
236
237
  name: "save_rubrics",
237
- description: "Persist rubric criteria to the campaign. Pass leadScoringRubrics directly to save without drafting. Saving active rubrics enables ICP filtering and keeps/moves the watched client to Filter Rules; it does not apply Filter Leads by itself.",
238
+ description: "Persist rubric criteria to the campaign. Pass leadScoringRubrics directly to save without drafting. Saving active rubrics enables ICP filtering and moves the watched client to Filter Leads; it does not run filtering by itself.",
238
239
  inputSchema: {
239
240
  type: "object",
240
241
  properties: {
@@ -570,7 +571,7 @@ export async function saveRubrics(input) {
570
571
  const payload = buildEnableIcpFiltersPayload(campaign.currentStep);
571
572
  await api.put(`/api/v2/campaign-offers/${input.campaignOfferId}`, payload);
572
573
  enableICPFiltersSet = true;
573
- currentStepSet = payload.currentStep === "create-icp-rubric";
574
+ currentStepSet = payload.currentStep === "apply-icp-rubric";
574
575
  }
575
576
  catch (error) {
576
577
  // Non-fatal: the rubric save already succeeded. Retry the minimum
@@ -590,7 +591,7 @@ export async function saveRubrics(input) {
590
591
  return {
591
592
  success: true,
592
593
  message: currentStepSet
593
- ? `Saved ${normalizedDraft.length} rubric criteria, ICP filtering is ON, and the campaign is on Filter Rules.`
594
+ ? `Saved ${normalizedDraft.length} rubric criteria, ICP filtering is ON, and the campaign is on Filter Leads.`
594
595
  : enableICPFiltersSet || campaign.enableICPFilters === true
595
596
  ? `Saved ${normalizedDraft.length} rubric criteria and ICP filtering is ON.`
596
597
  : `Saved ${normalizedDraft.length} rubric criteria to the campaign. WARNING: could not auto-enable ICP filtering — call update_campaign({ campaignId, enableICPFilters: true }) to activate rubric-based filtering.`,
@@ -598,7 +599,7 @@ export async function saveRubrics(input) {
598
599
  deletedCount: deletedRubricIds.length,
599
600
  enableICPFiltersSet,
600
601
  currentStep: currentStepSet
601
- ? "create-icp-rubric"
602
+ ? "apply-icp-rubric"
602
603
  : (campaign.currentStep ?? null),
603
604
  currentStepSet,
604
605
  rubrics: result?.rubrics ?? normalizedDraft,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sellable/mcp",
3
- "version": "0.1.140",
3
+ "version": "0.1.142",
4
4
  "type": "module",
5
5
  "description": "Sellable MCP server for Claude Code and Codex campaign workflows",
6
6
  "main": "dist/index.js",
@@ -16,7 +16,6 @@ allowed-tools:
16
16
  - mcp__sellable__get_active_workspace
17
17
  - mcp__sellable__list_senders
18
18
  - mcp__sellable__get_sender
19
- - mcp__sellable__enrich_sender
20
19
  - mcp__sellable__complete_sender_research
21
20
  - mcp__sellable__fetch_linkedin_profile
22
21
  - mcp__sellable__fetch_linkedin_posts
@@ -54,7 +53,6 @@ allowed-tools:
54
53
  - mcp__sellable__load_csv_linkedin_leads
55
54
  - mcp__sellable__load_csv_domains
56
55
  - mcp__sellable__queue_cells
57
- - mcp__sellable__generate_messages
58
56
  - mcp__sellable__get_campaign_messages_preview
59
57
  - mcp__sellable__attach_sequence
60
58
  - mcp__sellable__attach_recommended_sequence
@@ -63,7 +61,10 @@ allowed-tools:
63
61
 
64
62
  # Sellable Create Campaign
65
63
 
66
- Use this as the customer-facing entrypoint for Sellable campaign creation.
64
+ Use this as the customer-facing public wrapper for Sellable campaign creation.
65
+ It bootstraps auth and host capabilities, then loads the internal
66
+ `create-campaign-v2` workflow prompt and `core/flow.v2.json` through MCP. Keep
67
+ this wrapper thin; v2 is the operational source of truth.
67
68
 
68
69
  CampaignOffer state and the watch link are the customer-facing source of truth.
69
70
  Disk artifacts are optional debug/UAT diagnostics; normal customer runs should
@@ -118,7 +119,8 @@ signals, cleanup risk, and confidence basis. If a user asks for a forecast,
118
119
  label it explicitly as not estimated from this run.
119
120
 
120
121
  Before any provider prompt, search, source scout, or signal-discovery call,
121
- show a short source-plan gate and ask for approval. The gate should say:
122
+ show a short source-plan gate and ask for approval. This first approval
123
+ authorizes scouting/search only. The gate should say:
122
124
 
123
125
  - given this campaign, the viable source options
124
126
  - the recommended first lane
@@ -127,14 +129,19 @@ show a short source-plan gate and ask for approval. The gate should say:
127
129
  - the fallback lane if relevant posts or ICP engagement look thin
128
130
  - that approval authorizes scouting/search only, not lead import or sending
129
131
 
130
- If active prospects likely engage with relevant LinkedIn content, recommend
131
- LinkedIn post engagement / Signal Discovery and name the post themes you will
132
- look for. If the niche is too private, low-volume, or unlikely to have relevant
133
- public posts, recommend Sales Nav recent activity, broader Sales Nav role/title
134
- filters, or Prospeo, and explain the tradeoff. Do not call `search_signals`,
135
- `search_sales_nav`, `search_prospeo`, `fetch_post_engagers`, or provider-scoped
136
- subagents until the user approves this source plan or explicitly chooses a
137
- different source.
132
+ Do not surface blanket source heuristics as product copy. Make the
133
+ recommendation specific to the campaign. If Signal Discovery is recommended,
134
+ name the exact post themes you will search. If relevant public conversations
135
+ look unlikely, recommend the specific Sales Nav or Prospeo lane instead and say
136
+ why. Do not call `search_signals`, `search_sales_nav`, `search_prospeo`,
137
+ `fetch_post_engagers`, or provider-scoped subagents until the user approves this
138
+ source plan or explicitly chooses a different source.
139
+
140
+ After scouting, ask for a second approval on the concrete source action. For
141
+ Signal Discovery, name how many selected posts will be scraped, the target
142
+ engager/source-candidate volume, and the bounded review-batch size. For Sales
143
+ Nav or Prospeo, name the specific approved import lane. Do not call
144
+ `import_leads` or `confirm_lead_list` until this second approval is granted.
138
145
 
139
146
  When the user has not supplied a source and multiple source angles are viable,
140
147
  scout those angles as independent branches when the host can actually do it:
@@ -331,7 +338,7 @@ the brief. This is only the client-prospect/bootstrap identity for
331
338
  Do not call `mcp__sellable__list_senders`, `mcp__sellable__get_sender`, or
332
339
  surface connected/missing sender state during setup, brief, source, filter, or
333
340
  message review. Sender availability belongs only to the Settings/final launch
334
- handoff after message approval and the 10-lead validation sample.
341
+ handoff after message approval and the review-batch validation sample.
335
342
 
336
343
  If the invocation or user answer includes an existing `clientProspectId`, keep
337
344
  it as the preferred `create_campaign` identity input. If it includes a LinkedIn
@@ -537,9 +544,9 @@ updates.
537
544
  ```text
538
545
  You're in — {activeWorkspaceName} workspace, ready to roll.
539
546
 
540
- Now — paste the LinkedIn profile URL of the person you'll be sending campaigns from. Usually that's you (the founder), or whoever's voice the messages should sound like.
547
+ Now — paste the LinkedIn profile or company website for the client/company this campaign is for. I’ll use that to resolve the campaign identity before we pick the target, offer, proof, and lead source.
541
548
 
542
- e.g. https://www.linkedin.com/in/your-handle
549
+ e.g. https://example.com or https://www.linkedin.com/in/client-handle
543
550
  ```
544
551
 
545
552
  - If `isReturningUser === false`, prepend ONE line confirming the new
@@ -548,16 +555,18 @@ updates.
548
555
  ```text
549
556
  You're set up — your {activeWorkspaceName} workspace is ready.
550
557
 
551
- Now — paste the LinkedIn profile URL of the person you'll be sending campaigns from. Usually that's you (the founder), or whoever's voice the messages should sound like.
558
+ Now — paste the LinkedIn profile or company website for the client/company this campaign is for. I’ll use that to resolve the campaign identity before we pick the target, offer, proof, and lead source.
552
559
 
553
- e.g. https://www.linkedin.com/in/your-handle
560
+ e.g. https://example.com or https://www.linkedin.com/in/client-handle
554
561
  ```
555
562
 
556
563
  No other lines. No "all set", no "signed in", no other acknowledgement.
557
564
 
558
- After the user pastes the URL, proceed with the existing identity-first
559
- sender flow (Step 3 onwards in the v2 subskill prompt sender
560
- enrichment via `fetch_linkedin_profile` / `enrich_sender`).
565
+ After the user pastes the URL, proceed with the identity-first campaign
566
+ setup in the v2 subskill prompt. Resolve a LinkedIn profile with
567
+ `fetch_linkedin_profile`, a company website/domain with `fetch_company`
568
+ when possible, and mark the client/company research gate with
569
+ `complete_sender_research` when that protocol is required.
561
570
 
562
571
  3. If auth is not OK with `error.type === "workspace"` (token valid, no active
563
572
  workspace), stop and show the returned guidance — that's not a fresh-user
@@ -599,8 +608,9 @@ updates.
599
608
  is synced into the campaign brief. When filters are approved, immediately
600
609
  call `mcp__sellable__update_campaign({ campaignId, enableICPFilters: true, currentStep: "create-icp-rubric", watchNarration })`
601
610
  so the watched app moves to Filter Rules while rubrics are drafted/saved.
602
- After rubrics save, pause and say the fit rules are saved; approve the
603
- message template next; after approval, queue the bounded review-batch
611
+ After rubrics save, move the watched app to `apply-icp-rubric` / Filter
612
+ Leads and say the fit rules are saved; approve the message template next;
613
+ after approval, queue the bounded review-batch
604
614
  `enrichCellId` cells to kick off enrichment/filtering.
605
615
  Product Generate Message cells must not run from the background template
606
616
  path before that template/token approval.
@@ -611,7 +621,8 @@ updates.
611
621
  visible work so the user can watch progress in the app: `create-offer` for
612
622
  the brief, `pick-provider` or the selected provider step while sourcing,
613
623
  `filter-choice` after the 15-row review batch, `create-icp-rubric` as soon
614
- as filters are approved, `messages` for the Use Template / AI Generated mode
624
+ as filters are approved, `apply-icp-rubric` after rubrics save, `messages`
625
+ for the Use Template / AI Generated mode
615
626
  choice, `auto-execute-messaging` for approved
616
627
  message work or the product's AI-generated path, `awaiting-user-greenlight`
617
628
  for the final handoff, `settings` for sender selection, `sequence` after
@@ -123,8 +123,8 @@ Default source order when the user has not supplied a source:
123
123
  4. Prospeo account/contact expansion
124
124
 
125
125
  Before any provider prompt, search, source scout, or signal-discovery call,
126
- show a short source-plan gate and ask for approval. The gate must say, in plain
127
- language:
126
+ show a short source-plan gate and ask for approval. This first approval
127
+ authorizes source scouting/search only. The gate must say, in plain language:
128
128
 
129
129
  - given this campaign, the viable source options
130
130
  - the recommended first lane
@@ -133,14 +133,13 @@ language:
133
133
  - the fallback lane if relevant posts or ICP engagement look thin
134
134
  - that approval authorizes scouting/search only, not lead import or sending
135
135
 
136
- If active prospects likely engage with relevant LinkedIn content, recommend
137
- LinkedIn post engagement / Signal Discovery and name the post themes you will
138
- look for. If the niche is too private, low-volume, or unlikely to have relevant
139
- public posts, recommend Sales Nav recent activity, broader Sales Nav role/title
140
- filters, or Prospeo, and explain the tradeoff. Do not call `search_signals`,
141
- `search_sales_nav`, `search_prospeo`, `fetch_post_engagers`, or provider-scoped
142
- subagents until the user approves this source plan or explicitly chooses a
143
- different source.
136
+ Do not surface blanket source heuristics as product copy. Make the recommendation
137
+ specific to the campaign. If Signal Discovery is recommended, name the exact
138
+ post themes you will search. If the campaign looks unlikely to have relevant
139
+ public conversations, recommend the specific Sales Nav or Prospeo lane instead
140
+ and say why. Do not call `search_signals`, `search_sales_nav`, `search_prospeo`,
141
+ `fetch_post_engagers`, or provider-scoped subagents until the user approves this
142
+ source plan or explicitly chooses a different source.
144
143
 
145
144
  Call `get_source_scout_registry` before source scouting. Source scouting is
146
145
  sequential by default. Run `source-scout-linkedin-engagement`,
@@ -148,10 +147,19 @@ sequential by default. Run `source-scout-linkedin-engagement`,
148
147
  when the user explicitly requested source comparison, a prior lane failed, or
149
148
  the first viable lane is borderline and a cheap fallback check is needed.
150
149
 
151
- No leads import until the user approves the source. A source recommendation
152
- must show concrete evidence: source lane, filters or recipe, raw volume, sample
153
- size, sampled fits as n/N plus percentage/range, estimated usable prospects,
154
- cleanup risk, runner-up, and what approval authorizes.
150
+ After scouting, show a second approval gate for the concrete source action. For
151
+ Signal Discovery, this gate must say how many selected posts will be scraped,
152
+ the target engager/source-candidate volume, the bounded campaign review-batch
153
+ size, the cleanup risk, and the fallback if the selected posts look wrong. The
154
+ approval label should describe the action, such as "Scrape 2 selected posts for
155
+ the bounded review batch." For Sales Nav or Prospeo, the label should name the
156
+ specific search/import lane. Do not call `import_leads` or `confirm_lead_list`
157
+ until this second source-action approval is granted.
158
+
159
+ A source recommendation must show concrete evidence: source lane, filters or
160
+ recipe, raw volume, sample size, sampled fits as n/N plus percentage/range,
161
+ estimated usable prospects, cleanup risk, runner-up, and what approval
162
+ authorizes.
155
163
 
156
164
  Supplied profile CSVs, company/domain CSVs, pasted domains, and existing
157
165
  Sellable lead lists are supported, but keep provider mechanics out of the first
@@ -168,7 +176,9 @@ artifacts are optional only.
168
176
  When the user chooses filters, immediately call
169
177
  `update_campaign({ campaignId, enableICPFilters: true, currentStep: "create-icp-rubric", watchNarration })`
170
178
  before rubric thinking or branch work. The watched app should move to Filter
171
- Rules quickly, then show the saved rubric there after `save_rubrics`.
179
+ Rules quickly. After `save_rubrics`, the watched app should move to
180
+ `apply-icp-rubric` / Filter Leads so the user can see the saved filters are
181
+ ready before message approval unlocks any enrichment or filtering.
172
182
 
173
183
  Lead Fit Builder persists production rubrics with `save_rubrics` when filters
174
184
  are enabled. It must not require `brief.md`, `lead-review.md`, or
@@ -47,14 +47,14 @@ The customer believes Sellable is helping them launch a campaign. Keep every
47
47
  turn anchored to that:
48
48
 
49
49
  1. Confirm who/what company the campaign is for.
50
- 2. Confirm who the LinkedIn messages send from.
51
- 3. Understand the company and offer.
52
- 4. Turn that into a campaign brief.
53
- 5. Find likely responders.
54
- 6. Filter for fit.
55
- 7. Draft messages.
56
- 8. Ask for approval.
57
- 9. Create the campaign.
50
+ 2. Understand the company, offer, target, proof, and lead-source direction.
51
+ 3. Turn that into a campaign brief and create the watchable campaign shell.
52
+ 4. Ask for brief approval before any lead import or sending-adjacent work.
53
+ 5. Find likely responders and approve the source.
54
+ 6. Import only the bounded review batch.
55
+ 7. Filter for fit and draft the reusable message template from the same sample.
56
+ 8. Ask for message approval before queueing the review-batch cascade.
57
+ 9. Use Settings for sender selection, sequence attach, and final launch handoff.
58
58
 
59
59
  Approvals only feel safe when the user can see what they are approving. Before
60
60
  any approve/revise question, show the relevant decision in plain language. For a
@@ -80,12 +80,13 @@ the sentence, or revise the message. It should include the exact fallback
80
80
  example, like `omit the noticed... line` or `use "operators"`. Missing data
81
81
  should never produce robotic or creepy copy.
82
82
 
83
- Approved token guidance is part of the campaign, not just the review. Before
84
- the 15-lead test batch imports, the campaign brief should carry forward the
85
- token fill rules and examples: good fills, good omits, bad fills, fallback
83
+ Approved token guidance is part of the campaign, not just the review. After the
84
+ bounded review batch exists and message generation has a selected template, the
85
+ campaign brief should carry forward the token fill rules and examples before any
86
+ workflow-table cascade is queued: good fills, good omits, bad fills, fallback
86
87
  rules, and why the bad fills are blocked.
87
88
  The campaign brief must carry forward the token fill rules from review into the
88
- live campaign state before import.
89
+ live campaign state before enrichment, filtering, or Generate Message cells run.
89
90
 
90
91
  ## Progress Updates
91
92
 
@@ -147,18 +148,17 @@ unless the user asks for them.
147
148
  Good opening:
148
149
 
149
150
  ```text
150
- I’ll help you launch this as a Sellable campaign. First I’ll confirm who we’re
151
- sending from and which company this is for, then I’ll turn that into a campaign
152
- brief before any leads are imported or anything can send.
151
+ I’ll help you launch this as a Sellable campaign. First I’ll resolve the
152
+ client/company this campaign is for, then I’ll turn that into a campaign brief
153
+ before any leads are imported or anything can send.
153
154
  ```
154
155
 
155
156
  Good identity setup:
156
157
 
157
158
  ```text
158
- I’ll check whether you already have a connected sender here. If I find one, I’ll
159
- ask whether that’s you and whether this campaign is for that company. If not,
160
- just paste your LinkedIn URL or company domain and I’ll look it up before we
161
- keep going.
159
+ I’ll confirm the client/company this campaign is for before we pick the target,
160
+ offer, proof, and lead source. Paste a LinkedIn profile or company website and
161
+ I’ll resolve the campaign identity before the setup choices.
162
162
  ```
163
163
 
164
164
  Bad:
@@ -328,11 +328,11 @@
328
328
  "requiredValues": {
329
329
  "currentStep": "pick-provider",
330
330
  "watchNarration.stage": "find-leads",
331
- "watchNarration.headline": "Choose the first source",
332
- "watchNarration.visibleState": "The browser is showing source selection.",
333
- "watchNarration.agentIntent": "Codex is explaining the source lane before sampling leads.",
334
- "watchNarration.nextAction": "Approve a source lane",
335
- "watchNarration.safety": "No leads import until a source is approved."
331
+ "watchNarration.headline": "Review the source plan",
332
+ "watchNarration.visibleState": "The browser is showing source selection before scouting starts.",
333
+ "watchNarration.agentIntent": "Codex is explaining the recommended campaign-specific source lane and fallback before any search runs.",
334
+ "watchNarration.nextAction": "Approve a scouting plan or choose another source",
335
+ "watchNarration.safety": "Approval here authorizes source scouting/search only; no selected-post scrape, lead import, filtering, messaging, sequence, or sending starts."
336
336
  },
337
337
  "purpose": "show the visible source-plan approval checkpoint before provider lanes"
338
338
  },
@@ -354,17 +354,48 @@
354
354
  "plain-language source options for this campaign",
355
355
  "recommended first source lane",
356
356
  "why the lane is likely to have relevant active prospects",
357
- "why a database/recent-activity fallback may be better if niche conversations are thin",
357
+ "fallback lane if the recommended lane looks weak",
358
358
  "what scouting will check next",
359
359
  "what approval authorizes"
360
360
  ],
361
361
  "choices": [
362
- "Approve recommended source",
362
+ "Approve Signal Discovery scouting plan",
363
+ "Approve Sales Nav scouting plan",
364
+ "Approve Prospeo scouting plan",
363
365
  "Choose different source",
364
366
  "Pause here"
365
367
  ],
368
+ "approvalChoiceLabelsByProvider": {
369
+ "signal-discovery": "Approve Signal Discovery scouting plan",
370
+ "sales-nav": "Approve Sales Nav scouting plan",
371
+ "prospeo": "Approve Prospeo scouting plan"
372
+ },
366
373
  "approvalState": "source_lane_approved"
367
374
  },
375
+ {
376
+ "action": "persist_provider_search_step_before_search",
377
+ "tool": "update_campaign",
378
+ "requiredPrecondition": "source_lane_approved",
379
+ "providerCurrentStepMap": {
380
+ "signal-discovery": "signal-discovery",
381
+ "sales-nav": "sales-nav",
382
+ "prospeo": "prospeo"
383
+ },
384
+ "requiredValues": {
385
+ "leadSourceType": "new",
386
+ "leadSourceProvider": "approved provider",
387
+ "currentStep": "provider-specific current step",
388
+ "watchNarration.stage": "find-leads",
389
+ "watchNarration.headline": "Searching the approved source lane",
390
+ "watchNarration.safety": "Search/scouting only; no lead import or selected-post scrape starts from this update."
391
+ },
392
+ "mustRunBefore": [
393
+ "search_signals",
394
+ "search_sales_nav",
395
+ "search_prospeo",
396
+ "fetch_post_engagers"
397
+ ]
398
+ },
368
399
  {
369
400
  "action": "run_sequential_source_funnel",
370
401
  "requiredPrecondition": "source_lane_approved",
@@ -428,6 +459,9 @@
428
459
  "action": "show_source_decision_card",
429
460
  "requiredInlineFields": [
430
461
  "primary source and exact filters/recipe",
462
+ "specific source action awaiting approval",
463
+ "for Signal Discovery: selected post count and target engager/source-candidate volume",
464
+ "bounded review batch size",
431
465
  "runner-up and why it lost",
432
466
  "raw volume",
433
467
  "sampled people",
@@ -441,10 +475,16 @@
441
475
  "action": "ask_source_review_choice",
442
476
  "uses": "request_user_input",
443
477
  "choices": [
444
- "Approve source",
478
+ "Scrape selected posts for the bounded review batch",
479
+ "Run the approved source import for the bounded review batch",
445
480
  "Revise source",
446
481
  "Pause here"
447
- ]
482
+ ],
483
+ "approvalChoiceLabelsByProvider": {
484
+ "signal-discovery": "Scrape selected posts for the bounded review batch",
485
+ "sales-nav": "Import the approved Sales Nav review batch",
486
+ "prospeo": "Import the approved Prospeo review batch"
487
+ }
448
488
  }
449
489
  ],
450
490
  "requiredCampaignState": [
@@ -683,7 +723,12 @@
683
723
  "campaignOfferId",
684
724
  "leadScoringRubrics"
685
725
  ],
686
- "writesCampaignState": "leadScoringRubrics"
726
+ "writesCampaignState": "leadScoringRubrics",
727
+ "requiredSideEffects": {
728
+ "enableICPFilters": true,
729
+ "currentStep": "apply-icp-rubric",
730
+ "watchNarration.headline": "Filter Leads is ready"
731
+ }
687
732
  }
688
733
  ],
689
734
  "requiredCampaignState": [
@@ -722,6 +767,10 @@
722
767
  "revise_rubric",
723
768
  "revise_messaging"
724
769
  ],
770
+ "hardRules": [
771
+ "after_save_rubrics_currentStep_must_be_apply-icp-rubric",
772
+ "do_not_move_browser_to_messages_until_filter_leads_step_is_current_or_filters_are_explicitly_skipped"
773
+ ],
725
774
  "transitions": {
726
775
  "post_lead_workstreams_ready": "message-review",
727
776
  "revise_leads": "find-leads",
@@ -747,7 +796,12 @@
747
796
  "campaignOfferId",
748
797
  "leadScoringRubrics"
749
798
  ],
750
- "writesCampaignState": "leadScoringRubrics"
799
+ "writesCampaignState": "leadScoringRubrics",
800
+ "requiredSideEffects": {
801
+ "enableICPFilters": true,
802
+ "currentStep": "apply-icp-rubric",
803
+ "watchNarration.headline": "Filter Leads is ready"
804
+ }
751
805
  }
752
806
  ],
753
807
  "requiredCampaignState": [
@@ -214,7 +214,9 @@ and do not say filtering the batch before rubrics and message approval are
214
214
  saved. Only show worker statuses as running if those branches actually started.
215
215
  When the user chooses filters, immediately persist `enableICPFilters: true` and
216
216
  move to `create-icp-rubric` so the watched app shows Filter Rules while Codex
217
- defines the rules in chat.
217
+ defines the rules in chat. After `save_rubrics`, move to `apply-icp-rubric`
218
+ so the watched app shows Filter Leads with saved rules before message approval
219
+ unlocks any enrichment or filtering.
218
220
 
219
221
  Fit + message:
220
222
 
@@ -132,9 +132,9 @@ The kickoff doc is the resume surface. Re-open it before repeating discovery wor
132
132
  - Before the first provider prompt, search, source scout, or signal-discovery
133
133
  call, show the user a compact source plan and get approval. The plan must say
134
134
  which campaign-specific options are plausible, which lane you recommend
135
- first, why active LinkedIn engagers are or are not likely to exist for this
136
- ICP/offer, what fallback lane you will use if the evidence is thin, and that
137
- approval authorizes scouting/search only.
135
+ first, the exact evidence you expect to test for this ICP/offer, what
136
+ fallback lane you will use if the evidence is thin, and that approval
137
+ authorizes scouting/search only.
138
138
  - When enough context exists, try 1-2 alternate hypotheses if the first lane is too weak or noisy.
139
139
  - 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.
140
140
  - If the user already has a LinkedIn-profile CSV, treat that as a direct lead-list path and skip discovery.