@sellable/mcp 0.1.77 → 0.1.79

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -243,6 +243,10 @@ Parallel execution contract:
243
243
  - Source scout names come from `agents/registry.json` and are exposed at runtime
244
244
  through `get_source_scout_registry`. Add new scouts there first; installer,
245
245
  Codex config, Claude agent files, and prompts should consume the registry.
246
+ - Post-find-leads scout names also come from `agents/registry.json` and are
247
+ exposed through `get_post_find_leads_scout_registry`. After source approval,
248
+ use that registry to launch the filter-leads scout and message-generation
249
+ scout together when the host supports real subagents.
246
250
  - Claude host: use the installed `source-scout-linkedin-engagement`,
247
251
  `source-scout-sales-nav`, and `source-scout-prospeo-contact` Task/Agent subagents
248
252
  for parallel lead-source scouting. Launch all credible lanes in one assistant
@@ -252,6 +256,10 @@ Parallel execution contract:
252
256
  `source-scout-prospeo-contact`. Use `multi_tool_use.parallel` for independent
253
257
  tool-call batches when subagents are unavailable or the work is only simple
254
258
  batched lookups.
259
+ - For the post-lead stage, Codex/Claude should launch
260
+ `post-find-leads-filter-scout` and `post-find-leads-message-scout` in the
261
+ same turn/message when subagents are available. If not, run them sequentially
262
+ and do not claim parallel work.
255
263
  - If neither backend is available, run sequentially with the same output schema.
256
264
 
257
265
  Config path resolution (in order):
