@sellable/mcp 0.1.149 → 0.1.150

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.
@@ -53,7 +53,29 @@ Activity Filters (use one or both):
53
53
 
54
54
  Sales Nav works without these filters too -- just expect lower reply rates.
55
55
 
56
- ### 3. Apollo (LAST RESORT FOR VOLUME)
56
+ ### 3. Prospeo (HIRING / ACCOUNT / VERIFIED-CONTACT EXPANSION)
57
+
58
+ Prospeo is the main lane when the user's direction points to database filters
59
+ that LinkedIn sources do not expose.
60
+
61
+ USE WHEN:
62
+
63
+ - Hiring-led targeting: companies actively hiring specific roles
64
+ - Named-account or domain-list targeting
65
+ - Broad persona expansion where verified contact coverage matters
66
+ - You need `company_job_posting_hiring_for`,
67
+ `company_job_posting_quantity`, funding, headcount, or domain filters
68
+
69
+ SKIP WHEN:
70
+
71
+ - The campaign is mainly about fresh LinkedIn activity or engagement warmth
72
+ - Signal Discovery can clearly produce enough ICP-fit engagers
73
+
74
+ WHY: Prospeo is the only current provider with company job-posting filters by
75
+ role. Sales Nav is stronger for recent LinkedIn activity, but not for
76
+ hiring-by-role filtering.
77
+
78
+ ### 4. Apollo (LAST RESORT FOR VOLUME)
57
79
 
58
80
  Large database but leads may not be active on LinkedIn.
59
81
 
@@ -74,25 +96,30 @@ Is your ICP LinkedIn-active (founders, sales, marketing, GTM, engineers)?
74
96
  NO -> Sales Navigator + POSTED_ON_LINKEDIN (4x reply rates)
75
97
  NO -> Sales Navigator (with or without POSTED_ON_LINKEDIN)
76
98
 
99
+ Need hiring-by-role filters? -> Prospeo (has company job-posting filters)
77
100
  Need tech stack targeting? -> Apollo (has technology filters)
78
- Have specific company names? -> Sales Navigator or Apollo with domains
101
+ Have specific company names/domains? -> Sales Navigator for small lists or Prospeo with domainFilterId
79
102
  ```
80
103
 
81
104
  ## ICP-to-Provider Quick Reference
82
105
 
83
- | ICP Type | Recommended | Why |
84
- | ------------------------- | ---------------- | ---------------------------------------------------- |
85
- | Founders, CEOs | Signal Discovery | Highly active on LinkedIn, lots of founder content |
86
- | Sales/GTM leaders | Signal Discovery | Very active, tons of sales content |
87
- | Marketing leaders | Signal Discovery | Active, lots of marketing content |
88
- | Engineers/DevOps | Signal Discovery | Active on tech discussions, open source |
89
- | Enterprise CTOs | Sales Navigator | Less public engagement, use POSTED_ON_LINKEDIN |
90
- | Procurement/Ops | Sales Navigator | Rarely engage publicly |
91
- | Few specific companies | Sales Navigator | Use CURRENT_COMPANY filter (lookup each company) |
92
- | Company domain list (ABM) | Apollo | Has `q_organization_domains_list` for bulk targeting |
93
- | Tech stack users | Apollo | Has technology filters |
94
-
95
- **Default:** Try Signal Discovery first. Fall back to Sales Navigator if ICP is too niche.
106
+ | ICP Type | Recommended | Why |
107
+ | ------------------------- | ---------------- | -------------------------------------------------- |
108
+ | Founders, CEOs | Signal Discovery | Highly active on LinkedIn, lots of founder content |
109
+ | Sales/GTM leaders | Signal Discovery | Very active, tons of sales content |
110
+ | Marketing leaders | Signal Discovery | Active, lots of marketing content |
111
+ | Engineers/DevOps | Signal Discovery | Active on tech discussions, open source |
112
+ | Enterprise CTOs | Sales Navigator | Less public engagement, use POSTED_ON_LINKEDIN |
113
+ | Procurement/Ops | Sales Navigator | Rarely engage publicly |
114
+ | Few specific companies | Sales Navigator | Use CURRENT_COMPANY filter (lookup each company) |
115
+ | Hiring-led targeting | Prospeo | Has company job-posting filters by role |
116
+ | Company domain list (ABM) | Prospeo | Uses `domainFilterId` for domain targeting |
117
+ | Tech stack users | Apollo | Has technology filters |
118
+
119
+ **Default:** Try Signal Discovery first only when there is no stronger source
120
+ direction and the ICP is plausibly active in public LinkedIn conversations. Fall
121
+ back to Sales Navigator if ICP is too niche. Use Prospeo first when the ask is
122
+ hiring-led, account/domain-led, or broad verified-contact expansion.
96
123
 
97
124
  ## ABM / Account-Based Targeting
98
125
 
@@ -103,10 +130,10 @@ When user has a list of target companies:
103
130
  - Requires looking up each company individually
104
131
  - Can combine with POSTED_ON_LINKEDIN for active users
105
132
 
106
- - **Many companies (domain list):** Use Apollo with `q_organization_domains_list`
107
- - Supports bulk domain upload (up to 190 per search)
108
- - Example: `filters: { q_organization_domains_list: ["stripe.com", "notion.so", "figma.com"] }`
109
- - Trade-off: No POSTED_ON_LINKEDIN equivalent, so reply rates may be lower
133
+ - **Many companies (domain list):** Use Prospeo with `domainFilterId`
134
+ - Supports large domain filters through `load_csv_domains` or `save_domain_filters`
135
+ - Example: create a domain filter for `stripe.com`, `notion.so`, and `figma.com`, then pass `domainFilterId` into `search_prospeo`
136
+ - Trade-off: No POSTED_ON_LINKEDIN equivalent, so LinkedIn activity warmth may be lower
110
137
 
111
138
  ## Recommendation Templates
112
139
 
@@ -141,6 +168,19 @@ Here's why:
141
168
  Should I search for active {icp} on Sales Navigator?
142
169
  ```
