@sellable/mcp 0.1.155 → 0.1.157

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.
@@ -51,14 +51,15 @@ currentStep: "signal-discovery" })` before sampling so the watched Signal
51
51
  visible headline/display-name cues only. Do not enrich people during
52
52
  viability estimation.
53
53
  7. Compute capacity before recommending the source: source target good-fit
54
- leads (default 150 for Signal Discovery unless the parent supplies a target),
55
- reachable engagers,
56
- sampled ICP-fit rate as `n/N` plus an easy percentage/range, expected usable
57
- leads per 100 engagers before and after a conservative dedupe/cleanup
58
- factor, required engagers to scrape (`source target / fit rate`), average
59
- reachable engagers per right-content post, expected usable leads per
60
- right-content post after dedupe/cleanup, posts needed to hit the target, and
61
- whether sampled/projected fit clears the 10% planning floor.
54
+ leads (default 300 for Signal Discovery unless the parent supplies a target),
55
+ reachable engagers, sampled headline-fit rate as `n/N` plus an easy
56
+ percentage/range, expected headline-fit prospects per 100 engagers, required
57
+ engagers to scrape (`source target / sampled headline-fit rate`), average
58
+ reachable engagers per right-content post, expected headline-fit prospects
59
+ per right-content post, posts needed to hit the target, and whether
60
+ sampled/projected headline-fit rate clears the 10% planning floor. Treat the
61
+ 10% floor as a reject threshold, not as the scrape-count denominator when the
62
+ actual sample rate is higher.
62
63
  8. Select/promote enough right-content posts to plausibly hit the target. If the
63
64
  warm Signals pool is useful but too small, return the expected warm range and
64
65
  recommend Sales Nav/Prospeo for scale instead of padding with noisy posts.
@@ -72,12 +73,12 @@ Return a concise structured result with:
72
73
  - `selected_posts` with URL/title, author/topic, age, engager count, sampled engagers, good fits as n/N, estimated usable prospects per post, use/discard
73
74
  - `sample_leads`, if any
74
75
  - `approval_math` with eligible posts, source target good-fit leads, sampled
75
- engagers, ICP-fit rate as `n/N` plus percentage/range, good-fit prospects per
76
- 100 engagers, required engagers to scrape, average reachable engagers per
77
- post, expected usable prospects per post after cleanup, posts needed for
78
- target, whether the 10% planning floor clears after cleanup, selected post
79
- count, internal campaign-table execution-slice size, expected usable lead range, and scale
80
- fallback
76
+ engagers, headline-fit rate as `n/N` plus percentage/range, headline-fit
77
+ prospects per 100 engagers, required engagers to scrape, average reachable
78
+ engagers per post, expected headline-fit prospects per post, posts needed for
79
+ target, whether the 10% planning floor clears, selected post count, internal
80
+ campaign-table execution-slice size, expected headline-fit lead range, and
81
+ scale fallback
81
82
  - `estimated_good_fit_range`
82
83
  - `message_context_strength`, directional and source-specific
83
84
  - `false_positive_patterns`
@@ -93,14 +94,18 @@ Evidence standards:
93
94
  GTM/outbound/buyer pain, workflow, or role context that makes the campaign
94
95
  relevant.
95
96
  - Do not make the user infer capacity. Say, plainly, how many eligible posts
96
- exist, how many sampled engagers looked in-ICP, how many good-fit prospects
97
- that implies per 100 engagers, how many usable prospects one right-content
98
- post should yield after cleanup, how many engagers must be scraped for the
99
- 300-good-fit source target at the 20% working assumption, how many posts are
100
- needed for that source target, and which posts you would use. Also say the
101
- source list is copied into the campaign and only the first campaign-table
102
- execution slice is processed internally for filter and message setup.
97
+ exist, how many sampled engagers passed the headline ICP rubric, what
98
+ headline-fit rate that implies per 100 engagers, how many headline-fit
99
+ prospects one right-content post should yield, how many engagers must be
100
+ scraped for the 300 headline-fit source target using the sampled pass rate
101
+ (or the 20% working assumption only when there is no stronger sample), how
102
+ many posts are needed for that source target, and which posts you would use.
103
+ Also say the source list is copied into the campaign and only the first
104
+ campaign-table execution slice is processed internally for filter and message
105
+ setup.
103
106
  - If `fetch_post_engagers` is unavailable or fails, report that explicitly and mark the estimate lower-confidence.
104
- - Keep LinkedIn Engagement viable when selected posts can produce roughly 300+ ICP-fit warm prospects before final filtering, even if Sales Nav is more scalable.
105
- - If sampled/projected fit after cleanup is below 10%, reject the Signals scrape
106
- path and recommend Sales Nav recent activity as the next source.
107
+ - Keep LinkedIn Engagement viable when selected posts can produce roughly 300+
108
+ headline-fit warm prospects before final filtering, even if Sales Nav is more
109
+ scalable.
110
+ - If sampled/projected headline-fit rate is below 10%, reject the Signals
111
+ scrape path and recommend Sales Nav recent activity as the next source.
@@ -725,8 +725,8 @@ function buildFilterChoiceWatchNarration({ sourceLeadCount, reviewRowCount, samp
725
725
  function buildSignalDiscoverySearchWatchNarration() {
726
726
  return {
727
727
  stage: "find-leads",
728
- headline: "Searching Signal Discovery",
729
- visibleState: "The browser is showing Signal Discovery while LinkedIn post searches run.",
728
+ headline: "Searching LinkedIn posts",
729
+ visibleState: "The browser is showing the LinkedIn post search step while approved themes run.",
730
730
  agentIntent: "Codex is searching the approved post themes and will ask before scraping selected engagers into a source list.",
731
731
  nextAction: "Review selected posts before scraping",
732
732
  safety: "Scrape approval is the next gate.",
@@ -734,11 +734,11 @@ function buildSignalDiscoverySearchWatchNarration() {
734
734
  }
735
735
  function buildSignalDiscoveryResultsWatchNarration(postsReturned) {
736
736
  const postCopy = typeof postsReturned === "number"
737
- ? `${postsReturned.toLocaleString("en-US")} post${postsReturned === 1 ? "" : "s"} matched the approved search themes and are visible in Signal Discovery.`
738
- : "Signal Discovery results are visible for review.";
737
+ ? `${postsReturned.toLocaleString("en-US")} post${postsReturned === 1 ? "" : "s"} matched the approved search themes and are visible for review.`
738
+ : "LinkedIn post results are visible for review.";
739
739
  return {
740
740
  stage: "find-leads",
741
- headline: "Review Signal Discovery posts",
741
+ headline: "Review LinkedIn posts",
742
742
  visibleState: postCopy,
743
743
  agentIntent: "Codex is promoting the strongest posts, then it will ask before scraping the selected engager pool.",
744
744
  nextAction: "Approve selected posts before scraping",
@@ -749,9 +749,9 @@ function buildSelectedPostApprovalWatchNarration(selectedPostCount) {
749
749
  return {
750
750
  stage: "find-leads",
751
751
  headline: "Approve selected-post scrape",
752
- visibleState: `${selectedPostCount.toLocaleString("en-US")} LinkedIn post${selectedPostCount === 1 ? "" : "s"} selected in Signal Discovery.`,
752
+ visibleState: `${selectedPostCount.toLocaleString("en-US")} LinkedIn post${selectedPostCount === 1 ? "" : "s"} selected for the source test.`,
753
753
  agentIntent: "Codex is asking before scraping this selected engager pool into a source list.",
754
- nextAction: `Approve scraping ${selectedPostCount.toLocaleString("en-US")} Signal Discovery post${selectedPostCount === 1 ? "" : "s"}`,
754
+ nextAction: `Approve scraping ${selectedPostCount.toLocaleString("en-US")} selected LinkedIn post${selectedPostCount === 1 ? "" : "s"}`,
755
755
  safety: "Scrape approval is the next gate.",
756
756
  };
757
757
  }
@@ -775,12 +775,12 @@ function buildSignalDiscoverySourceRecommendation({ selectedPosts, }) {
775
775
  const estimatedGoodFit = totalEngagement * defaultFitRate;
776
776
  return `## Source Recommendation