@@ -0,0 +1,67 @@
1
+ You are the Post-Lead Filter Scout for Sellable create-campaign-v2.
2
+
3
+ Your job starts only after find-leads has produced `lead-review.md` and
4
+ `lead-sample.json`, and the lead source has been approved or auto-confirmed.
5
+ Work only on the lead filter branch. Do not source new leads, draft messages,
6
+ import leads, create campaigns, ask the user questions, or mutate live campaign
7
+ state.
8
+
9
+ Required inputs:
10
+
11
+ - `brief.md`
12
+ - `lead-review.md`
13
+ - `lead-sample.json`
14
+
15
+ Required first steps:
16
+
17
+ 1. Read the three required inputs.
18
+ 2. Load the filter-leads reference before writing artifacts:
19
+ `get_subskill_asset({ subskillName: "create-campaign-v2", assetPath: "references/filter-leads.md" })`.
20
+
21
+ Owned outputs:
22
+
23
+ - `lead-filter.md`
24
+ - `rubric.json` when the filter is confirmed and production-shaped rubrics are
25
+ safe to write
26
+
27
+ Do not write or modify:
28
+
29
+ - `message-validation.md`
30
+ - `message-review.md`
31
+ - `approval-packet.md`
32
+ - `brief.md`
33
+ - `lead-review.md`
34
+ - `lead-sample.json`
35
+
36
+ Process:
37
+
38
+ 1. Preserve the approved source decision and the sample math from
39
+ `lead-review.md`; do not re-run sourcing.
40
+ 2. Turn the sample's good-fit and false-positive patterns into a strict but
41
+ campaign-native filter.
42
+ 3. Include keep rules, exclude rules, sample false positives, pass-rate /
43
+ expected-yield impact, and a recommendation.
44
+ 4. Add an explicit ability-to-pay or economic-capacity gate unless the brief
45
+ clearly says the offer is free or has no meaningful budget requirement.
46
+ 5. Keep source mechanics out of production rubrics. Engagement, provider,
47
+ priority, or first-send ordering can inform prioritization, but they are not
48
+ standalone ICP qualification rules.
49
+ 6. Write `lead-filter.md` first. If status is `confirmed`, also write
50
+ `rubric.json` with 2-5 production-shaped `leadScoringRubrics`.
51
+
52
+ Return a concise final status with:
53
+
54
+ - filter status: `confirmed`, `confirm-with-user`, or `revise-find-leads`
55
+ - artifacts written
56
+ - strongest keep rules
57
+ - strongest exclusion rules
58
+ - expected pass-rate / yield impact
59
+ - any blocker that prevents message review from joining
60
+
61
+ Quality bar:
62
+
63
+ - Every passing lead should be someone the user would be glad to hear back
64
+ from.
65
+ - Do not loosen the filter just to preserve volume.
66
+ - Do not make the filter so narrow that it contradicts the approved source
67
+ unless the sample evidence clearly requires it.
@@ -0,0 +1,67 @@
1
+ You are the Post-Lead Message Scout for Sellable create-campaign-v2.
2
+
3
+ Your job starts only after find-leads has produced `lead-review.md` and
4
+ `lead-sample.json`, and the lead source has been approved or auto-confirmed.
5
+ Work only on the message generation branch. Do not source new leads, create lead
6
+ filters, import leads, create campaigns, ask the user questions, or mutate live
7
+ campaign state.
8
+
9
+ Required inputs:
10
+
11
+ - `brief.md`
12
+ - `lead-review.md`
13
+ - `lead-sample.json`
14
+
15
+ Required first steps:
16
+
17
+ 1. Read the three required inputs.
18
+ 2. Load 100% of the real generate-messages prompt with chunked
19
+ `get_subskill_prompt({ subskillName: "generate-messages", offset, limit })`
20
+ calls until `hasMore` is false.
21
+
22
+ Owned outputs:
23
+
24
+ - `message-validation.md`
25
+ - optional `message-prep.md`
26
+ - optional `message-candidate-drafts.md`
27
+
28
+ Do not write or modify:
29
+
30
+ - `lead-filter.md`
31
+ - `rubric.json`
32
+ - `message-review.md`
33
+ - `approval-packet.md`
34
+ - `brief.md`
35
+ - `lead-review.md`
36
+ - `lead-sample.json`
37
+
38
+ Process:
39
+
40
+ 1. Run the loaded generate-messages workflow in dry mode from the approved
41
+ brief, lead-review source decision, and `lead-sample.json`.
42
+ 2. Use `lead-sample.json` as the only lead sample source. Do not fetch new
43
+ prospects or invent richer row signals.
44
+ 3. Build proof inventory, token fill rules, token adherence, angle drafts,
45
+ kill/combine review, finalists, skeptical-prospect review, winner gate, and
46
+ a raw sendable selected winner.
47
+ 4. If `lead-filter.md` already exists, cite only basis rows that pass it. If it
48
+ does not exist yet, choose probable good-fit rows from `lead-sample.json` and
49
+ mark the final reconciliation as pending.
50
+ 5. Write `message-validation.md`. Do not write `message-review.md`; the parent
51
+ thread owns the joined review after both post-lead scouts finish.
52
+
53
+ Return a concise final status with:
54
+
55
+ - artifacts written
56
+ - whether the full generate-messages prompt was loaded
57
+ - lead sample basis used
58
+ - selected winner summary
59
+ - whether final reconciliation with `lead-filter.md` is complete or pending
60
+
61
+ Quality bar:
62
+
63
+ - Do not synthesize a lightweight message from general knowledge. The artifact
64
+ must prove the full generate-messages workflow ran.
65
+ - Message generation can start before `lead-filter.md`, but message review
66
+ cannot start until the parent verifies the selected basis rows still pass the
67
+ final filter.
@@ -4,6 +4,7 @@
4
4
  {
5
5
  "id": "linkedin-engagement",
6
6
  "name": "source-scout-linkedin-engagement",
7
+ "kind": "source-scout",
7
8
  "promptFile": "source-scout-linkedin-engagement.md",
8
9
  "displayName": "LinkedIn Engagement Scout",
9
10
  "provider": "signal-discovery",
@@ -52,6 +53,7 @@
52
53
  {
53
54
  "id": "sales-nav",
54
55
  "name": "source-scout-sales-nav",
56
+ "kind": "source-scout",
55
57
  "promptFile": "source-scout-sales-nav.md",
56
58
  "displayName": "Sales Nav Scout",
57
59
  "provider": "sales-nav",
@@ -100,6 +102,7 @@
100
102
  {
101
103
  "id": "prospeo-contact",
102
104
  "name": "source-scout-prospeo-contact",
105
+ "kind": "source-scout",
103
106
  "promptFile": "source-scout-prospeo-contact.md",
104
107
  "displayName": "Prospeo Contact Scout",
105
108
  "provider": "prospeo",
@@ -145,6 +148,87 @@
145
148
  "mcp__sellable__search_prospeo"
146
149
  ]
147
150
  }
151
+ },
152
+ {
153
+ "id": "filter-leads",
154
+ "name": "post-find-leads-filter-scout",
155
+ "kind": "post-find-leads-scout",
156
+ "promptFile": "post-find-leads-filter-scout.md",
157
+ "displayName": "Post-Lead Filter Scout",
158
+ "target": "filter-leads",
159
+ "inputs": ["brief.md", "lead-review.md", "lead-sample.json"],
160
+ "producesArtifacts": ["lead-filter.md"],
161
+ "optionalProducesArtifacts": ["rubric.json"],
162
+ "ownership": "lead quality, false-positive patterns, keep/exclude rules, ability-to-pay checks, and production rubric translation only",
163
+ "codex": {
164
+ "description": "Sellable post-find-leads scout for lead filtering and rubric generation after source approval.",
165
+ "model": "gpt-5.5",
166
+ "modelReasoningEffort": "high",
167
+ "sandboxMode": "workspace-write",
168
+ "nicknameCandidates": [
169
+ "Lead Filter Scout",
170
+ "Filter Scout",
171
+ "Rubric Scout"
172
+ ]
173
+ },
174
+ "claude": {
175
+ "description": "Use proactively as a background Sellable post-find-leads scout after lead source approval to produce lead-filter.md and rubric.json from brief.md, lead-review.md, and lead-sample.json.",
176
+ "model": "inherit",
177
+ "background": true,
178
+ "maxTurns": 8,
179
+ "color": "yellow",
180
+ "tools": [
181
+ "Read",
182
+ "Write",
183
+ "Edit",
184
+ "Grep",
185
+ "Glob",
186
+ "mcp__sellable__get_subskill_prompt",
187
+ "mcp__sellable__get_subskill_asset"
188
+ ]
189
+ }
190
+ },
191
+ {
192
+ "id": "message-generation",
193
+ "name": "post-find-leads-message-scout",
194
+ "kind": "post-find-leads-scout",
195
+ "promptFile": "post-find-leads-message-scout.md",
196
+ "displayName": "Post-Lead Message Scout",
197
+ "target": "generate-messages",
198
+ "inputs": ["brief.md", "lead-review.md", "lead-sample.json"],
199
+ "producesArtifacts": ["message-validation.md"],
200
+ "optionalProducesArtifacts": [
201
+ "message-prep.md",
202
+ "message-candidate-drafts.md"
203
+ ],
204
+ "ownership": "proof inventory, token strategy, angle drafting, skeptical-prospect review, and selected winner only",
205
+ "codex": {
206
+ "description": "Sellable post-find-leads scout for dry-mode message generation after source approval.",
207
+ "model": "gpt-5.5",
208
+ "modelReasoningEffort": "high",
209
+ "sandboxMode": "workspace-write",
210
+ "nicknameCandidates": [
211
+ "Message Scout",
212
+ "Message Generation Scout",
213
+ "Copy Scout"
214
+ ]
215
+ },
216
+ "claude": {
217
+ "description": "Use proactively as a background Sellable post-find-leads scout after lead source approval to run dry-mode message generation from brief.md, lead-review.md, and lead-sample.json.",
218
+ "model": "inherit",
219
+ "background": true,
220
+ "maxTurns": 10,
221
+ "color": "magenta",
222
+ "tools": [
223
+ "Read",
224
+ "Write",
225
+ "Edit",
226
+ "Grep",
227
+ "Glob",
228
+ "mcp__sellable__get_subskill_prompt",
229
+ "mcp__sellable__get_subskill_asset"
230
+ ]
231
+ }
148
232
  }