143
170
 
171
+ **Prospeo (hiring-led ICP):**
172
+
173
+ ```
174
+ Since this campaign depends on companies actively hiring for **{roleThemes}**, I'd recommend **Prospeo** first.
175
+
176
+ Here's why:
177
+ - Prospeo can filter companies by open-role themes using job-posting filters
178
+ - We can pair those hiring signals with founder, GTM, recruiting, or revenue-owner contacts
179
+ - Sales Nav is useful for LinkedIn activity and referrals, but it cannot filter companies by hiring role
180
+
181
+ Should I search Prospeo for companies hiring {roleThemes} and the right hiring-owner contacts?
182
+ ```
183
+
144
184
  **Apollo (tech stack targeting):**
145
185
 
146
186
  ```
@@ -154,17 +194,17 @@ Here's why:
154
194
  Should I search Apollo for {techStack} users?
155
195
  ```
156
196
 
157
- **Apollo ABM (company domains):**
197
+ **Prospeo ABM (company domains):**
158
198
 
159
199
  ```
160
- Since you have a **list of target companies**, I'd recommend **Apollo** with domain targeting.
200
+ Since you have a **list of target company domains**, I'd recommend **Prospeo** with a domain filter.
161
201
 
162
202
  Here's why:
163
- - Apollo supports bulk domain lists (up to 190 companies per search)
203
+ - Prospeo is the campaign-default path for supplied company-domain lists
204
+ - We'll create a `domainFilterId` from the CSV or pasted domains before searching
164
205
  - We can find decision-makers at your exact target accounts
165
- - Just share the domains (e.g., stripe.com, notion.so) and I'll find your ICP at those companies
166
206
 
167
- Note: Reply rates may be lower than Signal Discovery or Sales Nav with POSTED_ON_LINKEDIN, since we can't filter for LinkedIn activity. Consider combining with a strong personalized message.
207
+ Note: LinkedIn activity warmth may be lower than Signal Discovery or Sales Nav with POSTED_ON_LINKEDIN, so we should inspect the first review batch before scaling.
168
208
 
169
209
  Can you share your target company domains?
170
210
  ```
@@ -179,7 +219,8 @@ Do not hardcode provider options. Build them from the framework:
179
219
  - If user already has some other list shape that does not fit those CSV paths -> proceed with provider selection
180
220
  - If the user specifies a provider or research plan -> treat that as a hard override
181
221
  - Else if `providerPreference` override exists -> recommend that provider
182
- - Else use the decision hierarchy above (Signal Discovery -> Sales Nav -> Apollo)
222
+ - Else use the decision hierarchy above only when there is no stronger source
223
+ direction (Signal Discovery -> Sales Nav -> Prospeo -> Apollo)
183
224
  2. Build options from `providerOrder` and `framework.providers[providerId].askOption`
184
225
  3. Mark the recommended option by appending " (Recommended)" if needed
185
226
 
