@sellable/mcp 0.1.78 → 0.1.80
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 +8 -0
- package/agents/post-find-leads-filter-scout.md +67 -0
- package/agents/post-find-leads-message-scout.md +67 -0
- package/agents/registry.json +84 -0
- package/dist/index-dev.js +0 -0
- package/dist/index.js +0 -0
- package/dist/server.js +4 -1
- package/dist/tools/campaigns.js +3 -2
- package/dist/tools/prompts.d.ts +38 -0
- package/dist/tools/prompts.js +67 -2
- package/package.json +1 -1
- package/skills/create-campaign/SKILL.md +17 -1
- package/skills/create-campaign-v2/SKILL.md +20 -5
- package/skills/create-campaign-v2/SOUL.md +8 -0
- package/skills/create-campaign-v2/core/flow.v2.json +9 -2
- package/skills/create-campaign-v2/references/approval-gate-framing.md +17 -2
- package/skills/create-campaign-v2/references/filter-leads.md +80 -21
- package/skills/generate-messages/SKILL.md +44 -0
- package/skills/research/config.json +9 -0
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.
|
package/agents/registry.json
CHANGED
|
@@ -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;
|
package/dist/tools/campaigns.js
CHANGED
|
@@ -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()
|
package/dist/tools/prompts.d.ts
CHANGED
|
@@ -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;
|
package/dist/tools/prompts.js
CHANGED
|
@@ -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
|
-
|
|
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
|
@@ -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
|
|
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
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
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
|
|
@@ -539,6 +548,12 @@ Message` rendered as the recipient would see it, in addition to the approved
|
|
|
539
548
|
settings review in the pre-mint approval packet. Those come after mint. The
|
|
540
549
|
pre-mint packet approves only the brief, lead source, lead filter/rubric, and
|
|
541
550
|
first message direction.
|
|
551
|
+
- Do not include post-accept DMs, connection-accepted follow-ups, day-2 / day-7
|
|
552
|
+
follow-ups, second touches, reply branches, or any other sequence-shaped copy
|
|
553
|
+
in `message-review.md` or the pre-mint approval packet. If
|
|
554
|
+
`message-validation.md` contains those, mark the message review as
|
|
555
|
+
`revise-messaging` and route back to message generation even if the artifact
|
|
556
|
+
says `Status: confirmed`.
|
|
542
557
|
- Persist `customer-roleplay.md` when a customer/operator roleplay critique is
|
|
543
558
|
run. This critique can recommend a decision, but it can never authorize mint.
|
|
544
559
|
- Persist `commit-gate-decision.md` with exactly one of the six gate choices.
|
|
@@ -1009,7 +1024,7 @@ mcp__sellable__get_subskill_prompt({ subskillName: "generate-messages", offset,
|
|
|
1009
1024
|
|
|
1010
1025
|
Continue chunked-read until `hasMore=false`. Treat all chunks as one prompt load. Follow it verbatim once loaded. The same directive is locked in `flow.v2.json::messageGeneration.toolRules`.
|
|
1011
1026
|
|
|
1012
|
-
Do NOT proceed to Step 4 (message review gate) without loading and following the full generate-messages workflow. `message-validation.md` must prove the full workflow ran (all required sections present + raw sendable Selected Winner) before `message-review.md` can recommend `approve-message`. If the message review or approval packet is ready but the generate-messages prompt was not retrieved in this run, route back to message-generation instead of asking the commit gate.
|
|
1027
|
+
Do NOT proceed to Step 4 (message review gate) without loading and following the full generate-messages workflow. `message-validation.md` must prove the full workflow ran (all required sections present + raw sendable Selected Winner + explicit single-send-unit PASS) before `message-review.md` can recommend `approve-message`. If the message review or approval packet is ready but the generate-messages prompt was not retrieved in this run, route back to message-generation instead of asking the commit gate. If `message-validation.md` contains post-accept DM, follow-up, second-touch, cadence, branch, or other sequence-shaped copy, do not summarize it as ready; route back to message-generation and require a single first outbound send.
|
|
1013
1028
|
|
|
1014
1029
|
## Tail (MANDATORY TOOL ORDER + Steps 13-16 + Threshold Trips + Hard Rules)
|
|
1015
1030
|
|
|
@@ -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
|
|
591
|
-
"codexRule": "In Codex, launch disjoint
|
|
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",
|
|
@@ -81,9 +81,13 @@ populated:
|
|
|
81
81
|
`Required Keep Rules`, `Required Exclude Rules`, `Pass Rate`,
|
|
82
82
|
`Recommendation`, and `Implementation Details` sections
|
|
83
83
|
- `message-validation.md` has `Status: confirmed`, `Mode: DRY MODE (no DB mutation)`,
|
|
84
|
-
a `Selected Winner`,
|
|
84
|
+
a `Selected Winner`, a non-empty `Token Adherence Table`, and an explicit
|
|
85
|
+
single-send-unit PASS with no post-accept DM, follow-up, cadence, branch, or
|
|
86
|
+
sequence-shaped copy in the first-message artifact
|
|
85
87
|
|
|
86
|
-
An empty anchor blocks `approve`.
|
|
88
|
+
An empty anchor blocks `approve`. Sequence-shaped message output also blocks
|
|
89
|
+
`approve` even when the copy quality is otherwise strong. The user can still
|
|
90
|
+
pick a revision choice.
|
|
87
91
|
|
|
88
92
|
## Approval Packet
|
|
89
93
|
|
|
@@ -154,6 +158,14 @@ review in the pre-mint approval packet. Those are configured and reviewed after
|
|
|
154
158
|
the campaign is created in the autonomous tail. The pre-mint packet approves
|
|
155
159
|
only the brief, lead source, lead filter/rubric, and first message direction.
|
|
156
160
|
|
|
161
|
+
Do not include or summarize post-accept DMs, connection-accepted follow-ups,
|
|
162
|
+
day-2 / day-7 follow-ups, second touches, reply branches, or any other
|
|
163
|
+
sequence-shaped copy in `message-review.md` or `approval-packet.md`. If
|
|
164
|
+
`message-validation.md` contains those, mark the message review as
|
|
165
|
+
`Recommendation: revise-messaging` and route back to message generation. Do not
|
|
166
|
+
approve by selecting only the first part of the sequence; the validation
|
|
167
|
+
artifact itself must be corrected so the approved template is unambiguous.
|
|
168
|
+
|
|
157
169
|
The packet must include a dedicated `## Message Review` section immediately
|
|
158
170
|
before `## Approved Message Template`. This is the customer-facing messaging
|
|
159
171
|
gate. It should not be a raw validation dump; it should answer:
|
|
@@ -169,6 +181,9 @@ draft if useful, mark `Recommendation: revise-messaging`, and route the user to
|
|
|
169
181
|
the `revise-messaging` commit-gate option instead of implying the campaign is
|
|
170
182
|
ready.
|
|
171
183
|
|
|
184
|
+
If the validation contains post-accept DM or follow-up copy, treat it exactly
|
|
185
|
+
like `revise-message` even if the artifact says `Status: confirmed`.
|
|
186
|
+
|
|
172
187
|
If `message-validation.md` has an extractable `Selected Winner`, the packet's
|
|
173
188
|
`## Approved Message Template` must use that winner exactly, except for
|
|
174
189
|
supported prospect-row `{{tokens}}`. Do not write a fresh approval-template
|
|
@@ -5,10 +5,10 @@ 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
|
|
9
|
-
|
|
10
|
-
|
|
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
12
|
|
|
13
13
|
The exact qualification gates are client- and offer-specific, but the standard
|
|
14
14
|
does not change: anyone we reach out to, and anyone who replies, should be worth
|
|
@@ -24,6 +24,34 @@ become a production filter or `leadScoringRubric` item. If the source lane was
|
|
|
24
24
|
built from topical engagement, the filter should still ask: is this the right
|
|
25
25
|
role, seniority, account type, budget band, market, and non-competitor?
|
|
26
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
|
+
|
|
27
55
|
Inputs:
|
|
28
56
|
|
|
29
57
|
- `brief.md`
|
|
@@ -72,8 +100,8 @@ Use the actual sample to answer:
|
|
|
72
100
|
- which repeated false positives must become explicit exclusions
|
|
73
101
|
- whether filters are enough, or whether the source lane would make the
|
|
74
102
|
campaign miss if launched as-is
|
|
75
|
-
- whether the filtered leads are
|
|
76
|
-
|
|
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
|
|
77
105
|
|
|
78
106
|
## Table-Stakes Qualification Gates
|
|
79
107
|
|
|
@@ -82,11 +110,12 @@ confirmed filter set must explicitly include each table-stakes gate below, or
|
|
|
82
110
|
`lead-filter.md` must state why a gate is irrelevant for this campaign:
|
|
83
111
|
|
|
84
112
|
Customize the concrete threshold for each gate to the client and offer. For
|
|
85
|
-
example, "
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
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.
|
|
90
119
|
|
|
91
120
|
- right role / function: the person owns or directly influences the buyer
|
|
92
121
|
problem; wrong-function leads fail even when they match the source topic
|
|
@@ -96,9 +125,10 @@ passing leads must be worth the customer's time.
|
|
|
96
125
|
- account and channel fit: the company is the right account type and its buyers
|
|
97
126
|
are reachable through the chosen channel, such as LinkedIn for LinkedIn
|
|
98
127
|
outbound campaigns
|
|
99
|
-
- economic capacity / ability to buy: the
|
|
100
|
-
proxy for the offer, such as
|
|
101
|
-
|
|
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
|
|
102
132
|
- competitor / vendor / wrong-side exclusion: competitors, adjacent vendors,
|
|
103
133
|
agencies/service providers, resellers, and marketplace-side conflicts fail
|
|
104
134
|
unless the brief explicitly targets that group
|
|
@@ -153,11 +183,15 @@ Status: confirmed | confirm-with-user | revise-find-leads
|
|
|
153
183
|
|
|
154
184
|
## Who We'll Keep
|
|
155
185
|
|
|
156
|
-
-
|
|
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
|
|
157
189
|
|
|
158
190
|
## Who We'll Exclude
|
|
159
191
|
|
|
160
|
-
-
|
|
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
|
|
161
195
|
|
|
162
196
|
## Sample False Positives
|
|
163
197
|
|
|
@@ -178,7 +212,9 @@ Status: confirmed | confirm-with-user | revise-find-leads
|
|
|
178
212
|
|
|
179
213
|
## Implementation Details
|
|
180
214
|
|
|
181
|
-
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:
|
|
182
218
|
|
|
183
219
|
```json
|
|
184
220
|
{
|
|
@@ -200,6 +236,15 @@ If `Status: confirmed`, include production rubrics:
|
|
|
200
236
|
"isRequiredCheck": true,
|
|
201
237
|
"allowPartialCredit": false,
|
|
202
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
|
|
203
248
|
}
|
|
204
249
|
]
|
|
205
250
|
}
|
|
@@ -243,6 +288,15 @@ Rules:
|
|
|
243
288
|
inputs
|
|
244
289
|
- `leadScoringRubrics` must contain 2-5 production-shaped rubric items, not one
|
|
245
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.
|
|
246
300
|
- do not put source-path mechanics or send-priority hints into
|
|
247
301
|
`leadScoringRubrics`. Examples: recently posted on LinkedIn, came from a
|
|
248
302
|
Signals search, appeared in Sales Nav, was found by Prospeo, first-send wave,
|
|
@@ -262,9 +316,10 @@ Rules:
|
|
|
262
316
|
- right role / function fit
|
|
263
317
|
- buyer seniority / authority
|
|
264
318
|
- account or company-type fit
|
|
265
|
-
- economic capacity / ability to buy, using a campaign-native
|
|
266
|
-
headcount, revenue, funding, practice
|
|
267
|
-
department maturity, existing tooling,
|
|
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
|
|
268
323
|
- geography / market, when relevant
|
|
269
324
|
- active-current-role safety
|
|
270
325
|
- hard exclusions for competitors, vendors, agencies, job seekers,
|
|
@@ -288,6 +343,7 @@ When relevant to the brief and sample, preserve these recurring families:
|
|
|
288
343
|
- buyer role / seniority
|
|
289
344
|
- wrong-function exclusions
|
|
290
345
|
- company-type exclusions
|
|
346
|
+
- economic capacity / ability-to-pay proxy
|
|
291
347
|
- competitor / vendor / intermediary exclusions
|
|
292
348
|
- geography
|
|
293
349
|
- company size
|
|
@@ -340,6 +396,9 @@ Rules:
|
|
|
340
396
|
`reason`, `isRequiredCheck`, `allowPartialCredit`, and `strictMatching`
|
|
341
397
|
- prefer 2-5 rubric criteria total; do not map every keep/exclude bullet into
|
|
342
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.
|
|
343
402
|
- production rubric criteria should be buyer-quality gates, not execution
|
|
344
403
|
metadata. Do not create rubric rows for provider source, recently-posted
|
|
345
404
|
priority, first-send ordering, or other search mechanics unless the user
|
|
@@ -363,7 +422,7 @@ Recommended rubric grouping:
|
|
|
363
422
|
|
|
364
423
|
- buyer authority / role fit
|
|
365
424
|
- company / industry / provider-lane fit
|
|
366
|
-
- economic capacity / ability to buy
|
|
425
|
+
- economic capacity / ability to buy, when material
|
|
367
426
|
- geography / size / stage fit, when relevant
|
|
368
427
|
- competitor / vendor / wrong-side / repeated false-positive exclusion
|
|
369
428
|
- one optional buyer-quality qualifier, at most, only when it changes fit scoring
|
|
@@ -561,6 +561,11 @@ The winner entry must:
|
|
|
561
561
|
`[insert ...]`, or `[generated ...]`. If the only scalable option needs a
|
|
562
562
|
deferred per-row instruction, route to `revise-message` and fix the
|
|
563
563
|
token plan instead.
|
|
564
|
+
- contain exactly one outbound send unit. Do not include post-accept DMs,
|
|
565
|
+
follow-ups, second touches, cadence branches, sequence notes, or later-message
|
|
566
|
+
copy in the Selected Winner or nearby rendered examples. If a later message
|
|
567
|
+
idea is useful, keep it out of the generated first-message artifact and put it
|
|
568
|
+
in an internal finding for post-mint sequence work.
|
|
564
569
|
- never use generic signal tokens like `{{recentSignal}}`,
|
|
565
570
|
`{{recent_signal}}`, or `{{recent_signal_quote}}`. If row personalization
|
|
566
571
|
is needed in the raw selected winner, render the concrete sentence from
|
|
@@ -1046,6 +1051,41 @@ This applies to:
|
|
|
1046
1051
|
|
|
1047
1052
|
Truth is not enough. The line must do a job for the prospect.
|
|
1048
1053
|
|
|
1054
|
+
### Single-send-unit gate (HARD INVARIANT)
|
|
1055
|
+
|
|
1056
|
+
Dry-mode create-campaign-v2 message generation approves the **first outbound
|
|
1057
|
+
send only**. The Selected Winner must be exactly one send unit for one channel:
|
|
1058
|
+
one INVITE, one INMAIL_OPEN / INMAIL_CLOSED body plus optional subject, or one
|
|
1059
|
+
DM body when the campaign explicitly starts with DM.
|
|
1060
|
+
|
|
1061
|
+
Do not generate, approve, or include sequence-shaped copy in
|
|
1062
|
+
`message-validation.md`, `message-review.md`, selected copy, rendered examples,
|
|
1063
|
+
token notes, findings, or recommendation text.
|
|
1064
|
+
|
|
1065
|
+
Blocked sequence-shaped outputs include:
|
|
1066
|
+
|
|
1067
|
+
- post-accept DMs
|
|
1068
|
+
- connection-accepted follow-ups
|
|
1069
|
+
- day-2 / day-7 follow-ups
|
|
1070
|
+
- second touches
|
|
1071
|
+
- "reply/acceptance" branches
|
|
1072
|
+
- cadence notes
|
|
1073
|
+
- fallback send paths as copy
|
|
1074
|
+
- any "INVITE + DM", "InMail + follow-up", or multi-step message plan
|
|
1075
|
+
|
|
1076
|
+
If a proof, PS, Loom, setup link, or meta-demo line cannot fit in the first send
|
|
1077
|
+
unit, either compress it into that first send unit or omit it. Do not move it to
|
|
1078
|
+
a later message. Preserve the idea as an internal `Findings` note only if it is
|
|
1079
|
+
useful after campaign mint.
|
|
1080
|
+
|
|
1081
|
+
Before returning `Status: confirmed`, run this explicit check:
|
|
1082
|
+
|
|
1083
|
+
`Single send unit: PASS | BLOCKED — reason`
|
|
1084
|
+
|
|
1085
|
+
If BLOCKED, set `Recommendation: revise-message` and rewrite the Selected
|
|
1086
|
+
Winner until it contains only the first outbound send. A message can be strong
|
|
1087
|
+
and still be invalid if it requires a post-accept DM or other sequence copy.
|
|
1088
|
+
|
|
1049
1089
|
Use the 4-block shape below as the default packaging only when it helps
|
|
1050
1090
|
the reply reason land. If a different shape is more natural, shorter, or
|
|
1051
1091
|
more compelling for this offer, use it and explain why in `Packaging
|
|
@@ -2216,6 +2256,8 @@ hiring for`
|
|
|
2216
2256
|
- No semicolons or `X here` sender fragments: PASS | BLOCKED — reason
|
|
2217
2257
|
- CTA is useful and low-friction: PASS | BLOCKED — reason
|
|
2218
2258
|
- Every line has a prospect-side "so what?": PASS | BLOCKED — reason
|
|
2259
|
+
- Single send unit only, with no post-accept DM/follow-up/sequence copy:
|
|
2260
|
+
PASS | BLOCKED — reason
|
|
2219
2261
|
- Winner clearly beats rejected drafts: PASS | BLOCKED — reason
|
|
2220
2262
|
- Decision: confirmed | revise-message | revise-filter
|
|
2221
2263
|
|
|
@@ -2247,6 +2289,8 @@ Automatic failures in dry mode:
|
|
|
2247
2289
|
- no compelling proof element despite the brief supporting one
|
|
2248
2290
|
- a CTA that depends on missing proof
|
|
2249
2291
|
- copy that fails the Thomas revision filters
|
|
2292
|
+
- any post-accept DM, follow-up, second touch, cadence branch, or sequence copy
|
|
2293
|
+
in the first-message validation artifact
|
|
2250
2294
|
- any mutation tool usage
|
|
2251
2295
|
|
|
2252
2296
|
## Workflow
|