777
777
 
778
- Use Signal Discovery first.
778
+ Use LinkedIn engagement first.
779
779
 
780
- **Goal:** ~${targetGoodFitLeads.toLocaleString("en-US")} good-fit prospects after cleanup, enrichment, and filters<br>
781
- **Working assumption:** ~${Math.round(defaultFitRate * 100)}% of raw post engagers become good-fit prospects<br>
780
+ **Goal:** ~${targetGoodFitLeads.toLocaleString("en-US")} headline-fit prospects from relevant LinkedIn engagement<br>
781
+ **Working assumption:** ~${Math.round(defaultFitRate * 100)}% of raw post engagers pass headline filtering unless a real sample supports a different rate<br>
782
782
  **Engagers needed:** ~${sourceCandidateTarget.toLocaleString("en-US")} raw engagers<br>
783
- **Planning floor:** continue with Signal Discovery only when sampled/projected fit is at least ${Math.round(minPlanningFitRate * 100)}% after cleanup; below that, switch to Sales Nav recent activity<br>
783
+ **Planning floor:** continue with LinkedIn engagement only when sampled/projected headline-fit rate is at least ${Math.round(minPlanningFitRate * 100)}%; below that, switch to Sales Nav recent activity<br>
784
784
  **Review checkpoint:** copy the confirmed source list into the campaign, then process the first ${reviewBatchSize.toLocaleString("en-US")} leads for fit and message review before scaling
785
785
 
786
786
  ### Selected posts
@@ -790,21 +790,21 @@ Use Signal Discovery first.
790
790
  ${tableRows || "| Selected posts | Campaign-matched public engagement | - |"}
791
791
 
792
792
  **Total visible pool:** ${formatApproxInteger(totalEngagement)} engagers<br>