@@ -49,9 +49,10 @@ for debug output.
49
49
  12. Sync the approved template into the campaign brief.
50
50
  13. After message approval, keep the watched app on Filter Leads while the
51
51
  bounded enrichment/filter cascade starts.
52
- 14. Move to Messages only after at least one review row passes, generate and
53
- review bounded messages, then hand off to Settings, sender, sequence, and
54
- explicit launch greenlight.
52
+ 14. Move to Messages only after at least one review row passes and one generated
53
+ message is ready for review. Do not wait for a stronger sample once that
54
+ first passing message exists; review it, then hand off to Settings, sender,
55
+ sequence, and explicit launch greenlight.
55
56
 
56
57
  There is no normal approval-packet, commit-gate, atomic-mint, or local
57
58
  artifact-validation step. Those belong only to legacy validation/rehearsal
@@ -125,6 +126,16 @@ Default source order when the user has not supplied a source:
125
126
  3. broader Sales Nav role/title filters
126
127
  4. Prospeo account/contact expansion
127
128
 
129
+ Use that order only when the brief has no stronger sourcing constraint. If the
130
+ campaign is hiring-led, asks for companies actively hiring specific roles, asks
131
+ for open-role/job-posting signals, or needs verified contacts at companies with
132
+ those hiring signals, start with Prospeo. Prospeo supports
133
+ `company_job_posting_hiring_for` and `company_job_posting_quantity`; Sales Nav
134
+ does not provide hiring-by-role filters. For those campaigns, Signal Discovery
135
+ can be a conversation-signal check if relevant hiring posts are likely, and
136
+ Sales Nav is a LinkedIn activity/referral fallback rather than the main hiring
137
+ filter.
138
+
128
139
  Before any provider prompt, search, source scout, or signal-discovery call,
129
140
  show a short source-plan gate and ask for approval. This first approval
130
141
  authorizes source scouting/search only. The gate must say, in plain language:
@@ -161,14 +172,30 @@ the target engager/source-candidate volume, the bounded campaign review-batch
161
172
  size, the cleanup risk, and the fallback if the selected posts look wrong. The
162
173
  approval label must describe the action with the count, such as "Approve
163
174
  scraping 3 Signal Discovery posts?" For Sales Nav or Prospeo, the label should
164
- name the specific search/import lane. Do not call `import_leads` or
165
- `confirm_lead_list` until this second source-action approval is granted.
175
+ name the specific search/import lane and source lead count. Do not call
176
+ `import_leads` or `confirm_lead_list` until this second source-action approval
177
+ is granted.
178
+
179
+ For Sales Nav and Prospeo, do not ask to import only the 25-row review batch at
180
+ the source-action gate. First-page samples are for source math: compute sampled
181
+ fit after conservative cleanup, project how many good-fit prospects the raw
182
+ pool can yield, and keep refining filters while the projected good-fit pool is
183
+ below the campaign target (normally about 150+ usable prospects unless the
184
+ campaign/source defaults say otherwise). Once the lane clears the target, ask
185
+ approval to materialize the source list with
186
+ `targetLeadCount = min(rawResultCount, providerMax, ceil(targetGoodFitLeads /
187
+ projectedFitRateAfterCleanup))`. After that source list is ready,
188
+ `confirm_lead_list` imports only the bounded review batch into the campaign
189
+ table for fit and message review.
190
+
166
191
  After the user approves this concrete source-action gate, do not show the
167
192
  Source Recommendation again and do not ask another source approval question.
168
193
  Acknowledge once, then call `import_leads` immediately with the approved source
169
- math. For Signal Discovery, pass `provider: "signal-discovery"`,
170
- `targetEngagerCount`, `maxPostsToScrape`, and `confirmed: true`; the tool owns
171
- moving the watch UI to source-list progress after a lead-list/job id exists.
194
+ math. For Sales Nav/Prospeo, pass `targetLeadCount` as the approved source-list
195
+ export count, not the review-batch size. For Signal Discovery, pass
196
+ `provider: "signal-discovery"`, `targetEngagerCount`, `maxPostsToScrape`, and
197
+ `confirmed: true`; the tool owns moving the watch UI to source-list progress
198
+ after a lead-list/job id exists.
172
199
 
173
200
  For Signal Discovery, use this compact source-action approval shape after
174
201
  selected posts exist:
@@ -181,7 +208,7 @@ Use Signal Discovery first.
181
208
  **Good-fit target:** ~150 prospects after cleanup, enrichment, and filters<br>
