@sellable/mcp 0.1.144 → 0.1.146

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
+ ];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sellable/mcp",
3
- "version": "0.1.144",
3
+ "version": "0.1.146",
4
4
  "type": "module",
5
5
  "description": "Sellable MCP server for Claude Code and Codex campaign workflows",
6
6
  "main": "dist/index.js",
@@ -152,13 +152,14 @@ For Signal Discovery, the customer-facing approval card must use the exact
152
152
  action shape "Approve scraping N Signal Discovery posts?" and the chat summary
153
153
  should be a compact `## Source Recommendation` block with:
154
154
 
155
- - goal: about 300 good-fit prospects after cleanup, enrichment, and filters
156
- - working assumption: about 15% of raw post engagers become good-fit prospects
157
- - engagers needed: about 2,000 raw engagers
155
+ - good-fit target: about 150 prospects after cleanup, enrichment, and filters
156
+ - source-candidate plan: about 1,000 raw engagers using a conservative 15%
157
+ fit-rate assumption unless sampled data supports a different number
158
+ - review checkpoint: import the first 25 leads for fit and message review
158
159
  - a selected-post table with post author/topic, why it fits, and visible
159
160
  engagement
160
161
  - total visible pool and estimated good-fit pool
161
- - first pass: build the source list, then import only 15 leads into the review
162
+ - first pass: build the source list, then import only the review
162
163
  batch
163
164
  - fallback: switch to Sales Nav recent activity if the review batch is
164
165
  vendor-heavy, agency-heavy, or off-ICP
@@ -251,7 +252,7 @@ After every `update_campaign({ campaignId, currentStep })`, use
251
252
  `get_campaign_navigation_state` when available as a compact orientation check:
252
253
  match the saved campaign state to the expected watch-link step, explain the
253
254
  current state in one sentence, and only then continue. Sender selection belongs
254
- at Settings after message approval and 15-row validation. After message
255
+ at Settings after message approval and review-batch validation. After message
255
256
  validation, use Settings to help the user connect or select a LinkedIn sender.
256
257
  Explain Slack reply review before launch. After sender selection, attach the
257
258
  recommended sequence and move the watched UI to Send. Do not start the campaign
@@ -614,9 +615,9 @@ updates.
614
615
  until `hasMore=false`. The create-campaign message-review safety gate is a
615
616
  supplemental approval checklist, not a replacement for the long prompt. Use
616
617
  campaign state, campaign brief content, selected source state, and imported
617
- review-batch rows as the source of truth; do not read stale local markdown,
618
- inspect the database directly, or synthesize local validation artifacts from
619
- general knowledge.
618
+ review-batch rows as the source of truth; do not read stale local markdown
619
+ such as `message-validation.md`, inspect the database directly, or synthesize
620
+ local validation artifacts from general knowledge.
620
621
  5. Create the campaign shell early with the v1 brief so the user can open the
621
622
  watch link and see useful setup state immediately. Import only the first
622
623
  bounded review batch after the source is attached to the campaign; do not
@@ -640,7 +641,7 @@ updates.
640
641
  `mcp__sellable__update_campaign({ campaignId, currentStep })` before major
641
642
  visible work so the user can watch progress in the app: `create-offer` for
642
643
  the brief, `pick-provider` or the selected provider step while sourcing,
643
- `filter-choice` after the 15-row review batch, `create-icp-rubric` as soon
644
+ `filter-choice` after the review batch, `create-icp-rubric` as soon
644
645
  as filters are approved, `apply-icp-rubric` after rubrics save and while
645
646
  waiting for message-template approval, `validate-sample` while the approved
646
647
  template unlocks bounded enrichment/filter scoring on Filter Leads,
@@ -158,6 +158,12 @@ approval label must describe the action with the count, such as "Approve
158
158
  scraping 3 Signal Discovery posts?" For Sales Nav or Prospeo, the label should
159
159
  name the specific search/import lane. Do not call `import_leads` or
160
160
  `confirm_lead_list` until this second source-action approval is granted.
