@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 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.78",
3
+ "version": "0.1.80",
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
@@ -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 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",
@@ -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`, and a non-empty `Token Adherence Table`
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`. The user can still pick a revision choice.
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 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
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 economically worth sending to, using the best
76
- 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
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, "has money" might mean enterprise headcount for one client, funded
86
- seed-stage team for another, multi-location practice size for another, or
87
- existing tool spend for another. Do not hard-code Sellable's exact budget,
88
- geography, or title rules into unrelated campaigns; hard-code the principle that
89
- passing leads must be worth the customer's time.
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 company has an appropriate budget
100
- proxy for the offer, such as headcount, revenue, funding, team size, practice
101
- 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
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 proxy such as
266
- headcount, revenue, funding, practice size, location count, patient volume,
267
- 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
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
@@ -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
+ }