182
209
  **Source-candidate plan:** scrape ~1,000 raw engagers using a conservative 15% fit-rate assumption<br>
183
210
  **Planning floor:** continue only when sampled/projected fit is at least 10% after cleanup; below that, switch to Sales Nav recent activity<br>
184
- **Review checkpoint:** import the first 25 leads into the campaign for fit and message review before scaling<br>
211
+ **Review checkpoint:** after the source list exists, clone only the bounded review batch into the campaign for fit and message review<br>
185
212
 
186
213
  ### Selected posts
187
214
 
@@ -202,8 +229,9 @@ This gives enough volume to work toward ~150 good-fit prospects, while
202
229
  keeping the source tied to people already engaging with Claude Code outbound /
203
230
  AI-native sales workflows.
204
231
 
205
- **First pass:** build the source list, then import only the 25-lead review
206
- batch so we can inspect fit and messages before scaling.
232
+ **First pass:** build the source list at the approved source-candidate target,
233
+ then clone only the bounded review batch into the campaign so we can inspect
234
+ fit and messages before sending.
207
235
 
208
236
  **Fallback:** if sampled/projected fit falls below 10%, or if the review batch
209
237
  is too vendor-heavy, agency-heavy, or off-ICP, switch to Sales Nav recent
@@ -213,7 +241,11 @@ activity.
213
241
  A source recommendation must show concrete evidence: source lane, filters or
214
242
  recipe, raw volume, sample size, sampled fits as n/N plus percentage/range,
215
243
  estimated usable prospects, cleanup risk, runner-up, and what approval
216
- authorizes.
244
+ authorizes. For Sales Nav/Prospeo it must also show source export math:
245
+ `rawResultCount`, `sampledFitRate`, conservative projected fit rate,
246
+ `targetLeadCount` for the source list, and projected good-fit count from that
247
+ export. If projected good-fit count is below target, the recommendation is to
248
+ refine or broaden filters, not to import a 25-row batch.
217
249
 
218
250
  Supplied profile CSVs, company/domain CSVs, pasted domains, and existing
219
251
  Sellable lead lists are supported, but keep provider mechanics out of the first
@@ -222,24 +254,28 @@ customer-facing source-choice labels.
222
254
  ## Post-Lead Workstreams
223
255
 
224
256
  After `confirm_lead_list` imports a non-empty bounded review batch and
225
- `get_rows_minimal({ tableId: workflowTableId })` proves rows exist, call
226
- `get_post_find_leads_scout_registry` and start the Message Draft Builder branch
227
- immediately when the current host exposes that returned agent. Do this before
228
- asking the filter-choice question. If no real background branch can start, say
229
- the real sequence and do not claim background drafting is running.
230
-
231
- The message branch uses live CampaignOffer/source/table state as the source of
232
- truth and may draft while the user is choosing filters. It does not write
233
- message cells, enrich rows, attach sequences, or unlock Settings. The Lead Fit
234
- Builder branch starts only after the user chooses filters. Debug markdown/json
235
- artifacts are optional only.
257
+ `get_rows_minimal({ tableId: workflowTableId })` proves rows exist, ask the
258
+ filter-choice question immediately. Do not call
259
+ `get_post_find_leads_scout_registry`, load filter/message subskill prompts, or
260
+ spawn post-lead workers before that question. The only customer-facing work
261
+ before the question should be a short review-batch summary and the choice:
262
+ add filters, skip filters, or revise source.
263
+
264
+ If the user chooses filters, then load the post-lead registry/reference needed
265
+ for filtering and start the Lead Fit Builder. Also start the Message Draft
266
+ Builder in the background from the same campaign/table basis as soon as the
267
+ filter-choice answer is known. If the user skips filters, move the watched app to
268
+ Messages and run the Message Draft Builder there. Debug markdown/json artifacts
269
+ are optional only.
236
270
 
237
271
  When the user chooses filters, immediately call
238
272
  `update_campaign({ campaignId, enableICPFilters: true, currentStep: "create-icp-rubric", watchNarration })`
239
273
  before rubric thinking or branch work. The watched app should move to Filter
240
274
  Rules quickly. After `save_rubrics`, the watched app should move to
241
275
  `apply-icp-rubric` / Filter Leads so the user can see the saved filters are
242
- ready before message approval unlocks any enrichment or filtering.
276
+ ready and wait there for the background message agent. Tell the user explicitly
277
+ that a message agent is drafting the first-message template while enrichment,
278
+ filtering, and Generate Message cells remain blocked.
243
279
 