161
+ After the user approves this concrete source-action gate, do not show the
162
+ Source Recommendation again and do not ask another source approval question.
163
+ Acknowledge once, then call `import_leads` immediately with the approved source
164
+ math. For Signal Discovery, pass `provider: "signal-discovery"`,
165
+ `targetEngagerCount`, `maxPostsToScrape`, and `confirmed: true`; the tool owns
166
+ moving the watch UI to source-list progress after a lead-list/job id exists.
161
167
 
162
168
  For Signal Discovery, use this compact source-action approval shape after
163
169
  selected posts exist:
@@ -167,9 +173,9 @@ selected posts exist:
167
173
 
168
174
  Use Signal Discovery first.
169
175
 
170
- **Goal:** ~300 good-fit prospects after cleanup, enrichment, and filters<br>
171
- **Working assumption:** ~15% of raw post engagers become good-fit prospects<br>
172
- **Engagers needed:** ~2,000 raw engagers
176
+ **Good-fit target:** ~150 prospects after cleanup, enrichment, and filters<br>
177
+ **Source-candidate plan:** scrape ~1,000 raw engagers using a conservative 15% fit-rate assumption<br>
178
+ **Review checkpoint:** import the first 25 leads into the campaign for fit and message review before scaling<br>
173
179
 
174
180
  ### Selected posts
175
181
 
@@ -186,12 +192,12 @@ Use Signal Discovery first.
186
192
 
187
193
  Approve scraping these 3 posts.
188
194
 
189
- This gives enough volume to target ~300 good-fit prospects after cleanup, while
195
+ This gives enough volume to work toward ~150 good-fit prospects, while
190
196
  keeping the source tied to people already engaging with Claude Code outbound /
191
197
  AI-native sales workflows.
192
198
 
193
- **First pass:** build the source list, then import only 15 leads into the
194
- review batch so we can inspect quality before scaling.
199
+ **First pass:** build the source list, then import only the 25-lead review
200
+ batch so we can inspect fit and messages before scaling.
195
201
 
196
202
  **Fallback:** if the review batch is too vendor-heavy, agency-heavy, or
197
203
  off-ICP, switch to Sales Nav recent activity.
@@ -238,11 +238,15 @@ for source approval before import.
238
238
 
239
239
  For Signal Discovery, the second gate is not "approve source" in the abstract.
240
240
  It is a concrete scrape approval: show a compact `## Source Recommendation`
241
- with the ~300 good-fit prospect goal, 15% raw-engager assumption, selected-post
242
- table, total visible pool, estimated good-fit pool, first-pass 15-row review
241
+ with the ~150 good-fit prospect goal, 15% raw-engager assumption, selected-post
242
+ table, total visible pool, estimated good-fit pool, first-pass review
243
243
  batch, and fallback. The approval question should be "Approve scraping N Signal
244
244
  Discovery posts?"
245
245
 
246
+ That scrape approval is single-use. Once the user approves it, do not replay
247
+ the source card or ask for the same approval again. Acknowledge the approval in
248
+ one sentence and call `import_leads` for the approved source immediately.
249
+
246
250
  ## Parallelism + Naming
247
251
 
248
252
  Source selection is sequential by default. Start with the first recommended
@@ -33,16 +33,16 @@ the imported review batch for validation).
33
33
 
34
34
  - **`sampleSize`** — How many rows the validation loop pulls from the
35
35
  imported review batch. In v2 this matches the review-batch cap
36
- (default 15), so the user sees enough real rows to judge the campaign
36
+ (default 25), so the user sees enough real rows to judge the campaign
37
37
  without spending credits on hundreds more.
38
38
  - **`minProjectedPass`** — Passing-message floor required before handoff.
39
- The default requires 3 passing examples from the 15-row review batch.
39
+ The default requires 5 passing examples from the 25-row review batch.
40
40
  Math:
41
41
  ```text
42
42
  projectedPass = round(passInSample / sampleSize * importLimit)
43
43
  ```
44
- With defaults (sampleSize=15, importLimit=15), `projectedPass` equals
45
- the actual review-batch pass count. 3 passing rows is exactly at the
44
+ With defaults (sampleSize=25, importLimit=25), `projectedPass` equals
45
+ the actual review-batch pass count. 5 passing rows is exactly at the
46
46
  floor.
47
47
  - **`maxRevisionRounds`** — Hard cap before escalating to the user. A