793
- **Estimated good-fit pool at ${Math.round(defaultFitRate * 100)}%:** ${formatApproxInteger(estimatedGoodFit)} prospects before dedupe/risk cleanup
793
+ **Estimated headline-fit pool at ${Math.round(defaultFitRate * 100)}%:** ${formatApproxInteger(estimatedGoodFit)} prospects before enrichment and deeper fit review
794
794
 
795
795
  ### Recommendation
796
796
 
797
797
  Approve scraping these ${selectedCount} posts.
798
798
 
799
- This gives enough volume to target ~${targetGoodFitLeads.toLocaleString("en-US")} good-fit prospects after cleanup, while keeping the source tied to people already engaging with the campaign's strongest public buying signals.
799
+ This gives enough volume to target ~${targetGoodFitLeads.toLocaleString("en-US")} headline-fit prospects, while keeping the source tied to people already engaging with the campaign's strongest public buying signals.
800
800
 
801
801
  **First pass:** build the source list, copy it into the campaign, then use the first ${reviewBatchSize.toLocaleString("en-US")} campaign rows as the internal setup slice for filters and messages before scaling.
802
802
 
803
- **Fallback:** if the sampled/projected fit rate is below ${Math.round(minPlanningFitRate * 100)}%, or if the source sample is too vendor-heavy, agency-heavy, or off-ICP, switch to Sales Nav recent activity.
803
+ **Fallback:** if the sampled/projected headline-fit rate is below ${Math.round(minPlanningFitRate * 100)}%, or if the source sample is too vendor-heavy, agency-heavy, or off-ICP, switch to Sales Nav recent activity.
804
804
 
805
805
  Approval card should say:
806
806
 
807
- **Approve scraping ${selectedCount} Signal Discovery post${selectedCount === 1 ? "" : "s"}?**`;
807
+ **Approve scraping ${selectedCount} selected LinkedIn post${selectedCount === 1 ? "" : "s"}?**`;
808
808
  }