244
280
  Lead Fit Builder persists production rubrics with `save_rubrics` when filters
245
281
  are enabled. It must not require `brief.md`, `lead-review.md`, or
@@ -274,14 +274,18 @@ keep the output numeric but do not claim the source scout was parallel or
274
274
  surface install status to the customer.
275
275
 
276
276
  For post-lead work, call `get_post_find_leads_scout_registry` after
277
+ the user chooses filters, not before the filter-choice question. After
277
278
  `confirm_lead_list` imports a non-empty bounded review batch and
278
- `get_rows_minimal` proves rows exist. Start `post-find-leads-message-scout`
279
- immediately when real subagents are available, before asking the filter-choice
280
- question. Start `post-find-leads-filter-scout` only after the user chooses
281
- filters. This is the same registry pattern as source scouting, but the trigger
282
- for message drafting is review-batch import, not filter approval. The join gate
283
- is live branch readiness: saved rubrics or a resolved skip-filter choice, plus a
284
- message recommendation grounded in the same campaign/table basis.
279
+ `get_rows_minimal` proves rows exist, ask add-filters vs skip-filters
280
+ immediately. Once the user answers, start `post-find-leads-message-scout` /
281
+ message generation from the same campaign/table basis. If the user chooses
282
+ filters, also start `post-find-leads-filter-scout`, move the browser to Filter
283
+ Rules, save rubrics, then keep the browser on Filter Leads while the message
284
+ recommendation is reviewed. If the user skips filters, move the browser to
285
+ Messages/message review. The join gate is live branch readiness: saved rubrics
286
+ or a resolved skip-filter choice, plus a message recommendation grounded in the
287
+ same campaign/table basis. Enrichment, filtering, and product Generate Message
288
+ cells still wait for template approval.
285
289
 
286
290
  Only promise parallel post-lead work when parallel work actually started. If the
287
291
  host cannot or should not launch background branches, say the real sequence:
@@ -588,20 +588,9 @@
588
588
  ]
589
589
  },
590
590
  {
591
- "action": "launch_message_draft_builder_after_review_batch_import",
592
- "tool": "get_post_find_leads_scout_registry",
593
- "target": "post-find-leads-message-scout",
594
- "requiresNonEmptyRows": true,
595
- "mustRunBefore": [
596
- "filter-choice question",
597
- "filter approval",
598
- "save_rubrics"
599
- ],
600
- "mode": "background_when_host_supports_subagents",
601
- "fallback": "if no real background branch can start, say the real sequence and do not claim background drafting is running",
602
- "toolCallRequiredInBranch": "get_subskill_prompt({ subskillName: \"generate-messages\", offset, limit }) until hasMore=false",
603
- "runtimeProofTransport": "CampaignOffer.watchNarration.workerDetails.messageDraftBuilder",
604
- "purpose": "start the provisional message draft from the imported review batch immediately; do not defer this to filter approval"
591
+ "action": "summarize_review_batch_and_advance_to_filter_choice",
592
+ "purpose": "ask filter choice immediately; no post-lead registries, filter refs, or message prompts first",
593
+ "maxCustomerCopyLines": 4
605
594
  }
606
595
  ],
607
596
  "requiredCampaignState": [
@@ -611,8 +600,6 @@
611
600
  ],
612
601
  "allowedTools": [
613
602
  "get_subskill_prompt",
614
- "get_subskill_asset",
615
- "get_post_find_leads_scout_registry",
616
603
  "import_leads",
617
604
  "wait_for_lead_list_ready",
618
605
  "confirm_lead_list",
@@ -620,16 +607,18 @@
620
607
  "get_rows_minimal",
621
608
  "update_campaign",
622
609
  "AskUserQuestion",
623
- "request_user_input",
624
- "Task",
625
- "spawn_agent"
610
+ "request_user_input"
626
611
  ],
627
612
  "doNotAllow": [
613
+ "get_post_find_leads_scout_registry",
614
+ "Task",
615
+ "spawn_agent",
628
616
  "list_senders",
629
617
  "queue_cells",
630
618
  "start_campaign",
631
619
  "enrich_with_prospeo",
632
- "bulk_enrich_with_prospeo"
620
+ "bulk_enrich_with_prospeo",
621
+ "save_rubrics"
633
622
  ],
634
623
  "waitFor": "review_batch_imported",
635
624
  "transitions": {
@@ -659,6 +648,13 @@
659
648
  ]
660
649
  }
661
650
  ],