48
48
  revision round is one full sample pass that triggered brief revision
@@ -17,7 +17,7 @@ import:
17
17
  # Initial review batch imported in Step 13 before the sample validation
18
18
  # loop. Import + enrichment must never exceed this cap before the user
19
19
  # reviews the sample and explicitly asks to expand.
20
- importLimit: 15
20
+ importLimit: 25
21
21
  # Provider is inherited from the campaign-attached source association created
22
22
  # during find-leads. Step 13 may re-run provider preflight in memory, but it
23
23
  # does not choose a new source.
@@ -27,11 +27,11 @@ sample:
27
27
  # Rows pulled from the imported cohort for the validation loop. In v2 this
28
28
  # equals the review-batch cap so the user sees a focused first test batch
29
29
  # without spending credits on hundreds of rows.
30
- sampleSize: 15
30
+ sampleSize: 25
31
31
  # Projected first-batch passing count required to proceed to sample
32
32
  # messaging. This is not approval to scale the full source list.
33
33
  # projectedPass = round(passInSample / sampleSize * importLimit).
34
- minProjectedPass: 3
34
+ minProjectedPass: 5
35
35
  # Hard cap on revision loops before escalating to the user. On a stale
36
36
  # resume the counter does NOT reset — the 3-round cap holds across
37
37
  # sessions.
@@ -106,7 +106,7 @@ handoff:
106
106
  # Orientation surfaced in Step 16 ("awaiting-user-greenlight") along with
107
107
  # the watch link. Keep this short and user-facing.
108
108
  orientation: >-
109
- Review the first 15 enriched leads and messages. If they look good,
109
+ Review the first 25 enriched leads and messages. If they look good,
110
110
  attach a sender in Settings, accept the recommended sequence, and start
111
111
  the campaign without spending credits on more leads first.
112
112
 
@@ -460,7 +460,7 @@
460
460
  "requiredInlineFields": [
461
461
  "primary source and exact filters/recipe",
462
462
  "specific source action awaiting approval",
463
- "for Signal Discovery: Source Recommendation markdown with goal ~300 good-fit prospects, ~15% raw-engager fit assumption, ~2,000 raw engagers needed, selected-post table, total visible pool, estimated good-fit pool, first pass, and fallback",
463
+ "for Signal Discovery: Source Recommendation markdown with ~150 good-fit prospects as the target, source-candidate plan ~1,000 raw engagers at a conservative ~15% fit assumption, review checkpoint size, selected-post table, total visible pool, estimated good-fit pool, first pass, and fallback",
464
464
  "for Signal Discovery: selected post count and target engager/source-candidate volume",
465
465
  "bounded review batch size",
466
466
  "runner-up and why it lost",
@@ -485,6 +485,16 @@
485
485
  "signal-discovery": "Approve scraping {selectedPostCount} Signal Discovery posts?",
486
486
  "sales-nav": "Import the approved Sales Nav review batch",
487
487
  "prospeo": "Import the approved Prospeo review batch"
488
+ },
489
+ "postApprovalContract": {
490
+ "singleUseApproval": true,
491
+ "doNotRepeatAfterApproval": [
492
+ "Source Recommendation",
493
+ "show_source_decision_card",
494
+ "ask_source_review_choice"
495
+ ],
496
+ "requiredNextActionAfterApproval": "acknowledge in one sentence, then call import_leads immediately",
497
+ "signalDiscoveryNextTool": "import_leads({ campaignOfferId, provider: \"signal-discovery\", targetEngagerCount, maxPostsToScrape, confirmed: true })"
488
498
  }
489
499
  }
490
500
  ],
@@ -541,7 +551,7 @@
541
551
  "importLimit"
542
552
  ],
543
553
  "requiredValues": {
544
- "importLimit": 15
554
+ "importLimit": 25
545
555
  },
546
556
  "modeAddHandshake": {
547
557
  "firstCallReturns": "needsModeSelection when adding to an existing campaign-attached list",
@@ -925,12 +935,24 @@
925
935
  "messageDraftRecommendation"
926
936
  ],