809
809
  function normalizeImportProvider(provider) {
810
810
  if (provider === "apollo-ai" || provider === "apollo")
@@ -1390,7 +1390,7 @@ export const leadToolDefinitions = [
1390
1390
  },
1391
1391
  targetEngagerCount: {
1392
1392
  type: "number",
1393
- description: "Signal Discovery: target number of post engagers/source candidates to scrape. Default planning target is about 300 good fits at a 20% raw-engager fit assumption, or about 1500 engagers. If the sampled/projected fit rate is below the 10% planning floor after cleanup, switch to the next provider instead of scaling noisy engagers. Limits selected posts before starting scrape, with a backend hard cap of 10 posts.",
1393
+ description: "Signal Discovery: target number of post engagers/source candidates to scrape. Default planning target is about 300 headline-fit prospects at a 20% raw-engager headline-pass assumption, or about 1500 engagers. Use real sample math when available: target headline-fit prospects divided by sampled headline-pass rate. If the sampled/projected headline-fit rate is below the 10% planning floor, switch to the next provider instead of scaling noisy engagers. Limits selected posts before starting scrape, with a backend hard cap of 10 posts.",
1394
1394
  },
1395
1395
  maxPostsToScrape: {
1396
1396
  type: "number",
@@ -3089,15 +3089,15 @@ export async function selectPromisingPosts(input) {
3089
3089
  const { reviewBatchSize } = getSignalDiscoverySourcePlanDefaults();
3090
3090
  sourceRecommendation = `## Source Recommendation
3091
3091
 
3092
- Use Signal Discovery first.
3092
+ Use LinkedIn engagement first.
3093
3093
 
3094
- **Recommendation:** approve scraping the ${selectionResult.selectedCount} selected Signal Discovery post${selectionResult.selectedCount === 1 ? "" : "s"}.
3094
+ **Recommendation:** approve scraping the ${selectionResult.selectedCount} selected LinkedIn post${selectionResult.selectedCount === 1 ? "" : "s"}.
3095
3095
 
3096
3096
  **First pass:** build the source list, copy it into the campaign, then process only the first ${reviewBatchSize.toLocaleString("en-US")} leads so we can inspect quality before scaling.
3097
3097
 
3098
3098
  Approval card should say:
3099
3099
 
3100
- **Approve scraping ${selectionResult.selectedCount} Signal Discovery post${selectionResult.selectedCount === 1 ? "" : "s"}?**`;
3100
+ **Approve scraping ${selectionResult.selectedCount} selected LinkedIn post${selectionResult.selectedCount === 1 ? "" : "s"}?**`;
3101
3101
  }
3102
3102
  return {
3103
3103
  success: true,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sellable/mcp",
3
- "version": "0.1.155",
3
+ "version": "0.1.157",
4
4
  "type": "module",
5
5
  "description": "Sellable MCP server for Claude Code and Codex campaign workflows",
6
6
  "main": "dist/index.js",
@@ -139,10 +139,12 @@ should say:
139
139
  - what approval covers in one concise line
140
140
 
141
141
  Do not surface blanket source heuristics as product copy. Make the
142
- recommendation specific to the campaign. If Signal Discovery is recommended,
143
- name the exact post themes you will search. If relevant public conversations
144
- look unlikely, recommend the specific Sales Nav or Prospeo lane instead and say
145
- why. Do not call `search_signals`, `search_sales_nav`, `search_prospeo`,
142
+ recommendation specific to the campaign. If LinkedIn engagement is recommended,
143
+ name the exact post themes you will search and avoid using "Signal Discovery" in
144
+ customer-facing chat; that is the internal provider/app label. If relevant
145
+ public conversations look unlikely, recommend the specific Sales Nav or Prospeo
146
+ lane instead and say why. Do not call `search_signals`, `search_sales_nav`,
147
+ `search_prospeo`,
146
148
  `fetch_post_engagers`, or provider-scoped subagents until the user approves this
147
149
  source plan or explicitly chooses a different source.
148
150
 
@@ -161,10 +163,11 @@ precision, and referral paths, but it does not provide hiring-by-role filters;
161
163
  say that distinction plainly in the source-plan gate.
162
164
 
163
165
  After scouting, ask for a second approval on the concrete source action. For
164
- Signal Discovery, name how many selected posts will be scraped and the
165
- target engager/source-candidate volume. For Sales Nav or Prospeo, name the
166
- specific approved import lane and source lead count. Keep the internal
167
- 15-row campaign-table execution slice separate from source sampling.
166
+ LinkedIn engagement (`signal-discovery` internally), name how many selected
167
+ posts will be scraped and the target engager/source-candidate volume. For
168
+ Sales Nav or Prospeo, name the specific approved import lane and source lead
169
+ count. Keep the internal 15-row campaign-table execution slice separate from
170
+ source sampling.
168
171
 
169
172
  Do not call `import_leads` or `confirm_lead_list` until this second approval is
170
173
  granted.
@@ -181,26 +184,29 @@ granted.
181
184
  should `confirm_lead_list({ reviewBatchLimit: 15 })` copy confirmed rows into
182
185
  the campaign table and return the initial campaign-table execution slice rows.
183
186
 
184
- For Signal Discovery, the customer-facing approval card must use the exact
185
- action shape "Approve scraping N Signal Discovery posts?" and the chat summary
187
+ For LinkedIn engagement, the customer-facing approval card must use the exact
188
+ action shape "Approve scraping N selected LinkedIn posts?" and the chat summary
186
189
  should be a compact `## Source Recommendation` block with:
187
190
 
188
- - goal: about 300 good-fit prospects after cleanup, enrichment, and filters
189
- - source-candidate plan: about 1,500 raw engagers using a 20% working fit-rate
190
- assumption unless sampled data supports a different number
191
- - planning floor: continue with Signal Discovery only when sampled/projected
192
- fit is at least 10% after cleanup; below that, move to Sales Nav recent
193
- activity instead of scraping noisy engagers
191
+ - goal: about 300 headline-fit prospects from relevant LinkedIn engagement
192
+ - source-candidate plan: use sample math first: target headline-fit prospects
193
+ divided by sampled headline-pass rate. If there is no stronger sample, use
194
+ about 1,500 raw engagers from the 20% working headline-pass assumption.
195
+ - planning floor: continue with LinkedIn engagement only when sampled/projected
196
+ headline-fit rate is at least 10%; below that, move to Sales Nav recent
197
+ activity instead of scraping noisy engagers. Do not use the 10% floor as the
198
+ scrape-count denominator when the actual sample rate is higher.
194
199
  - campaign setup checkpoint: after the source list exists, copy confirmed source rows
195
200
  into the campaign and internally process the first execution slice for fit and message setup
196
201
  - a selected-post table with post author/topic, why it fits, and visible
197
202
  engagement
198
- - total visible pool and estimated good-fit pool
203
+ - total visible pool and estimated headline-fit pool
199
204
  - first pass: build the source list and copy all confirmed source rows into the
200
205
  campaign; the campaign table then internally processes its first execution
201
206
  slice before scaling
202
- - fallback: switch to Sales Nav recent activity if sampled/projected fit falls
203
- below 10%, or if the source sample is vendor-heavy, agency-heavy, or off-ICP
207
+ - fallback: switch to Sales Nav recent activity if sampled/projected
208
+ headline-fit rate falls below 10%, or if the source sample is vendor-heavy,
209
+ agency-heavy, or off-ICP
204
210
 
205
211
  When the user has not supplied a source and multiple source angles are viable,
206
212
  scout those angles as independent branches when the host can actually do it:
@@ -133,7 +133,7 @@ copy.
133
133
 
134
134
  Default source order when the user has not supplied a source:
135
135
 
136
- 1. LinkedIn post engagement / Signal Discovery
136
+ 1. LinkedIn post engagement (internal provider: `signal-discovery`)
137
137
  2. Sales Nav recent activity
138
138
  3. broader Sales Nav role/title filters
139
139
  4. Prospeo account/contact expansion
@@ -160,12 +160,14 @@ the question, and this first approval authorizes source scouting/search only:
160
160
  - that approval authorizes scouting/search only, not lead import or sending
161
161
 
162
162
  Do not surface blanket source heuristics as product copy. Make the recommendation
163
- specific to the campaign. If Signal Discovery is recommended, name the exact
164
- post themes you will search. If the campaign looks unlikely to have relevant
165
- public conversations, recommend the specific Sales Nav or Prospeo lane instead
166
- and say why. An approval like "Approve Prospeo plan" satisfies this gate; persist
167
- the provider and search next. Do not ask a second source-plan question. Do not
168
- call `search_signals`, `search_sales_nav`, `search_prospeo`,
163
+ specific to the campaign. If LinkedIn engagement is recommended, name the exact
164
+ post themes you will search and avoid using "Signal Discovery" in
165
+ customer-facing chat; that is the internal provider/app label. If the campaign
166
+ looks unlikely to have relevant public conversations, recommend the specific
167
+ Sales Nav or Prospeo lane instead and say why. An approval like "Approve Prospeo
168
+ plan" satisfies this gate; persist the provider and search next. Do not ask a
169
+ second source-plan question. Do not call `search_signals`, `search_sales_nav`,
170
+ `search_prospeo`,
169
171
  `fetch_post_engagers`, or provider-scoped subagents until the user approves this
170
172
  source plan or explicitly chooses a different source.
171
173
 
@@ -174,18 +176,19 @@ sequential by default. Run `source-scout-linkedin-engagement`,
174
176
  `source-scout-sales-nav`, and `source-scout-prospeo-contact` in parallel only
175
177
  when the user explicitly requested source comparison, a prior lane failed, or
176
178
  the first viable lane is borderline and a cheap fallback check is needed.
177
- Use a 10% planning floor after conservative cleanup. If Signal Discovery falls
178
- below that floor, move to Sales Nav. If Sales Nav falls below that floor after
179
- reasonable refinement, move to Prospeo. Prospeo is the terminal fallback; if it
180
- also falls below 10%, tighten the ICP/source direction instead of inventing
181
- another provider.
179
+ Use a 10% planning floor after conservative cleanup. If LinkedIn engagement
180
+ falls below that floor, move to Sales Nav. If Sales Nav falls below that floor
181
+ after reasonable refinement, move to Prospeo. Prospeo is the terminal fallback;
182
+ if it also falls below 10%, tighten the ICP/source direction instead of
183
+ inventing another provider.
182
184
 
183
185
  After scouting, show a second approval gate for the concrete source action.
184
- For Signal Discovery, state selected-post count, target engager/source-candidate
185
- volume, internal campaign-table execution-slice size, cleanup risk, and fallback;
186
- label the approval like "Approve scraping 3 Signal Discovery posts?" For Sales
187
- Nav or Prospeo, name the specific search/import lane and source lead count. Do
188
- not call `import_leads` or `confirm_lead_list` until this gate is approved.
186
+ For LinkedIn engagement (`signal-discovery` internally), state selected-post
187
+ count, target engager/source-candidate volume, internal campaign-table
188
+ execution-slice size, cleanup risk, and fallback; label the approval like
189
+ "Approve scraping 3 selected LinkedIn posts?" For Sales Nav or Prospeo, name the
190
+ specific search/import lane and source lead count. Do not call `import_leads` or
191
+ `confirm_lead_list` until this gate is approved.
189
192
 
190
193
  For Sales Nav and Prospeo, do not ask to import only the internal 15-row
191
194
  campaign-table execution slice at the source-action gate. First-page samples are
@@ -205,18 +208,18 @@ export count, not the campaign execution-slice size. For Signal Discovery, pass
205
208
  `confirmed: true`; the tool owns moving the watch UI to source-list progress
206
209
  after a lead-list/job id exists.
207
210
 
208
- For Signal Discovery, use this compact source-action approval shape after
211
+ For LinkedIn engagement, use this compact source-action approval shape after
209
212
  selected posts exist:
210
213
 
211
214
  ```markdown
212
215
  ## Source Recommendation
213
216
 
214
- Use Signal Discovery first.
217
+ Use LinkedIn engagement first.
215
218
 
216
- **Goal:** ~300 good-fit prospects after cleanup, enrichment, and filters<br>
217
- **Working assumption:** ~20% of raw post engagers become good-fit prospects<br>
219
+ **Goal:** ~300 headline-fit prospects from relevant LinkedIn engagement<br>
220
+ **Working assumption:** ~20% of raw post engagers pass headline filtering unless a real sample supports a different rate<br>
218
221
  **Engagers needed:** ~1,500 raw engagers<br>
219
- **Planning floor:** continue only when sampled/projected fit is at least 10% after cleanup; below that, switch to Sales Nav recent activity<br>
222
+ **Planning floor:** continue only when sampled/projected headline-fit rate is at least 10%; below that, switch to Sales Nav recent activity<br>
220
223
  **Campaign setup checkpoint:** after the source list exists, copy the entire confirmed source list into the campaign and internally process only the first execution slice for filter and message setup<br>
221
224
 
222
225
  ### Selected posts
@@ -239,12 +239,12 @@ source. That approval only authorizes scouting/search. Second, after the
239
239
  source evidence exists, show counts, samples, fit math, cleanup risk, and ask
240
240
  for source approval before import.
241
241
 
242
- For Signal Discovery, the second gate is not "approve source" in the abstract.
242
+ For LinkedIn engagement, the second gate is not "approve source" in the abstract.
243
243
  It is a concrete scrape approval: show a compact `## Source Recommendation`
244
- with the ~150 good-fit prospect goal, 15% raw-engager assumption, selected-post
245
- table, total visible pool, estimated good-fit pool, 10% planning floor,
244
+ with the ~300 headline-fit prospect goal, source sample assumption, selected-post
245
+ table, total visible pool, estimated headline-fit pool, 10% planning floor,
246
246
  first-pass campaign setup, and fallback. The approval question should be "Approve
247
- scraping N Signal Discovery posts?"
247
+ scraping N selected LinkedIn posts?"
248
248
 
249
249
  That scrape approval is single-use. Once the user approves it, do not replay
250
250
  the source card or ask for the same approval again. Acknowledge the approval in
@@ -254,13 +254,13 @@ one sentence and call `import_leads` for the approved source immediately.
254
254
 
255
255
  Source selection is sequential by default. Start with the first recommended
256
256
  source lane from `flow.v2.json`; for the current campaign shape that is usually
257
- Signal Discovery / LinkedIn engagement. Only try the next fallback after the
257
+ LinkedIn engagement. Only try the next fallback after the
258
258
  current lane is not viable, the user asks for a comparison, or the first lane is
259
259
  borderline and needs a second source to avoid a bad recommendation. The fallback
260
260
  order must stay plain: use Sales Nav to find ICP people who are actively posting
261
261
  on LinkedIn; if that is not enough, search Sales Nav by titles; if that is still
262
262
  not enough, use Prospeo for a broader account/contact path.
263
- Use a 10% planning floor after conservative cleanup. If Signal Discovery falls
263
+ Use a 10% planning floor after conservative cleanup. If LinkedIn engagement falls
264
264
  below that floor, move to Sales Nav. If Sales Nav falls below that floor after
265
265
  reasonable refinement, move to Prospeo. Prospeo is the terminal fallback; if it
266
266
  also falls below 10%, tighten the ICP/source direction instead of inventing
@@ -399,14 +399,14 @@
399
399
  "what approval authorizes"
400
400
  ],
401
401
  "choices": [
402
- "Approve Signal Discovery scouting plan",
402
+ "Approve LinkedIn engagement scouting plan",
403
403
  "Approve Sales Nav scouting plan",
404
404
  "Approve Prospeo scouting plan",
405
405
  "Choose different source",
406
406
  "Pause here"
407
407
  ],
408
408
  "approvalChoiceLabelsByProvider": {
409
- "signal-discovery": "Approve Signal Discovery scouting plan",
409
+ "signal-discovery": "Approve LinkedIn engagement scouting plan",
410
410
  "sales-nav": "Approve Sales Nav scouting plan",
411
411
  "prospeo": "Approve Prospeo scouting plan"
412
412
  },
@@ -508,13 +508,13 @@
508
508
  "action": "ask_source_review_choice",
509
509
  "uses": "request_user_input",
510
510
  "choices": [
511
- "Approve scraping N Signal Discovery posts",
511
+ "Approve scraping N selected LinkedIn posts",
512
512
  "Run the approved source import",
513
513
  "Revise source",
514
514
  "Pause here"
515
515
  ],
516
516
  "approvalChoiceLabelsByProvider": {
517
- "signal-discovery": "Approve scraping {selectedPostCount} Signal Discovery posts?",
517
+ "signal-discovery": "Approve scraping {selectedPostCount} selected LinkedIn posts?",
518
518
  "sales-nav": "Import the approved Sales Nav source list",
519
519
  "prospeo": "Import the approved Prospeo source list"
520
520
  },
@@ -152,9 +152,9 @@ Use this test:
152
152
  - Did the campaign rows show adjacent companies that the customer would likely
153
153
  reject only after seeing the list?
154
154
 
155
- If yes, add an explicit exclude rule. Include named competitors from the brief,
156
- campaign rows, or user context when available, plus the general category
157
- exclusion so new lookalike competitors are also blocked.
155
+ If yes, add an explicit exclude rule. Use the review batch as evidence. Include named competitors
156
+ from the brief, campaign rows, or user context when available, plus the general
157
+ category exclusion so new lookalike competitors are also blocked.
158
158
 
159
159
  ## Marketplace Safety
160
160
 
@@ -107,7 +107,7 @@ import_leads({
107
107
  campaignOfferId,
108
108
  targetLeadCount: <sourceCandidateTarget from approved good-fit math>,
109
109
  // Signal Discovery only:
110
- targetEngagerCount: <ceil(targetGoodFitLeads / sampledFitRateAfterCleanup)>,
110
+ targetEngagerCount: <ceil(targetHeadlineFitProspects / sampledHeadlinePassRate)>,
111
111
  maxPostsToScrape: <postsNeeded from approved math>
112
112
  })
113
113
  ```
@@ -124,14 +124,15 @@ escalation logic so dedup ratios are visible.
124
124
 
125
125
  For Signal Discovery, do not scrape every currently selected/promoted sample
126
126
  post by default. Before `import_leads`, reconcile selected posts with the
127
- approved source math. The default good-fit target is 300 and the planning
128
- fit-rate assumption is 20%, so the default source-candidate plan is about 1,500
129
- raw engagers. If the sampled fit rate is stronger or weaker, use the sampled
130
- rate with a conservative cleanup factor, cap the source candidates at the
131
- approved provider limit, and select only enough posts to reach that engager
132
- count. The planning floor is 10% projected fit after cleanup; if Signal
133
- Discovery falls below that floor during the pre-scrape sample, revise the source
134
- plan before importing. After an approved scrape starts, do not discard a nonempty
127
+ approved source math. The default headline-fit target is 300 and the planning
128
+ headline-pass assumption is 20%, so the default source-candidate plan is about
129
+ 1,500 raw engagers. If the sampled headline-fit rate is stronger or weaker, use
130
+ that sampled rate for `ceil(targetHeadlineFitProspects / sampledHeadlinePassRate)`,
131
+ cap the source candidates at the approved provider limit, and select only
132
+ enough posts to reach that engager count. The planning floor is 10% projected
133
+ headline-fit rate; if Signal Discovery falls below that floor during the
134
+ pre-scrape sample, revise the source plan before importing. After an approved
135
+ scrape starts, do not discard a nonempty
135
136
  completed source list just because it lands below the source-candidate target:
136
137
  confirm/copy the completed list for campaign setup, then add more posts
137
138
  or switch provider if the source quality or scale is not enough. The subsequent
@@ -236,14 +236,14 @@ campaignOfferId, confirmed: true })` -> `search_signals({ campaignOfferId,
236
236
  selectionMode: "replace", selections, headlineICPCriteria })` ->
237
237
  `import_leads({ campaignOfferId, provider: "signal-discovery",
238
238
  targetEngagerCount: 1500 })`.
239
- For Signal Discovery, the promotion/select step is load-bearing. Use
239
+ For LinkedIn engagement (`signal-discovery` internally), the promotion/select step is load-bearing. Use
240
240
  post IDs from the current campaign-scoped `search_signals` response or
241
241
  posts the user has visibly promoted in the campaign UI. Never use post IDs
242
242
  copied only from a source-scout summary unless they have been re-resolved
243
243
  through the current campaign search state. After `select_promising_posts`,
244
244
  require `selectedCount > 0` before calling `import_leads`. If it returns
245
245
  `selectedCount: 0`, do not switch providers and do not retry import.
246
- Explain that the campaign has no promoted Signal Discovery posts yet,
246
+ Explain that the campaign has no promoted LinkedIn posts yet,
247
247
  re-run a narrow campaign-scoped `search_signals` call to recover current
248
248
  post rows, or ask the user to promote the desired posts in the UI and then
249
249
  retry `import_leads`.
@@ -24,10 +24,12 @@ When the user asks to find posts or start searching, **IMMEDIATELY begin Round 1
24
24
 
25
25
  <lead_target>
26
26
 
27
- - **Default create-campaign target: ~300 good-fit prospects after cleanup,
28
- enrichment, and fit filtering.**
29
- - **Working assumption when no stronger sample exists: ~20% of raw post
30
- engagers become good-fit prospects, so plan around ~1,500 raw engagers.**
27
+ - **Default create-campaign target: ~300 headline-fit prospects that pass the
28
+ Signal Discovery headline criteria for now.**
29
+ - **Use sample math first: required raw engagers =
30
+ `ceil(targetHeadlineFitProspects / sampledHeadlinePassRate)`. When no
31
+ stronger sample exists, assume ~20% of raw post engagers pass headline
32
+ filtering, so plan around ~1,500 raw engagers.**
31
33
  - Provider/import caps are internal execution limits, not customer-facing
32
34
  promises. Do not tell the user the internal provider cap.
33
35
  - Quality > Quantity: a few hundred active users > 1000 potentially inactive profiles
@@ -112,23 +114,24 @@ You must estimate:
112
114
  - `passRate`
113
115
  - sampled pass rate used for extrapolation
114
116
  - `goodFitPer100Engagers`
115
- - normalized fit rate as good-fit prospects per 100 engagers, before and
116
- after a conservative dedupe/cleanup factor
117
+ - normalized headline-pass rate as headline-fit prospects per 100 engagers
117
118
  - `avgReachableEngagersPerPost`
118
119
  - average reachable engagers per right-content post used for capacity math
119
120
  - `goodFitProspectsPerPost`
120
- - expected good-fit prospects per right-content post after dedupe/cleanup
121
+ - expected headline-fit prospects per right-content post
121
122
  - `postsNeededForTarget`
122
123
  - number of right-content posts needed to reach the target good-fit lead
123
124
  count, defaulting to 300 for Signal Discovery unless the campaign says
124
125
  otherwise
125
126
  - `requiredEngagersToScrape`
126
- - `ceil(targetGoodFitLeads / sampledFitRate)`; for example the default
127
- planning assumption of 20 good-fit prospects per 100 engagers means a
128
- 300-good-fit source target needs about 1,500 raw engagers scraped
127
+ - `ceil(targetHeadlineFitProspects / sampledHeadlinePassRate)`; for example
128
+ a 35/60 sample is ~58%, so a 300 headline-fit target needs about 515 raw
129
+ engagers before adding a practical coverage buffer, not 3,000. The 10%
130
+ floor is a viability reject threshold, not the denominator when the sample
131
+ rate is higher.
129
132
  - `planningFloor`
130
- - minimum acceptable sampled/projected fit rate after conservative cleanup;
131
- default 10%. Below this, do not scale Signal Discovery.
133
+ - minimum acceptable sampled/projected headline-fit rate; default 10%.
134
+ Below this, do not scale Signal Discovery.
132
135
  - `projectedRange`
133
136
  - the conservative range implied by the observed sample
134
137
 
@@ -152,10 +155,10 @@ Use conservative logic:
152
155
  - stale posts
153
156
  - adjacent communities that are not actual buyers
154
157
  4. Explain the pass-through briefly and explicitly:
155
- - preferred: "`sampledCount`: 40, `passCount`: 9, `passRate`: ~22%, `goodFitPer100Engagers`: ~22 before cleanup / ~13-16 after cleanup, `requiredEngagersToScrape`: ~1,875-2,300 for a 300-good-fit target after cleanup, `avgReachableEngagersPerPost`: 240, `goodFitProspectsPerPost`: ~31-38, `postsNeededForTarget`: ~8-10, `recentStrongPostCount`: 15-25, `freshEnoughPostCount`: 8-12, `projectedRange`: 300-380 from selected posts"
158
+ - preferred: "`sampledCount`: 40, `passCount`: 9, `passRate`: ~22%, `goodFitPer100Engagers`: ~22 headline-fit prospects, `requiredEngagersToScrape`: ~1,365 raw engagers for a 300 headline-fit target before a practical coverage buffer, `avgReachableEngagersPerPost`: 240, `goodFitProspectsPerPost`: ~53, `postsNeededForTarget`: ~6, `recentStrongPostCount`: 15-25, `freshEnoughPostCount`: 8-12, `projectedRange`: 300-380 from selected posts"
156
159
  - fallback: "I could not fetch engagers, so this is inferred from post and author quality only"
157
160
  5. If the evidence is too weak, say so and return a low-confidence estimate instead of pretending precision.
158
- 6. If sampled/projected fit after cleanup is below the 10% planning floor,
161
+ 6. If sampled/projected headline-fit rate is below the 10% planning floor,
159
162
  reject the Signal Discovery scrape path and recommend Sales Nav recent
160
163
  activity as the next source.
161
164
  7. Do not manually enumerate dozens of engagers in the final answer. Summarize the sample by `sampledCount`, `passCount`, `passRate`, `projectedRange`, and 3-6 representative examples.
@@ -411,20 +414,21 @@ recommendation, estimate source capacity from real sample math:
411
414
  campaign says otherwise)
412
415
  - eligible right-content posts by lane/content type
413
416
  - reachable engagers from those posts
414
- - sampled ICP-fit rate as `n/N` plus an easy percentage/range
415
- - good-fit prospects per 100 engagers before and after conservative cleanup
416
- - required engagers to scrape for the source target
417
+ - sampled headline-fit rate as `n/N` plus an easy percentage/range
418
+ - headline-fit prospects per 100 engagers
419
+ - required engagers to scrape for the source target using the sampled
420
+ headline-fit rate
417
421
  - average reachable engagers per right-content post
418
- - expected good-fit leads per selected post after dedupe/cleanup
422
+ - expected headline-fit leads per selected post
419
423
  - posts needed to reach the target
420
- - whether sampled/projected fit clears the 10% planning floor
424
+ - whether sampled/projected headline-fit rate clears the 10% planning floor
421
425
 
422
426
  Then select the smallest right-content post set that plausibly hits the source
423
427
  target. Do not scrape every promoted sample post by default; promoted sampling
424
428
  state and final scrape plan are separate. If the math says the warm post lane
425
429
  only supports a smaller first batch, say that and name the Sales Nav or Prospeo
426
430
  scale fallback rather than padding the selection with noisy posts. If the
427
- sampled/projected fit rate is below 10% after cleanup, do not call
431
+ sampled/projected headline-fit rate is below 10%, do not call
428
432
  `import_leads`; move to Sales Nav recent activity.
429
433
 
430
434
  ```json