149
233
  ]
150
234
  }
package/dist/index-dev.js CHANGED
File without changes
package/dist/index.js CHANGED
File without changes
package/dist/server.js CHANGED
@@ -21,7 +21,7 @@ import { fetchCompany, fetchCompanyPosts, fetchLinkedInPosts, fetchLinkedInProfi
21
21
  import { getCampaignNavigationState, navigationToolDefinitions, } from "./tools/navigation.js";
22
22
  import { addOnDemandLeads, createOnDemandCampaign, createOnDemandTable, initOnDemandSequence, onDemandToolDefinitions, pauseOnDemandCampaign, startOnDemandCampaign, } from "./tools/one-off.js";
23
23
  import { processingToolDefinitions, upsertRubric } from "./tools/processing.js";
24
- import { completeSenderResearch, getMessagePrompt, getSourceScoutRegistry, getSubskillAsset, getSubskillPrompt, listSubskillPrompts, promptToolDefinitions, searchSubskillPrompts, } from "./tools/prompts.js";
24
+ import { completeSenderResearch, getMessagePrompt, getPostFindLeadsScoutRegistry, getSourceScoutRegistry, getSubskillAsset, getSubskillPrompt, listSubskillPrompts, promptToolDefinitions, searchSubskillPrompts, } from "./tools/prompts.js";
25
25
  import { readinessToolDefinitions, waitForCampaignTableReady, waitForLeadListReady, } from "./tools/readiness.js";
26
26
  import { getRows, getTableRows, getTableRowsMinimal, rowToolDefinitions, } from "./tools/rows.js";
27
27
  import { addRubricItem, checkRubric, deleteRubricItem, draftRubrics, rubricToolDefinitions, saveRubrics, selectNecessaryRubrics, updateRubricItem, waitForRubricResults, } from "./tools/rubrics.js";
@@ -411,6 +411,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
411
411
  case "get_source_scout_registry":
412
412
  result = getSourceScoutRegistry();
413
413
  break;
414
+ case "get_post_find_leads_scout_registry":
415
+ result = getPostFindLeadsScoutRegistry();
416
+ break;
414
417
  case "complete_sender_research":
415
418
  result = completeSenderResearch(args);
416
419
  break;
@@ -1,6 +1,6 @@
1
1
  import { getApi } from "../api.js";
2
2
  import { getConfig } from "../auth.js";
3
- import { assertCreateCampaignPromptLoaded } from "./flow-preflight.js";
3
+ import { assertCreateCampaignPromptLoaded, assertNetNewCreateCampaignResearchReady, } from "./flow-preflight.js";
4
4
  import { setCampaignInteractionMode, } from "./interaction-mode.js";
5
5
  import { fetchCampaignRubrics } from "./processing.js";
6
6
  const LEAD_SOURCE_PROVIDERS = {
@@ -618,9 +618,10 @@ export async function createCampaign(input) {
618
618
  `or all create fields. Missing: ${missing.join(", ")}.\n\n` +
619
619
  "Remediation:\n" +
620
620
  '- For full workflow, call get_subskill_prompt({ subskillName: "create-campaign" }) and follow it.\n' +
621
- "- For net-new campaign creation: pass either `clientProspectId` (preferred) or `senderLinkedinUrl` and let the backend resolve sender context at mint time.\n" +
621
+ "- For net-new campaign creation: run the `research-sender` flow first, call `complete_sender_research`, then pass either `clientProspectId` (preferred) or `senderLinkedinUrl` and let the backend resolve sender context at mint time.\n" +
622
622
  "- For resume, call create_campaign with campaignId only.");
623
623
  }
624
+ assertNetNewCreateCampaignResearchReady();
624
625
  const name = input.name;
625
626
  const clientProspectId = hasClientProspectId
626
627
  ? input.clientProspectId?.trim()
@@ -96,6 +96,43 @@ export interface SourceScoutRegistryResponse {
96
96
  parentThreadRule: string;
97
97
  };
98
98
  }
99
+ export interface PostFindLeadsScoutRegistryResponse {
100
+ version: number;
101
+ trigger: "find_leads_source_approved";
102
+ requiredArtifacts: string[];
103
+ scouts: Array<{
104
+ id: string;
105
+ name: string;
106
+ displayName: string;
107
+ target: string;
108
+ inputs: string[];
109
+ producesArtifacts: string[];
110
+ optionalProducesArtifacts: string[];
111
+ ownership: string;
112
+ codex: {
113
+ filename: string;
114
+ description: string;
115
+ model: string;
116
+ modelReasoningEffort: string;
117
+ };
118
+ claude: {
119
+ filename: string;
120
+ description: string;
121
+ tools: string[];
122
+ };
123
+ }>;
124
+ joinGate: {
125
+ afterAllComplete: boolean;
126
+ requiredArtifacts: string[];
127
+ show: string[];
128
+ nextStage: string;
129
+ };
130
+ usage: {
131
+ codex: string;
132
+ claude: string;
133
+ parentThreadRule: string;
134
+ };
135
+ }
99
136
  export declare const DEFAULT_SUBSKILL_PROMPT_CHUNK_CHARS = 48000;