927
937
  "requiredVisibleLabels": [
928
- "Subject:",
929
- "Sample prospect fill:",
938
+ "## Message Template",
939
+ "## Rendered Example",
940
+ "Good token fill:",
930
941
  "My take:",
931
- "Concern:",
932
942
  "Question: approve-message or revise-messaging?",
933
943
  "Recommendation:"
944
+ ],
945
+ "doNotShowByDefault": [
946
+ "Token Notes",
947
+ "Good omit / fallback",
948
+ "Bad fill to avoid",
949
+ "Token Adherence Table"
950
+ ],
951
+ "internalPersistenceOnly": [
952
+ "Token Fill Rules",
953
+ "Token Fill Examples",
954
+ "fallback guidance",
955
+ "bad-fill avoidance notes"
934
956
  ]
935
957
  },
936
958
  {
@@ -15,7 +15,7 @@ packet.
15
15
 
16
16
  The approval gate used to be the place where the user authorized
17
17
  spend/send-adjacent mutation. It is now compatibility-only. New runs import the
18
- bounded 15-row review batch after source approval, save rubrics and the approved
18
+ bounded review batch after source approval, save rubrics and the approved
19
19
  message template after post-import review, and only then queue the cascade.
20
20
 
21
21
  If `campaign-shell.json` exists, a draft `CampaignOffer` row already exists but
@@ -174,6 +174,14 @@ If more than roughly 20% of the review batch appears to be the forbidden side,
174
174
  classify it as a list/source problem and return `revise-find-leads`; do not
175
175
  hide that problem behind narrow rubrics.
176
176
 
177
+ ## Suppression And Relationship Safety
178
+
179
+ Do not put DNC or one-off relationship-safety notes into production rubrics
180
+ unless the review batch shows that the pattern will leak at meaningful volume
181
+ and normal DNC/domain suppression will not catch it. Former employers, existing
182
+ customers, investors, partner lists, and one-off do-not-contact domains usually
183
+ belong in the recommendation as DNC/suppression instructions outside the rubric JSON.
184
+
177
185
  ## Production Rubric Shape
178
186
 
179
187
  Confirmed rubrics must use the production `LeadScoringRubric` fields exactly:
@@ -265,6 +273,40 @@ Interpretation:
265
273
  - Density dramatically below the band: likely source contamination, ICP drift,
266
274
  or provider-side weakness; investigate before launch.
267
275
 
276
+ ## Post-Filter Signal Enrichment Pass (Sales Nav lane)
277
+
278
+ Run this only for Sales Nav FIT rows when the downstream message step does not
279
+ already have row-level post evidence. In normal campaign-backed runs, use live
280
+ campaign-table row state as the source of truth. In explicit debug/UAT artifact
281
+ flows, append the diagnostic copy back to `lead-sample.json`.
282
+
283
+ This pass exists to protect the Earned-right and Read-as-1:1 substance filters.
284
+ Without row-level posts, message generation is likely to fall back to generic
285
+ category openers or presumed intent instead of evidence the recipient can
286
+ recognize.
287
+
288
+ Contract:
289
+
290
+ - scope: Sales Nav FIT rows only; do not enrich MAYBE or REJECT rows for this
291
+ pass
292
+ - budget cap: top 10 FIT rows by filter score
293
+ - tool: `mcp__sellable__fetch_linkedin_posts`, called per row
294
+ - fetch: most recent 3-5 public posts when available
295
+ - target surface: store each result as `recent_posts[]` entries with
296
+ `{ url, posted_at, excerpt }`; keep `recent_posts: []` for quiet profiles
297
+ - order of operations: run AFTER `lead-filter.md` is written, BEFORE
298
+ `message-validation.md` starts drafting
299
+
300
+ Record enrichment outcome in the filter summary: rows attempted, rows with
301
+ posts, rows with zero public posts, and tool failures. If more than half of the
302
+ attempted rows have zero posts, flag low signal density and tell message
303
+ generation to prefer trigger, peer-observation, or non-post proof instead of a
304
+ post-quote opener.
305
+
306
+ Do not convert "recently posted" or post availability into an ICP fit rubric.
307
+ The posts are message evidence only; buyer qualification still comes from role,
308
+ authority, account fit, capacity, geography, and exclusions.
309
+
268
310
  ## Decision Rules
269
311
 
270
312
  - Use `confirmed` when the source lane is viable and the rubrics are enough to
@@ -31,8 +31,11 @@ instead of pulling the long prompt into the main thread.
31
31
  local markdown/json artifacts in normal customer runs; emit debug artifacts
32
32
  only when the parent explicitly asks for debug/UAT output.
33
33
  5. Render the customer-facing message review in chat before asking for approval.
34
- After approval, the parent persists template and token rules into campaign
35
- state with `update_campaign_brief`.
34
+ Keep chat lightweight: show the tokenized template and one strong rendered
35
+ good-fill example only. Do not print the token notes table, omit/fallback
36
+ example, or bad-fill analysis in chat unless the user explicitly asks.
37
+ After approval, the parent persists template, token rules, fallback guidance,
38
+ and bad-fill avoidance notes into campaign state with `update_campaign_brief`.
36
39
  6. Ask exactly `approve-message` or `revise-messaging`. Do not import, queue,
37
40
  attach sequence, or start before `approve-message`.
38
41
 
@@ -89,10 +92,17 @@ but the [topic] thread felt close enough to send`. Otherwise omit the
89
92
  - If the message is plausible but not ready to send, set
90
93
  `Recommendation: revise-messaging`.
91
94
 
95
+ ## Internal Message Recommendation
96
+
97
+ The message scout or parent safety gate should still produce complete internal
98
+ review data: token-fill rules, one rendered omit/fallback example, and bad-fill
99
+ avoidance notes. This data is for campaign-brief persistence and safety checks,
100
+ not for the default chat approval packet.
101
+
92
102
  ## Customer-Facing Message Review
93
103
 
94
104
  Render this in chat before asking. Use Markdown structure so the approval target
95
- is visually scannable:
105
+ is visually scannable and low-overhead:
96
106
 
97
107
  ````markdown
98
108
  Status: message-review
@@ -111,33 +121,11 @@ Status: message-review
111
121
  {{tokenized_message_body}}
112
122
  ```
113
123
 
114
- ## Rendered Examples
115
-
116
- ### Good token fill
117
-
118
- Use when the row has a clean, supported fill.
119
-
120
- ```text
121
- Subject: ...
122
-
123
- Hey First,
124
-
125
- ...
126
- ```
127
-
128
- ### Good omit / fallback
129
-
130
- Use when a row signal or company context is missing, weak, or awkward.
131
-
132
- ```text
133
- Subject: ...
134
-
135
- Hey First,
124
+ ## Rendered Example
136
125
 
137
- ...
138
- ```
126
+ ### Example
139
127
 
140
- ### Bad fill to avoid
128
+ Good token fill:
141
129
 
142
130
  ```text
143
131
  Subject: ...
@@ -147,14 +135,6 @@ Hey First,
147
135
  ...
148
136
  ```
149
137
 
150
- **Why this is wrong:** one sentence naming the exact token-fill failure.
151
-
152
- ## Token Notes
153
-
154
- | Token | Use when | Fallback |
155
- | ---------------- | ------------------------ | -------- |
156
- | `{{first_name}}` | Clean first name exists. | `there` |
157
-
158
138
  ## Recommendation
159
139
 
160
140
  **My take:** ...
@@ -168,15 +148,13 @@ Hey First,
168
148
 
169
149
  Formatting requirements:
170
150
 
171
- - Put the tokenized template and every rendered example in fenced `text` blocks
151
+ - Put the tokenized template and rendered example in fenced `text` blocks
172
152
  so chat gives them a distinct background.
173
- - `Good token fill` and `Good omit / fallback` must each contain a complete
174
- rendered subject + body, not a bullet list of token names or a single
175
- bridge-line fragment.
176
- - Include `Bad fill to avoid` when the row sample has a tempting but unsafe
177
- source-mechanics or raw-headline fill; otherwise include `Bad fill to avoid:
178
- none found`.
179
- - Use a token notes table, not paragraph-only token notes.
153
+ - The chat example must contain a complete rendered subject + body, not a
154
+ bullet list of token names or a single bridge-line fragment.
155
+ - Do not show `Good omit / fallback`, `Bad fill to avoid`, or `Token Notes` in
156
+ the default chat approval packet. Keep those in the internal recommendation
157
+ and persist them to the campaign brief after approval.
180
158
  - Keep reasoning outside the code blocks so the blocks are easy to inspect and
181
159
  approve.
182
160
 
@@ -6,7 +6,7 @@ on every revision round.
6
6
 
7
7
  ## Principle
8
8
 
9
- We spend a bounded review batch (default 15 rows) to prove fit before the
9
+ We spend a bounded review batch (default 25 rows) to prove fit before the
10
10
  user spends credits on hundreds more leads. The sample loop has one job:
11
11
  answer the question "do we have enough real passing examples for the user to
12
12
  judge this campaign?" Message generation starts earlier: the first row that
@@ -23,7 +23,7 @@ auto-revise leads.
23
23
  - `CampaignOffer.currentStep === "apply-icp-rubric"` (the watched app is
24
24
  already on Filter Leads after `save_rubrics`; `validate-sample` is the
25
25
  logical loop name, not a visible route)
26
- - Imported review batch from Step 13 (size: `importLimit`, default 15)
26
+ - Imported review batch from Step 13 (size: `importLimit`, default 25)
27
27
  - Config from `auto-execute.yaml`: `sample.sampleSize`,
28
28
  `sample.minProjectedPass`, `sample.maxRevisionRounds`
29
29
  - Persisted counter `revisionRound` (starts at 0 on first entry; persists
@@ -56,7 +56,7 @@ auto-revise leads.
56
56
  6. wait_for_rubric_results(sample, targetCount = <cohortSize>, minPassedCount = 1)
57
57
  - cohortSize = stats.totalRows of the enrichment batch, or the
58
58
  imported batch count
59
- - default targetCount=15 matches the default review batch, but pass the
59
+ - default targetCount=25 matches the default review batch, but pass the
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)
@@ -172,8 +172,8 @@ company, enrichCellId, enrichStatus) over the default shape.
172
172
 