651
+ "hardRules": [
652
+ "ask_filter_choice_immediately_after_review_batch_import",
653
+ "do_not_call_get_subskill_prompt_before_filter_choice",
654
+ "do_not_call_get_subskill_asset_before_filter_choice",
655
+ "do_not_call_get_post_find_leads_scout_registry_before_filter_choice",
656
+ "do_not_spawn_post_lead_agents_before_filter_choice"
657
+ ],
662
658
  "requiredCampaignState": [
663
659
  "campaignId",
664
660
  "campaignBrief",
@@ -672,6 +668,11 @@
672
668
  "get_campaign_navigation_state"
673
669
  ],
674
670
  "doNotAllow": [
671
+ "get_subskill_prompt",
672
+ "get_subskill_asset",
673
+ "get_post_find_leads_scout_registry",
674
+ "Task",
675
+ "spawn_agent",
675
676
  "create_campaign",
676
677
  "list_senders",
677
678
  "import_leads",
@@ -693,7 +694,7 @@
693
694
  },
694
695
  {
695
696
  "id": "post-lead-workstreams",
696
- "label": "Filter and message workstreams",
697
+ "label": "Filter workstream",
697
698
  "onEnter": [
698
699
  {
699
700
  "action": "persist_add_filters_approval",
@@ -715,17 +716,18 @@
715
716
  },
716
717
  "mustRunBefore": [
717
718
  "get_post_find_leads_scout_registry",
718
- "launch_or_reuse_post_lead_workstreams",
719
+ "launch_lead_fit_builder_after_filter_choice",
719
720
  "save_rubrics"
720
721
  ]
721
722
  },
722
723
  {
723
724
  "tool": "get_post_find_leads_scout_registry",
724
- "purpose": "load canonical post-lead worker names"
725
+ "purpose": "load canonical post-lead worker names only after the user has chosen filters"
725
726
  },
726
727
  {
727
- "action": "launch_or_reuse_lead_fit_builder_and_existing_message_draft",
728
+ "action": "launch_lead_fit_builder_after_filter_choice",
728
729
  "mode": "parallel_when_host_supports_subagents",
730
+ "target": "post-find-leads-filter-scout",
729
731
  "inputs": [
730
732
  "campaignId",
731
733
  "campaignBrief",
@@ -734,9 +736,16 @@
734
736
  "reviewBatchRowIds/hash",
735
737
  "filterChoice"
736
738
  ],
737
- "stateSource": "CampaignOffer and workflow table rows",
738
- "debugFilesOptionalOnly": true,
739
- "messageDraftRule": "reuse or join the Message Draft Builder branch that started immediately after confirm_lead_list; do not start first-time message drafting here unless the earlier branch failed to start"
739
+ "stateSource": "live campaign/table",
740
+ "debugFilesOptionalOnly": true
741
+ },
742
+ {
743
+ "action": "launch_message_draft_builder_after_filter_decision",
744
+ "mode": "parallel_when_host_supports_subagents",
745
+ "target": "post-find-leads-message-scout",
746
+ "when": "filters_enabled",
747
+ "doesNotQueueCells": true,
748
+ "customerNarration": "Say message agent is drafting."
740
749
  },
741
750
  {
742
751
  "action": "save_filter_rubrics_to_campaign",
@@ -793,10 +802,13 @@
793
802
  "hardRules": [
794
803
  "after_save_rubrics_currentStep_must_be_apply-icp-rubric",
795
804
  "do_not_move_browser_to_messages_until_filter_leads_step_is_current_or_filters_are_explicitly_skipped",
796
- "message_draft_builder_start_is_not_deferred_until_filters"
805
+ "no_post_lead_worker_or_deep_prompt_before_filter_choice",
806
+ "lead_fit_builder_starts_only_after_filters_enabled",
807
+ "msg_draft_after_filter_choice",
808
+ "msg_draft_no_cells"
797
809
  ],
798
810
  "transitions": {
799
- "post_lead_workstreams_ready": "message-review",
811
+ "post_lead_workstreams_ready": "message-generation",
800
812
  "revise_leads": "find-leads",
801
813
  "revise_rubric": "filter-rubric",
802
814
  "revise_messaging": "message-generation",
@@ -865,14 +877,23 @@
865
877
  "id": "message-generation",
866
878
  "label": "Message generation",
867
879
  "onEnter": [
880
+ {
881
+ "action": "set_message_review_visible_step_by_filter_choice",
882
+ "tool": "update_campaign",
883
+ "branchRules": [
884
+ "yes: currentStep=apply-icp-rubric; wait on Filter Leads",
885
+ "no: currentStep=messages; message review"
886
+ ],
887
+ "watchNarration.stage": "fit-message"
888
+ },
868
889
  {
869
890
  "action": "run_or_reconcile_message_draft_builder",
870
891
  "target": "post-find-leads-message-scout",
871
892
  "toolCallRequiredBeforeDraft": [
872
893
  "get_subskill_prompt({ subskillName: \"generate-messages\", offset, limit }) until hasMore=false",
873
- "for approval-only parent-thread review, use get_subskill_asset({ subskillName: \"create-campaign-v2\", assetPath: \"references/message-review-safety-gate.md\" }) instead of loading generate-messages"
894
+ "parent approval uses references/message-review-safety-gate.md only"
874
895
  ],
875
- "stateSource": "campaignBrief, selected source state, selectedLeadListId, workflowTableId, bounded review-batch row ids/hash",
896
+ "stateSource": "campaignBrief, source, selectedLeadListId, workflowTableId, review-batch row ids/hash",
876
897
  "outputState": "messageDraftRecommendation"
877
898
  }
878
899
  ],
@@ -887,11 +908,12 @@
887
908
  "get_subskill_asset",
888
909
  "get_campaign",
889
910
  "get_rows_minimal",
911
+ "update_campaign",
890
912
  "AskUserQuestion",
891
913
  "request_user_input"
892
914
  ],
893
915
  "toolRules": [
894
- "Before message drafting, the Message Draft Builder branch loads the full get_subskill_prompt({ subskillName: \"generate-messages\" }) prompt and drafts from campaignBrief, selected source state, selectedLeadListId, workflowTableId, and imported review-batch rows. For approval-only parent-thread template review, load references/message-review-safety-gate.md and do not load the full generate-messages prompt.",
916
+ "Message Draft Builder loads full get_subskill_prompt({ subskillName: \"generate-messages\" }) from live state. Parent approval uses message-review-safety-gate only.",
895
917
  "Do not use brief.md, lead-review.md, or lead-sample.json as required live state; those files are optional debug context only.",
896
918
  "The compact messageDraftRecommendation must return templateRecommendation, tokenFillRules, renderedSample, concerns, status, basisToken, outputAt, outputHash, and error or retry detail.",
897
919
  "If campaign/source/table/review-batch basis does not match, classify the output stale or blocked."
@@ -902,7 +924,6 @@
902
924
  "save_rubrics",
903
925
  "import_leads",
904
926
  "confirm_lead_list",
905
- "update_campaign",
906
927
  "queue_cells",
907
928
  "start_campaign",
908
929
  "generate_messages"
@@ -1124,8 +1145,14 @@
1124
1145
  "cellSource": "pending_generate_message_cells_for_passing_rows using approved_campaign_brief_template"
1125
1146
  },
1126
1147
  {
1127
- "tool": "wait_for_campaign_table_ready",
1128
- "purpose": "wait_for_generate_message_cells_to_complete"
1148
+ "tool": "wait_for_rubric_results",
1149
+ "purpose": "wait_for_first_passing_generated_message",
1150
+ "requiredValues": {
1151
+ "includeRows": false,
1152
+ "minPassedCount": 1,
1153
+ "minMessagesCount": 1
1154
+ },
1155
+ "readVia": "stats_only_tool_result"
1129
1156
  },
1130
1157
  {
1131
1158
  "action": "observe_generate_message_results",
@@ -1169,25 +1196,24 @@
1169
1196
  "currentStep": "auto-execute-messaging",
1170
1197
  "watchNarration.stage": "review-ready"
1171
1198
  },
1172
- "watchNarrationRule": "Say what just happened, that review-batch messages are ready in Messages, and that the next action is reviewing or approving generated messages before Settings."
1199
+ "watchNarrationRule": "Say the first passing generated message is ready in Messages; next is review before Settings."
1173
1200
  },
1174
1201
  {
1175
1202
  "action": "ask_generated_message_review_choice",
1176
1203
  "uses": "request_user_input",
1177
1204
  "choices": [
1178
- "Approve generated messages",
1205
+ "Approve generated message and continue to Settings",
1179
1206
  "Revise filters",
1180
1207
  "Revise message template",
1181
1208
  "Pause here"
1182
- ],
1183
- "rule": "Do not advance to Settings until the generated review-batch messages have been reviewed and approved."
1209
+ ]
1184
1210
  }