100
137
  export declare const MAX_SUBSKILL_PROMPT_CHUNK_CHARS = 48000;
101
138
  export declare const promptToolDefinitions: ({
@@ -323,6 +360,7 @@ export declare const promptToolDefinitions: ({
323
360
  _meta?: undefined;
324
361
  })[];
325
362
  export declare function getSourceScoutRegistry(): SourceScoutRegistryResponse;
363
+ export declare function getPostFindLeadsScoutRegistry(): PostFindLeadsScoutRegistryResponse;
326
364
  export declare function getMessagePrompt(): Promise<PromptResponse>;
327
365
  export declare function listSubskillPrompts(limit?: number, includePublic?: boolean, includeInternal?: boolean): ListSubskillPromptsResponse;
328
366
  export declare function getSubskillPrompt(subskillName: string, offset?: number, limit?: number): SubskillPromptResponse;
@@ -140,6 +140,16 @@ export const promptToolDefinitions = [
140
140
  additionalProperties: false,
141
141
  },
142
142
  },
143
+ {
144
+ name: "get_post_find_leads_scout_registry",
145
+ description: "Return the canonical Sellable post-find-leads scout names for the two parallel workstreams unlocked after lead source approval: filter-leads and message generation.",
146
+ inputSchema: {
147
+ type: "object",
148
+ properties: {},
149
+ required: [],
150
+ additionalProperties: false,
151
+ },
152
+ },
143
153
  {
144
154
  name: "complete_sender_research",
145
155
  description: "Mark sender research as completed for create-campaign preflight. Call this after research-sender synthesis and before create_campaign.",
@@ -196,12 +206,22 @@ function resolveAgentsDir() {
196
206
  }
197
207
  return candidates[0] ?? path.resolve(process.cwd(), "mcp/sellable/agents");
198
208
  }
199
- export function getSourceScoutRegistry() {
209
+ function readAgentRegistry() {
200
210
  const registryPath = path.join(resolveAgentsDir(), "registry.json");
201
211
  const registry = JSON.parse(fs.readFileSync(registryPath, "utf8"));
202
- const agents = Array.isArray(registry.agents) ? registry.agents : [];
203
212
  return {
204
213
  version: Number(registry.version || 1),
214
+ agents: Array.isArray(registry.agents) ? registry.agents : [],
215
+ };
216
+ }
217
+ export function getSourceScoutRegistry() {
218
+ const registry = readAgentRegistry();
219
+ const agents = registry.agents.filter((agent) => {
220
+ const kind = String(agent.kind || "source-scout");
221
+ return kind === "source-scout";
222
+ });
223
+ return {
224
+ version: registry.version,
205
225
  agents: agents.map((agent) => ({
206
226
  id: String(agent.id || ""),
207
227
  name: String(agent.name || ""),
@@ -228,6 +248,51 @@ export function getSourceScoutRegistry() {
228
248
  },
229
249
  };
230
250
  }
251
+ export function getPostFindLeadsScoutRegistry() {
252
+ const registry = readAgentRegistry();
253
+ const scouts = registry.agents.filter((agent) => String(agent.kind || "") === "post-find-leads-scout");
254
+ return {
255
+ version: registry.version,
256
+ trigger: "find_leads_source_approved",
257
+ requiredArtifacts: ["brief.md", "lead-review.md", "lead-sample.json"],
258
+ scouts: scouts.map((agent) => ({
259
+ id: String(agent.id || ""),
260
+ name: String(agent.name || ""),
261
+ displayName: String(agent.displayName || agent.name || ""),
262
+ target: String(agent.target || agent.id || ""),
263
+ inputs: Array.isArray(agent.inputs) ? agent.inputs.map(String) : [],
264
+ producesArtifacts: Array.isArray(agent.producesArtifacts)
265
+ ? agent.producesArtifacts.map(String)
266
+ : [],
267
+ optionalProducesArtifacts: Array.isArray(agent.optionalProducesArtifacts)
268
+ ? agent.optionalProducesArtifacts.map(String)
269
+ : [],
270
+ ownership: String(agent.ownership || ""),
271
+ codex: {
272
+ filename: String(agent.codex?.filename || `${agent.name}.toml`),
273
+ description: String(agent.codex?.description || ""),
274
+ model: String(agent.codex?.model || "gpt-5.5"),
275
+ modelReasoningEffort: String(agent.codex?.modelReasoningEffort || "high"),
276
+ },
277
+ claude: {
278
+ filename: String(agent.claude?.filename || `${agent.name}.md`),
279
+ description: String(agent.claude?.description || ""),
280
+ tools: Array.isArray(agent.claude?.tools) ? agent.claude.tools : [],
281
+ },
282
+ })),
283
+ joinGate: {
284
+ afterAllComplete: true,
285
+ requiredArtifacts: ["lead-filter.md", "message-validation.md"],
286
+ show: ["lead_filter_summary", "sample_message"],
287
+ nextStage: "message-review",
288
+ },
289
+ usage: {
290
+ codex: "After the user approves or auto-confirms the lead source, spawn both returned scout `name` values in one assistant turn when subagents are available.",
291
+ claude: "After lead source approval, invoke both returned Task/Agent subagents in one assistant message so filter-leads and message generation run concurrently.",
292
+ parentThreadRule: "Do not run filter-leads to completion before starting message generation. Both scouts consume brief.md, lead-review.md, and lead-sample.json; join before message review.",
293
+ },
294
+ };
295
+ }
231
296
  export async function getMessagePrompt() {
232
297
  const api = getApi();
233
298
  return api.get("/api/v3/prompts/edit-message");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sellable/mcp",
3
- "version": "0.1.77",
3
+ "version": "0.1.79",
4
4
  "type": "module",
5
5
  "description": "Sellable MCP server for Claude Code and Codex campaign workflows",
6
6
  "main": "dist/index.js",
@@ -12,6 +12,7 @@ allowed-tools:
12
12
  - mcp__sellable__search_subskill_prompts
13
13
  - mcp__sellable__get_provider_prompt
14
14
  - mcp__sellable__get_source_scout_registry
15
+ - mcp__sellable__get_post_find_leads_scout_registry
15
16
  - mcp__sellable__get_message_prompt
16
17
  - mcp__sellable__get_active_workspace
17
18
  - mcp__sellable__list_senders
@@ -114,6 +115,13 @@ copy, is the runtime source of truth. If the host runs them sequentially, do not
114
115
  claim they ran in parallel. In chat, call the downstream copy stage `message generation`;
115
116
  `message-validation.md` is only an internal proof artifact.
116
117
 
118
+ After find-leads returns a lead source and the user approves it, use the same
119
+ registry pattern for the two post-lead branches. The create-campaign-v2 subskill
120
+ calls `get_post_find_leads_scout_registry`, then launches the returned
121
+ filter-leads scout and message-generation scout together when real subagents are
122
+ available. Message generation must not wait for the filter unless the host
123
+ cannot run the two branches concurrently.
124
+
117
125
  Use rendered Markdown for user review surfaces, not fenced code blocks. Keep
118
126
  lines short, use indexed section labels and bullets, and translate internal
119
127
  sourcing terms into plain language.
@@ -190,14 +198,22 @@ Treat host capabilities as concrete functions, not prose conventions:
190
198
  and continue chunks until `hasMore` is false.
191
199
  - `load_source_scout_registry`: call
192
200
  `mcp__sellable__get_source_scout_registry({})` before any scout dispatch.
201
+ - `load_post_find_leads_scout_registry`: call
202
+ `mcp__sellable__get_post_find_leads_scout_registry({})` after source
203
+ approval and before dispatching the post-lead filter/message scouts.
193
204
  - `launch_source_scout`: Claude Code uses `Task` with `subagent_type` equal to
194
205
  the registry `name`; Codex uses named custom agents such as
195
206
  `source-scout-linkedin-engagement`, `source-scout-sales-nav`, and
196
207
  `source-scout-prospeo-contact` when subagents are available.
208
+ - `launch_post_find_leads_scout`: Claude Code uses `Task` with `subagent_type`
209
+ equal to the returned post-lead registry `name`; Codex uses the returned
210
+ custom agents such as `post-find-leads-filter-scout` and
211
+ `post-find-leads-message-scout` when subagents are available.
197
212
 
198
213
  If a required interactive host function is missing, stop and explain the
199
214
  Sellable install/reload problem. Do not silently simulate structured choices,
200
- subprompt loading, or source-scout dispatch with local scripts.
215
+ subprompt loading, source-scout dispatch, or post-lead scout dispatch with local
216
+ scripts.
201
217
 
202
218
  Never narrate local draft housekeeping to the user. If you create directories,
203
219
  save drafts, write artifacts, or persist intermediate state, translate it into
@@ -460,10 +460,12 @@ should test for this campaign. Those can run in parallel and usually take
460
460
  `post-lead-workstreams` step. Launch `filter leads` and `message generation`
461
461
  from the same basis (`brief.md`, `lead-review.md`, `lead-sample.json`) as
462
462
  separate workstreams when the host supports real subagents/background work.
463
- In Claude Code, use two Task/Agent subagents in the same assistant message;
464
- in Codex, use disjoint worker subagents when the host exposes them. The
465
- existing `filter-rubric` and `message-generation` steps remain focused retry
466
- and resume targets.
463
+ First call `get_post_find_leads_scout_registry` and use the returned
464
+ canonical `name` values. In Claude Code, use both returned Task/Agent
465
+ subagents in the same assistant message; in Codex, use both returned custom
466
+ scouts as disjoint subagents in the same assistant turn when the host exposes
467
+ them. The existing `filter-rubric` and `message-generation` steps remain
468
+ focused retry and resume targets.
467
469
  - Message generation does not need `lead-filter.md` to start. The moment
468
470
  `lead-sample.json` exists with at least 5 probable good-fit rows and the lead
469
471
  source is confirmed or auto-confirmed, start the message-generation
@@ -499,6 +501,13 @@ should test for this campaign. Those can run in parallel and usually take
499
501
  then draft the message from the same sample.` Never say `kicking off two
500
502
  workstreams`, `in parallel`, or `background` unless parallel branches were
501
503
  actually launched.
504
+ - For post-lead workstreams, first call
505
+ `get_post_find_leads_scout_registry` and launch exactly the returned
506
+ `filter-leads` and `message-generation` scouts when real subagents are
507
+ available. This is the same registry pattern as source scouting, but the
508
+ trigger is source approval and the join gate is both `lead-filter.md` and
509
+ `message-validation.md` existing from the same `brief.md`, `lead-review.md`,
510
+ and `lead-sample.json`.
502
511
  - Never run a downstream stage until the active `flow.v2.json` step's
503
512
  `requiredArtifacts` exist.
504
513
  - Never call a tool outside the active step's `allowedTools`, and never call a
@@ -926,6 +935,9 @@ Required behavior:
926
935
  economic capacity, competitor/vendor exclusion, and active current-role status
927
936
  as table-stakes buyer-quality gates; include geography/market whenever budget,
928
937
  channel adoption, language, compliance, or delivery assumptions depend on it
938
+ - customize the concrete thresholds for those gates to the client's offer,
939
+ market, price point, delivery model, and competitor landscape; the invariant
940
+ is that every passing lead should be worth the user's time if they reply
929
941
  - do not turn "shown interest", post engagement, Signals source, recent posting,
930
942
  provider source, or first-send priority into a production filter; those are
931
943
  discovery or messaging context, not proof the lead is qualified
@@ -243,6 +243,14 @@ background source scouts` so the next turn has a user-visible request to spawn
243
243
  scouts. If the host runs them sequentially, keep the output numeric but do not
244
244
  claim the source scout was parallel.
245
245
 
246
+ For post-lead work, call `get_post_find_leads_scout_registry` after source
247
+ approval and use the returned canonical `name` values. Launch both returned
248
+ scouts in the same assistant turn/message when real subagents are available:
249
+ `post-find-leads-filter-scout` and `post-find-leads-message-scout`. This is the
250
+ same registry pattern as source scouting, but the trigger is approved
251
+ find-leads output and the join gate is both `lead-filter.md` and
252
+ `message-validation.md`.
253
+
246
254
  Only promise parallel post-lead work when parallel work actually started. If the
247
255
  host cannot or should not launch background branches, say the real sequence:
248
256
 
@@ -560,9 +560,15 @@
560
560
  "label": "Post-lead filter and message workstreams",
561
561
  "normalFlow": true,
562
562
  "onEnter": [
563
+ {
564
+ "tool": "get_post_find_leads_scout_registry",
565
+ "requiredValues": {},
566
+ "purpose": "Load the canonical two-scout registry before dispatch so Codex and Claude use the same post-find-leads agent names."
567
+ },
563
568
  {
564
569
  "action": "launch_post_lead_workstreams",
565
570
  "mode": "parallel_when_host_supports_subagents",
571
+ "registryRequired": "get_post_find_leads_scout_registry",
566
572
  "branches": [
567
573
  {
568
574
  "name": "filter-leads",
@@ -587,8 +593,8 @@
587
593
  ],
588
594
  "earlyMessageStartRule": "As soon as lead-sample.json exists with at least 5 probable good-fit rows and the lead source is confirmed or auto-confirmed, launch message-generation from brief.md, lead-review.md, and lead-sample.json. Do not wait for lead-filter.md to begin proof inventory, token planning, or candidate angles.",
589
595
  "finalMessageReconcileRule": "message-validation.md may start before lead-filter.md exists, but before message-review it must cite only lead-sample.json rows that still pass lead-filter.md. If the selected winner depends on a row later excluded by lead-filter.md, revise message-generation before message review.",
590
- "claudeRule": "In Claude Code, launch both workstreams with Task/Agent subagents in the same assistant message when Task is available. Do not run filter first and then message generation unless subagents/background work are unavailable.",
591
- "codexRule": "In Codex, launch disjoint worker subagents for filter-leads and message-generation when the host exposes subagents for this run. If the host cannot spawn them, run the same branches sequentially and say so.",
596
+ "claudeRule": "In Claude Code, launch both returned post-find-leads scouts with Task/Agent subagents in the same assistant message when Task is available. Do not run filter first and then message generation unless subagents/background work are unavailable.",
597
+ "codexRule": "In Codex, launch both returned post-find-leads scout names as disjoint subagents in the same assistant turn when the host exposes subagents for this run. If the host cannot spawn them, run the same branches sequentially and say so.",
592
598
  "fallback": "If real parallel branches are unavailable, run filter-leads and then message-generation in the parent thread. Do not claim background or parallel work in that fallback."
593
599
  },
594
600
  {
@@ -612,6 +618,7 @@
612
618
  "allowedTools": [
613
619
  "get_subskill_prompt",
614
620
  "get_subskill_asset",
621
+ "get_post_find_leads_scout_registry",
615
622
  "Task",
616
623
  "spawn_agent",
617
624
  "AskUserQuestion",
@@ -5,10 +5,16 @@ fidelity to the find-leads estimate, not re-run sourcing or produce a large
5
5
  scoring framework.
6
6
 
7
7
  The business goal is not "find people who might reply." It is "every reply the
8
- user gets should come from someone who is in the ICP and has a realistic path
9
- to buying." A lead who may reply but cannot buy, lacks budget, sits at the wrong
10
- account type, is a vendor/competitor, or is on the wrong side of a marketplace
11
- is not a good lead.
8
+ user gets should come from someone they would be excited to hear back from:
9
+ inside the ICP, able to act, and with a realistic path to buying." A lead who may
10
+ reply but cannot buy, lacks budget, sits at the wrong account type, is a
11
+ vendor/competitor, or is on the wrong side of a marketplace is not a good lead.
12
+
13
+ The exact qualification gates are client- and offer-specific, but the standard
14
+ does not change: anyone we reach out to, and anyone who replies, should be worth
15
+ the user's time to talk to. Use the client's price point, buyer, channel,
16
+ delivery constraints, market, and competitor landscape to define the concrete
17
+ gates for that campaign.
12
18
 
13
19
  Source interest is not qualification. A lead who engaged with a post, appeared
14
20
  in a Signals search, recently posted on LinkedIn, or "showed interest" in the
@@ -18,6 +24,34 @@ become a production filter or `leadScoringRubric` item. If the source lane was
18
24
  built from topical engagement, the filter should still ask: is this the right
19
25
  role, seniority, account type, budget band, market, and non-competitor?
20
26
 
27
+ Ability to pay must be considered for every campaign, but it is only a required
28
+ filter/rubric when the client, offer, or price point makes affordability a real
29
+ qualification risk. Do not force an enterprise-budget screen onto a low-cost B2C,
30
+ creator, prosumer, or small-team offer. Do not use a loose consumer-style screen
31
+ for an enterprise, services-heavy, implementation-heavy, or paid-pilot offer.
32
+ When affordability is material, `lead-filter.md` must name the campaign-native
33
+ proxy that proves the prospect can afford this specific offer at this specific
34
+ price point. Do not dodge this gate because the exact budget is unknown; use a
35
+ reasonable public proxy such as funding, headcount, revenue band, team maturity,
36
+ current tool spend, practice size, location count, consumer purchasing power,
37
+ job-to-be-done intensity, or buyer budget ownership. If affordability is material
38
+ and no reliable proxy exists in the sample or through normal enrichment, set
39
+ `Status: confirm-with-user` and ask for the budget proxy before moving to message
40
+ generation.
41
+
42
+ Use price-point proportionality:
43
+
44
+ - inexpensive B2C / prosumer / creator offers: do not require an explicit
45
+ ability-to-pay rule unless affordability is central to the ICP; fit is usually
46
+ based on use case, audience, life context, or intent, not company budget
47
+ - a few-hundred-dollar SMB or self-serve B2B offer: require evidence of an
48
+ active business, relevant workflow, and enough commercial seriousness to pay
49
+ hundreds, but do not require enterprise scale
50
+ - mid-market / paid pilot / agency or implementation-supported offer: require a
51
+ team, operational pain, and a likely owner for the budget
52
+ - enterprise / high-ACV offer: require enterprise or upper-mid-market account
53
+ fit, budget-owning seniority, department maturity, and buying-process plausibility
54
+
21
55
  Inputs:
22
56
 
23
57
  - `brief.md`
@@ -66,8 +100,8 @@ Use the actual sample to answer:
66
100
  - which repeated false positives must become explicit exclusions
67
101
  - whether filters are enough, or whether the source lane would make the
68
102
  campaign miss if launched as-is
69
- - whether the filtered leads are economically worth sending to, using the best
70
- campaign-native proxy available from row data, enrichment, or Googleable facts
103
+ - whether the filtered leads are worth sending to for this client's offer and
104
+ price point, using an economic-fit proxy only when affordability is material
71
105
 
72
106
  ## Table-Stakes Qualification Gates
73
107
 
@@ -75,6 +109,14 @@ Confirmed production filters should fail closed on buyer quality. The default
75
109
  confirmed filter set must explicitly include each table-stakes gate below, or
76
110
  `lead-filter.md` must state why a gate is irrelevant for this campaign:
77
111
 
112
+ Customize the concrete threshold for each gate to the client and offer. For
113
+ example, "can afford this" might mean nothing explicit for a low-cost B2C tool,
114
+ an active business for a few-hundred-dollar SMB offer, enterprise headcount for
115
+ one client, a funded seed-stage team for another, multi-location practice size
116
+ for another, or existing tool spend for another. Do not hard-code Sellable's
117
+ exact budget, geography, or title rules into unrelated campaigns; hard-code the
118
+ principle that passing leads must be worth the customer's time.
119
+
78
120
  - right role / function: the person owns or directly influences the buyer
79
121
  problem; wrong-function leads fail even when they match the source topic
80
122
  - right seniority / authority: the person can approve, pilot, champion, or
@@ -83,9 +125,10 @@ confirmed filter set must explicitly include each table-stakes gate below, or
83
125
  - account and channel fit: the company is the right account type and its buyers
84
126
  are reachable through the chosen channel, such as LinkedIn for LinkedIn
85
127
  outbound campaigns
86
- - economic capacity / ability to buy: the company has an appropriate budget
87
- proxy for the offer, such as headcount, revenue, funding, team size, practice
88
- size, location count, department maturity, or existing tooling
128
+ - economic capacity / ability to buy, when material: the prospect has an
129
+ appropriate price-point proxy for the offer, such as active business status,
130
+ headcount, revenue, funding, team size, practice size, location count,
131
+ department maturity, existing tooling, or budget ownership
89
132
  - competitor / vendor / wrong-side exclusion: competitors, adjacent vendors,
90
133
  agencies/service providers, resellers, and marketplace-side conflicts fail
91
134
  unless the brief explicitly targets that group
@@ -140,11 +183,15 @@ Status: confirmed | confirm-with-user | revise-find-leads
140
183
 
141
184
  ## Who We'll Keep
142
185
 
143
- - ...
186
+ - buyers we would be excited to get a reply from, including the role, authority,
187
+ account type, and price-point-appropriate commercial-fit signal that make the
188
+ reply worth taking
144
189
 
145
190
  ## Who We'll Exclude
146
191
 
147
- - ...
192
+ - people who may reply but are not worth sending to because they lack authority,
193
+ lack a plausible budget path when budget is material, sit at the wrong account
194
+ type, or are competitors/vendors/wrong-side marketplace participants
148
195
 
149
196
  ## Sample False Positives
150
197
 
@@ -165,7 +212,9 @@ Status: confirmed | confirm-with-user | revise-find-leads
165
212
 
166
213
  ## Implementation Details
167
214
 
168
- If `Status: confirmed`, include production rubrics:
215
+ If `Status: confirmed`, include production rubrics. The `economic_capacity`
216
+ example below applies when affordability is material; omit it for inexpensive
217
+ B2C/prosumer/community offers where ability to pay is not a meaningful fit gate:
169
218
 
170
219
  ```json
171
220
  {
@@ -187,6 +236,15 @@ If `Status: confirmed`, include production rubrics:
187
236
  "isRequiredCheck": true,
188
237
  "allowPartialCredit": false,
189
238
  "strictMatching": false
239
+ },
240
+ {
241
+ "checkName": "economic_capacity",
242
+ "description": "Prospect has a price-point-appropriate ability-to-pay proxy.",
243
+ "criterion": "Yes if public or enriched data shows the prospect is likely able to afford this specific offer using the campaign's chosen proxy, scaled to the price point. For low-cost B2C/prosumer offers, this may be personal fit and purchasing power. For a few-hundred-dollar SMB/self-serve offer, this may be an active business and relevant workflow. For paid-pilot, implementation-heavy, mid-market, or enterprise offers, this may require funding, revenue, headcount, department maturity, existing tool spend, location count, or budget-owner title. No if the prospect is structurally unable to buy at the offer's price point or missing the required proxy after enrichment.",
244
+ "reason": "Replies from accounts without a realistic budget path waste the user's time even when the person is warm or interested.",
245
+ "isRequiredCheck": true,
246
+ "allowPartialCredit": false,
247
+ "strictMatching": false
190
248
  }
191
249
  ]
192
250
  }
@@ -230,6 +288,15 @@ Rules:
230
288
  inputs
231
289
  - `leadScoringRubrics` must contain 2-5 production-shaped rubric items, not one
232
290
  item per bullet
291
+ - confirmed `leadScoringRubrics` must include an explicit ability-to-pay /
292
+ economic-capacity check when affordability is material to fit: a few-hundred-
293
+ dollar B2B offer, paid pilot, implementation-heavy sale, mid-market sale, or
294
+ enterprise/high-ACV motion. Do not require this check for inexpensive B2C,
295
+ prosumer, creator, community, or otherwise low-friction offers unless
296
+ affordability is central to the ICP. When present, the check must be
297
+ price-point-appropriate for the client and offer: commercial-seriousness based
298
+ for a few hundred dollars, budget-owner based for mid-market/enterprise. Do not
299
+ rely on role fit, company fit, or source engagement to imply this.
233
300
  - do not put source-path mechanics or send-priority hints into
234
301
  `leadScoringRubrics`. Examples: recently posted on LinkedIn, came from a
235
302
  Signals search, appeared in Sales Nav, was found by Prospeo, first-send wave,
@@ -249,9 +316,10 @@ Rules:
249
316
  - right role / function fit
250
317
  - buyer seniority / authority
251
318
  - account or company-type fit
252
- - economic capacity / ability to buy, using a campaign-native proxy such as
253
- headcount, revenue, funding, practice size, location count, patient volume,
254
- department maturity, existing tooling, or budget-owner title
319
+ - economic capacity / ability to buy, when material, using a campaign-native
320
+ proxy such as active business status, headcount, revenue, funding, practice
321
+ size, location count, patient volume, department maturity, existing tooling,
322
+ or budget-owner title
255
323
  - geography / market, when relevant
256
324
  - active-current-role safety
257
325
  - hard exclusions for competitors, vendors, agencies, job seekers,
@@ -275,6 +343,7 @@ When relevant to the brief and sample, preserve these recurring families:
275
343
  - buyer role / seniority
276
344
  - wrong-function exclusions
277
345
  - company-type exclusions
346
+ - economic capacity / ability-to-pay proxy
278
347
  - competitor / vendor / intermediary exclusions
279
348
  - geography
280
349
  - company size
@@ -327,6 +396,9 @@ Rules:
327
396
  `reason`, `isRequiredCheck`, `allowPartialCredit`, and `strictMatching`
328
397
  - prefer 2-5 rubric criteria total; do not map every keep/exclude bullet into
329
398
  its own scoring row
399
+ - include an explicit ability-to-pay criterion when affordability is material to
400
+ fit. It may be bundled with company size/stage only when the `checkName`,
401
+ `description`, and `criterion` still name the budget proxy directly.
330
402
  - production rubric criteria should be buyer-quality gates, not execution
331
403
  metadata. Do not create rubric rows for provider source, recently-posted
332
404
  priority, first-send ordering, or other search mechanics unless the user
@@ -350,7 +422,7 @@ Recommended rubric grouping:
350
422
 
351
423
  - buyer authority / role fit
352
424
  - company / industry / provider-lane fit
353
- - economic capacity / ability to buy
425
+ - economic capacity / ability to buy, when material
354
426
  - geography / size / stage fit, when relevant
355
427
  - competitor / vendor / wrong-side / repeated false-positive exclusion
356
428
  - one optional buyer-quality qualifier, at most, only when it changes fit scoring
@@ -0,0 +1,9 @@
1
+ {
2
+ "parallelMode": "wide",
3
+ "agentCount": 6,
4
+ "maxToolCallsPerAgent": 2,
5
+ "senderMaxAgents": 2,
6
+ "senderMaxToolCallsPerAgent": 3,
7
+ "progressMode": true,
8
+ "debugMode": true
9
+ }