173
173
  ### 5. `wait_for_rubric_results` can timeout with enough signal to decide
174
174
 
175
- Observed: a 15-row review batch may return `ready=false`, `reason="timeout"`,
176
- and partial stats such as 13/15 scored, 2 passing, 2 messages generated. That is
175
+ Observed: a 25-row review batch may return `ready=false`, `reason="timeout"`,
176
+ and partial stats such as 20/25 scored, 4 passing, 4 messages generated. That is
177
177
  enough to diagnose an underperforming sample. Waiting again without active
178
178
  processing makes the experience feel frozen.
179
179
 
@@ -190,16 +190,16 @@ once only if active processing is still visible.
190
190
  projectedPass = round(passInSample / sampleSize * importLimit)
191
191
  ```
192
192
 
193
- Worked examples with defaults (sampleSize=15, importLimit=15):
193
+ Worked examples with defaults (sampleSize=25, importLimit=25):
194
194
 
195
- | passInSample | projectedPass | Handoff? (minProjectedPass=3) |
195
+ | passInSample | projectedPass | Handoff? (minProjectedPass=5) |
196
196
  | ------------ | ------------- | ----------------------------- |
197
197
  | 0 | 0 | No — escalate or revise |
198
- | 2 | 2 | No — escalate or revise |
199
- | 3 | 3 | Yes — exactly at floor |
200
- | 4 | 4 | Yes |
198
+ | 3 | 3 | No — escalate or revise |
199
+ | 5 | 5 | Yes — exactly at floor |
200
+ | 8 | 8 | Yes |
201
201
  | 10 | 10 | Yes |
202
- | 15 | 15 | Yes |
202
+ | 25 | 25 | Yes |
203
203
 
204
204
  When non-default importLimit/sampleSize are configured, the math scales
205
205
  the same way. In the default review-batch mode, the sample size equals the
@@ -263,8 +263,8 @@ rate is zero with no pattern that brief editing could fix.
263
263
 
264
264
  Signal examples:
265
265
 
266
- - 15/15 rows are wrong function / wrong seniority / wrong geo.
267
- - 12/15 rows enrich to empty strings for the tokens the brief needs.
266
+ - 25/25 rows are wrong function / wrong seniority / wrong geo.
267
+ - 20/25 rows enrich to empty strings for the tokens the brief needs.
268
268
  - Rubric passes 0 rows and the failure reasons are all "not ICP."
269
269
  - **Marketplace supply-side contamination:** >20% of sample rows match
270
270
  the brief §14 forbidden side (e.g. Skillsync sample contains
@@ -55,9 +55,9 @@ Supported branches:
55
55
  `import_leads({ campaignOfferId })` for Signal Discovery unless selected posts
56
56
  already exist on the campaign. Provider import materializes the source lead
57
57
  list from the approved source-capacity plan; it is not the same target as the
58
- 15-row campaign review batch.
58
+ default 25-row campaign review batch.
59
59
  - **Supplied LinkedIn profile CSV** — confirm `load_csv_linkedin_leads` only
60
- after rubrics and the approved message set are ready. Batch/materialize the
60
+ after the user approves that supplied-list source. Batch/materialize the
61
61
  uploaded CSV into a Sellable lead-list table. Persist the returned
62
62
  `leadListId`, then import the bounded review batch into the campaign table
63
63
  with
@@ -106,9 +106,9 @@ 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: <sourceTargetGoodFitLeads; default 300>,
109
+ targetLeadCount: <sourceCandidateTarget; provider default>,
110
110
  // Signal Discovery only:
111
- targetEngagerCount: <ceil(sourceTargetGoodFitLeads / sampledFitRateAfterCleanup)>,
111
+ targetEngagerCount: <ceil(targetGoodFitLeads / sampledFitRateAfterCleanup)>,
112
112
  maxPostsToScrape: <postsNeeded from approved math>
113
113
  })
114
114
  ```