1185
1211
  ],
1186
1212
  "allowedTools": [
1187
1213
  "get_subskill_asset",
1188
1214
  "get_rows_minimal",
1189
1215
  "queue_cells",
1190
- "wait_for_campaign_table_ready",
1216
+ "wait_for_rubric_results",
1191
1217
  "update_campaign",
1192
1218
  "AskUserQuestion",
1193
1219
  "request_user_input"
@@ -1201,6 +1227,7 @@
1201
1227
  "hardRules": [
1202
1228
  "critique_failure_never_escalates",
1203
1229
  "critique_sample_size_bounded_by_config",
1230
+ "first_passing_generated_message_unblocks_review",
1204
1231
  "critics_fixed_at_targeting_copy_voice",
1205
1232
  "synthesis_enforces_phase_84_token_contract",
1206
1233
  "opus_reserved_for_highest_value_subset",
@@ -60,9 +60,10 @@ auto-revise leads.
60
60
  explicit batch count anyway so future larger expansion batches do not
61
61
  accidentally stop early
62
62
  (see §Known Tool Behaviors #3)
63
- - minPassedCount=1 means one passing filtered row unblocks Step 15
64
- Generate Message observation. Do not wait for all sample rows to finish
65
- before messages start.
63
+ - minPassedCount=1 means one passing filtered row unblocks Step 15 Generate
64
+ Message observation. Step 15 then waits only for one generated message
65
+ (`minMessagesCount=1`) before review. Do not wait for all sample rows to
66
+ finish before messages start.
66
67
 
67
68
  7. call `wait_for_rubric_results` with `includeRows=false`; extract ONLY:
68
69
  - ready: boolean
@@ -101,7 +102,9 @@ auto-revise leads.
101
102
  10. branch:
102
103
  if passInSample >= 1:
103
104
  proceed to Step 15 (auto-execute-messaging) with currently passing rows
104
- so Generate Message can start without waiting for the full sample
105
+ so Generate Message can start without waiting for the full sample. Once
106
+ one passing generated message is ready, stop for user review instead of
107
+ waiting for a stronger sample.
105
108
  else:
106
109
  diagnose (see Brief-vs-List Diagnosis below)
107
110
  revisionRound += 1
@@ -179,7 +182,8 @@ processing makes the experience feel frozen.
179
182
 
180
183
  Workaround: treat timeout stats as a partial sample. If at least one row has
181
184
  passed, move to Step 15 and observe or queue Generate Message for the passing
182
- rows. If zero rows have passed and no active processing is visible, stop at
185
+ rows; Step 15 stops when one generated message is ready. If zero rows have
186
+ passed and no active processing is visible, stop at
183
187
  `Status: sample-needs-revision` before Settings. Show the completed / passed /
184
188
  pending counts and ask whether to revise source, revise filter/rubric, or wait
185
189
  once only if active processing is still visible.
@@ -106,7 +106,7 @@ fills the source lead list; it does **not** clone rows into the campaign table:
106
106
  ```text
107
107
  import_leads({
108
108
  campaignOfferId,
109
- targetLeadCount: <sourceCandidateTarget; provider default>,
109
+ targetLeadCount: <sourceCandidateTarget from approved good-fit math>,
110
110
  // Signal Discovery only:
111
111
  targetEngagerCount: <ceil(targetGoodFitLeads / sampledFitRateAfterCleanup)>,
112
112
  maxPostsToScrape: <postsNeeded from approved math>
@@ -114,9 +114,15 @@ import_leads({
114
114
  ```
115
115
 
116
116
  Provider is inherited from the selected source decision (not re-selected here)
117
- and should already be saved on the campaign before import. Response returns
118
- `{ imported, skipped, duplicates }` surface all three to the escalation logic
119
- so dedup ratios are visible.
117
+ and should already be saved on the campaign before import. For Sales Nav and
118
+ Prospeo, `<sourceCandidateTarget>` is not the 25-row review batch. It is the
119
+ source-list export/materialization count from the approved source math:
120
+ `min(rawResultCount, providerMax, ceil(targetGoodFitLeads /
121
+ projectedFitRateAfterCleanup))`. If projected good fits remain below the target
122
+ (often about 150+ usable prospects unless the campaign/source defaults specify
123
+ otherwise), return to find-leads and refine filters before import. Response
124
+ returns `{ imported, skipped, duplicates }` — surface all three to the
125
+ escalation logic so dedup ratios are visible.
120
126
 
121
127
  For Signal Discovery, do not scrape every currently selected/promoted sample
122
128
  post by default. Before `import_leads`, reconcile selected posts with the