@@ -120,9 +120,12 @@ so dedup ratios are visible.
120
120
 
121
121
  For Signal Discovery, do not scrape every currently selected/promoted sample
122
122
  post by default. Before `import_leads`, reconcile selected posts with the
123
- approved source math. If the math says 20 good fits per 100 engagers and the
124
- source target is 300 good-fit leads, pass `targetEngagerCount` around 1,500 and
125
- only enough posts to reach that engager count. The subsequent
123
+ approved source math. The default good-fit target is 150 and the conservative
124
+ fallback fit rate is 15%, so the default source-candidate plan is about 1,000
125
+ raw engagers. If the sampled fit rate is stronger or weaker, use the sampled
126
+ rate with a conservative cleanup factor, cap the source candidates at the
127
+ approved provider limit, and select only enough posts to reach that engager
128
+ count. The subsequent
126
129
  `confirm_lead_list` call still uses `targetLeadCount: <importLimit>` so only the
127
130
  bounded review batch enters the campaign table.
128
131
 
@@ -216,8 +219,10 @@ cannot scale further without a new source lane.
216
219
  - Zero-imported + zero-skipped ⇒ hard-fail escalate.
217
220
  - Zero-imported + high-skipped ⇒ lane-exhausted operator notice (NOT a
218
221
  silent proceed).
219
- - `import_leads` is NOT called before rubrics are saved and the approved message
220
- set is synced into the campaign brief.
222
+ - `import_leads` is allowed immediately after the concrete source-action
223
+ approval. It materializes the source list and then `confirm_lead_list` imports
224
+ only the bounded review batch. Enrichment, fit scoring, and message cells wait
225
+ for saved rubrics plus the approved message set.
221
226
  - Step 13 MUST reuse an approved source already attached with `campaignOfferId`
222
227
  before `import_leads`; it does not replay provider searches.
223
228
  - `import_leads` is NOT called again in Step 14 (validate-sample). Full
@@ -178,8 +178,8 @@ Source approved and import starting:
178
178
  {
179
179
  "stage": "review-batch",
180
180
  "headline": "Importing the review batch",
181
- "visibleState": "The browser is still showing the approved LinkedIn Engagement source leads.",
182
- "agentIntent": "Codex is importing only the bounded 15-row review batch into the campaign now.",
181
+ "visibleState": "The browser is still showing the approved LinkedIn Engagement source candidates.",
182
+ "agentIntent": "Codex is importing only the bounded review batch into the campaign now.",
183
183
  "nextAction": "Review batch ready"
184
184
  }
185
185
  ```