@sellable/mcp 0.1.192 → 0.1.194
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 +2 -2
- package/agents/post-find-leads-message-scout.md +9 -10
- package/dist/auth.js +1 -1
- package/dist/server.js +1 -4
- package/dist/tools/leads.d.ts +5 -0
- package/dist/tools/leads.js +6 -1
- package/dist/tools/prompts.d.ts +1 -37
- package/dist/tools/prompts.js +3 -17
- package/dist/tools/registry.d.ts +0 -23
- package/package.json +1 -1
- package/skills/create-campaign/SKILL.md +2 -2
- package/skills/create-campaign-v2/SKILL.md +4 -4
- package/skills/create-campaign-v2/core/auto-execute.README.md +2 -2
- package/skills/create-campaign-v2/core/auto-execute.yaml +1 -1
- package/skills/create-campaign-v2/core/flow.v2.json +1 -1
- package/skills/create-campaign-v2/references/approval-gate-framing.md +1 -1
- package/skills/create-campaign-v2/references/parallel-critique-protocol.md +1 -1
- package/skills/create-campaign-v2/references/thomas-variant-selection.md +1 -1
- package/skills/create-campaign-v2-tail/SKILL.md +4 -4
- package/skills/generate-messages/SKILL.md +7 -7
- package/skills/research/SKILL.md +1 -1
- package/skills/generate-messages-compact/SKILL.md +0 -100
- package/skills/generate-messages-compact/references/examples-critique-revision.md +0 -37
- /package/skills/{campaign-messages → _archive/campaign-messages}/SKILL.md +0 -0
- /package/skills/{campaign-messages → _archive/campaign-messages}/flow.v1.json +0 -0
- /package/skills/{craft-message → _archive/craft-message}/SKILL.md +0 -0
package/README.md
CHANGED
|
@@ -388,7 +388,7 @@ Sellable writing workflows.
|
|
|
388
388
|
- `get_rows_minimal` - Get minimal lead list (fast)
|
|
389
389
|
- `get_rows` - Get full lead data
|
|
390
390
|
- `update_cell` - Save message/approve
|
|
391
|
-
- `
|
|
391
|
+
- `get_subskill_prompt({ subskillName: "generate-messages" })` - Get the full message-generation prompt with references and quality gates
|
|
392
392
|
- `start_campaign` / `pause_campaign` - Control sending
|
|
393
393
|
|
|
394
394
|
### Lead Tools (Free)
|
|
@@ -435,7 +435,7 @@ Sellable writing workflows.
|
|
|
435
435
|
|
|
436
436
|
### "Sellable not configured" Error
|
|
437
437
|
|
|
438
|
-
Create `~/.sellable/config.json` with your token. Get one at https://app.sellable.dev/settings
|
|
438
|
+
Create `~/.sellable/config.json` with your token. Get one at https://app.sellable.dev/settings?tab=integrations.
|
|
439
439
|
|
|
440
440
|
### Token Not Working
|
|
441
441
|
|
|
@@ -37,14 +37,13 @@ the branch input.
|
|
|
37
37
|
|
|
38
38
|
## Required First Steps
|
|
39
39
|
|
|
40
|
-
1. Load the
|
|
40
|
+
1. Load the full normal-path message prompt:
|
|
41
41
|
|
|
42
|
-
`get_subskill_prompt({ subskillName: "generate-messages
|
|
42
|
+
`get_subskill_prompt({ subskillName: "generate-messages" })`
|
|
43
43
|
|
|
44
|
-
2. Use that prompt as the drafting contract.
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
safety/checklist instructions as a substitute for the message prompt.
|
|
44
|
+
2. Use that prompt as the drafting contract. Do not use any alternate prompt,
|
|
45
|
+
examples-only shortcut, or create-campaign safety/checklist instructions as a
|
|
46
|
+
substitute for the full message prompt.
|
|
48
47
|
3. Draft only from the campaign brief, selected source context, and initial
|
|
49
48
|
campaign-table execution slice rows supplied by the parent.
|
|
50
49
|
4. Keep the work provisional until the user chooses `Use Template` in Messages.
|
|
@@ -57,8 +56,8 @@ Return the following to the parent thread:
|
|
|
57
56
|
- token fill rules and fallbacks
|
|
58
57
|
- one rendered good-fill sample for a plausible passing campaign-table row
|
|
59
58
|
- one omit/fallback sample when the row signal is not safe
|
|
60
|
-
- pass/fail notes against the generate-messages
|
|
61
|
-
-
|
|
59
|
+
- pass/fail notes against the generate-messages quality gates
|
|
60
|
+
- message-draft runtime status: `ready`, `blocked`, `retry-needed`, or `stale`
|
|
62
61
|
- basis token containing campaign revision/updatedAt, brief hash,
|
|
63
62
|
`selectedLeadListId`, `workflowTableId`, execution-slice row ids/hash, filter
|
|
64
63
|
choice, and rubric/filter basis when present
|
|
@@ -78,7 +77,7 @@ When reporting branch runtime proof, use this shape under
|
|
|
78
77
|
- `runId` or `fallbackId`
|
|
79
78
|
- `startedAt` and `updatedAt`
|
|
80
79
|
- `basisToken` and `basis`
|
|
81
|
-
- optional `
|
|
80
|
+
- optional `messageDraftOutputRef`, `messageDraftOutput`, and `error`
|
|
82
81
|
|
|
83
82
|
Do not tell the UI to show Message Draft Builder as running unless this proof
|
|
84
83
|
exists and points at the current non-empty campaign-table execution slice.
|
|
@@ -137,7 +136,7 @@ enablement`, or omit it.
|
|
|
137
136
|
|
|
138
137
|
Return a concise status with:
|
|
139
138
|
|
|
140
|
-
- prompt basis loaded: `generate-messages
|
|
139
|
+
- prompt basis loaded: `generate-messages`
|
|
141
140
|
- live campaign basis used
|
|
142
141
|
- proposed template
|
|
143
142
|
- token fill rules/fallbacks
|
package/dist/auth.js
CHANGED
|
@@ -72,7 +72,7 @@ export function getConfig() {
|
|
|
72
72
|
"}\n\n" +
|
|
73
73
|
"Config path resolution order:\n" +
|
|
74
74
|
`${renderConfigPathOrder(configPathCandidates)}\n\n` +
|
|
75
|
-
"Get your token at: https://app.sellable.dev/settings
|
|
75
|
+
"Get your token at: https://app.sellable.dev/settings?tab=integrations\n" +
|
|
76
76
|
"Then run list_workspaces + set_active_workspace to select a workspace.");
|
|
77
77
|
}
|
|
78
78
|
try {
|
package/dist/server.js
CHANGED
|
@@ -22,7 +22,7 @@ import { fetchCompany, fetchCompanyPosts, fetchLinkedInPosts, fetchLinkedInProfi
|
|
|
22
22
|
import { getCampaignNavigationState } from "./tools/navigation.js";
|
|
23
23
|
import { addOnDemandLeads, createOnDemandCampaign, createOnDemandTable, initOnDemandSequence, pauseOnDemandCampaign, startOnDemandCampaign, } from "./tools/one-off.js";
|
|
24
24
|
import { upsertRubric } from "./tools/processing.js";
|
|
25
|
-
import { completeSenderResearch,
|
|
25
|
+
import { completeSenderResearch, getPostFindLeadsScoutRegistry, getSourceScoutRegistry, getSubskillAsset, getSubskillPrompt, listSubskillPrompts, searchSubskillPrompts, } from "./tools/prompts.js";
|
|
26
26
|
import { waitForCampaignTableReady, waitForLeadListReady, } from "./tools/readiness.js";
|
|
27
27
|
import { allTools } from "./tools/registry.js";
|
|
28
28
|
import { getRows, getTableRows, getTableRowsMinimal } from "./tools/rows.js";
|
|
@@ -392,9 +392,6 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
392
392
|
case "queue_cells":
|
|
393
393
|
result = await queueCells(args?.tableId, args?.cellIds);
|
|
394
394
|
break;
|
|
395
|
-
case "get_message_prompt":
|
|
396
|
-
result = await getMessagePrompt();
|
|
397
|
-
break;
|
|
398
395
|
case "get_subskill_prompt":
|
|
399
396
|
result = getSubskillPrompt(args?.subskillName, args?.offset, args?.limit);
|
|
400
397
|
break;
|
package/dist/tools/leads.d.ts
CHANGED
|
@@ -2394,6 +2394,8 @@ export declare function searchSignals(input: SignalSearchInput): Promise<{
|
|
|
2394
2394
|
id: string;
|
|
2395
2395
|
url: string | undefined;
|
|
2396
2396
|
matchedKeyword: string | null;
|
|
2397
|
+
searchType: "company" | "keyword" | "profile" | "post" | null;
|
|
2398
|
+
displayLabel: string | null;
|
|
2397
2399
|
authorName: string;
|
|
2398
2400
|
authorHeadline: string;
|
|
2399
2401
|
authorProfileUrl: string;
|
|
@@ -2411,6 +2413,9 @@ export declare function searchSignals(input: SignalSearchInput): Promise<{
|
|
|
2411
2413
|
}[];
|
|
2412
2414
|
keywordResults: {
|
|
2413
2415
|
keyword: string;
|
|
2416
|
+
searchType: "company" | "keyword" | "profile" | "post";
|
|
2417
|
+
displayLabel: string;
|
|
2418
|
+
tabId: string | null;
|
|
2414
2419
|
postCount: number;
|
|
2415
2420
|
avgEngagement: number | null;
|
|
2416
2421
|
hasMore: boolean;
|
package/dist/tools/leads.js
CHANGED
|
@@ -471,6 +471,8 @@ function summarizeSignalPost(post) {
|
|
|
471
471
|
id: post.id,
|
|
472
472
|
url: post.url,
|
|
473
473
|
matchedKeyword: post.matchedKeyword ?? null,
|
|
474
|
+
searchType: post.searchType ?? null,
|
|
475
|
+
displayLabel: post.displayLabel ?? null,
|
|
474
476
|
authorName: post.author?.name ?? "",
|
|
475
477
|
authorHeadline: post.author?.headline ?? "",
|
|
476
478
|
authorProfileUrl: post.author?.profileUrl ?? "",
|
|
@@ -500,6 +502,9 @@ function summarizeSignalSearchResponse(response) {
|
|
|
500
502
|
.map(summarizeSignalPost);
|
|
501
503
|
const keywordResults = (response.keywordResults ?? []).map((kw) => ({
|
|
502
504
|
keyword: kw.keyword,
|
|
505
|
+
searchType: kw.searchType ?? "keyword",
|
|
506
|
+
displayLabel: kw.displayLabel ?? kw.keyword,
|
|
507
|
+
tabId: kw.tabId ?? null,
|
|
503
508
|
postCount: kw.postCount,
|
|
504
509
|
avgEngagement: kw.avgEngagement ?? null,
|
|
505
510
|
hasMore: kw.hasMore ?? false,
|
|
@@ -2877,7 +2882,7 @@ export async function confirmLeadList(input) {
|
|
|
2877
2882
|
rowCount: leadListRowCount,
|
|
2878
2883
|
status: typeof readiness.status === "string"
|
|
2879
2884
|
? readiness.status
|
|
2880
|
-
: leadListConfig?.importStatus ?? null,
|
|
2885
|
+
: (leadListConfig?.importStatus ?? null),
|
|
2881
2886
|
targetLeadCount: typeof readiness.targetLeadCount === "number"
|
|
2882
2887
|
? readiness.targetLeadCount
|
|
2883
2888
|
: typeof leadListConfig?.targetLeadCount === "number"
|
package/dist/tools/prompts.d.ts
CHANGED
|
@@ -1,15 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Response from the edit-message prompt endpoint.
|
|
3
|
-
* The prompt contains all 11 quality gates inline.
|
|
4
|
-
*/
|
|
5
|
-
export interface PromptResponse {
|
|
6
|
-
prompt: string;
|
|
7
|
-
metadata: {
|
|
8
|
-
framework: string;
|
|
9
|
-
author: string;
|
|
10
|
-
description: string;
|
|
11
|
-
};
|
|
12
|
-
}
|
|
13
1
|
export interface SubskillPromptResponse {
|
|
14
2
|
name: string;
|
|
15
3
|
description: string;
|
|
@@ -136,7 +124,7 @@ export interface PostFindLeadsScoutRegistryResponse {
|
|
|
136
124
|
runtimeProofRequiredFields: string[];
|
|
137
125
|
promptRequired?: string;
|
|
138
126
|
basisFields: string[];
|
|
139
|
-
|
|
127
|
+
messageDraftOutputFields: string[];
|
|
140
128
|
reusePolicy?: string;
|
|
141
129
|
};
|
|
142
130
|
usage: {
|
|
@@ -148,29 +136,6 @@ export interface PostFindLeadsScoutRegistryResponse {
|
|
|
148
136
|
export declare const DEFAULT_SUBSKILL_PROMPT_CHUNK_CHARS = 48000;
|
|
149
137
|
export declare const MAX_SUBSKILL_PROMPT_CHUNK_CHARS = 48000;
|
|
150
138
|
export declare const promptToolDefinitions: ({
|
|
151
|
-
name: string;
|
|
152
|
-
description: string;
|
|
153
|
-
inputSchema: {
|
|
154
|
-
type: string;
|
|
155
|
-
properties: {
|
|
156
|
-
limit?: undefined;
|
|
157
|
-
includePublic?: undefined;
|
|
158
|
-
includeInternal?: undefined;
|
|
159
|
-
subskillName?: undefined;
|
|
160
|
-
offset?: undefined;
|
|
161
|
-
assetPath?: undefined;
|
|
162
|
-
query?: undefined;
|
|
163
|
-
depth?: undefined;
|
|
164
|
-
proofItemsFound?: undefined;
|
|
165
|
-
caseStudyItemsFound?: undefined;
|
|
166
|
-
credibilitySignalsFound?: undefined;
|
|
167
|
-
notes?: undefined;
|
|
168
|
-
};
|
|
169
|
-
required: never[];
|
|
170
|
-
additionalProperties?: undefined;
|
|
171
|
-
};
|
|
172
|
-
_meta?: undefined;
|
|
173
|
-
} | {
|
|
174
139
|
name: string;
|
|
175
140
|
description: string;
|
|
176
141
|
inputSchema: {
|
|
@@ -373,7 +338,6 @@ export declare const promptToolDefinitions: ({
|
|
|
373
338
|
})[];
|
|
374
339
|
export declare function getSourceScoutRegistry(): SourceScoutRegistryResponse;
|
|
375
340
|
export declare function getPostFindLeadsScoutRegistry(): PostFindLeadsScoutRegistryResponse;
|
|
376
|
-
export declare function getMessagePrompt(): Promise<PromptResponse>;
|
|
377
341
|
export declare function listSubskillPrompts(limit?: number, includePublic?: boolean, includeInternal?: boolean): ListSubskillPromptsResponse;
|
|
378
342
|
export declare function getSubskillPrompt(subskillName: string, offset?: number, limit?: number): SubskillPromptResponse;
|
|
379
343
|
export declare function getSubskillAsset(subskillName: string, assetPath: string, offset?: number, limit?: number): SubskillAssetResponse;
|
package/dist/tools/prompts.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import * as fs from "fs";
|
|
2
2
|
import * as path from "path";
|
|
3
|
-
import { getApi } from "../api.js";
|
|
4
3
|
import { getSkillByName, listSkills, resolveSkillsDir, stripFrontmatter, } from "../skills.js";
|
|
5
4
|
import { markCreateCampaignPromptLoaded, markResearchPromptLoaded, markSenderResearchCompleted, } from "./flow-preflight.js";
|
|
6
5
|
// Chunk size sits below Claude Code's max-token cap on tool results
|
|
@@ -15,15 +14,6 @@ import { markCreateCampaignPromptLoaded, markResearchPromptLoaded, markSenderRes
|
|
|
15
14
|
export const DEFAULT_SUBSKILL_PROMPT_CHUNK_CHARS = 48_000;
|
|
16
15
|
export const MAX_SUBSKILL_PROMPT_CHUNK_CHARS = 48_000;
|
|
17
16
|
export const promptToolDefinitions = [
|
|
18
|
-
{
|
|
19
|
-
name: "get_message_prompt",
|
|
20
|
-
description: "Get the REPLY framework prompt for crafting LinkedIn messages. Returns the full prompt with 11 quality gates inline. Load this when actively crafting to ensure quality standards.",
|
|
21
|
-
inputSchema: {
|
|
22
|
-
type: "object",
|
|
23
|
-
properties: {},
|
|
24
|
-
required: [],
|
|
25
|
-
},
|
|
26
|
-
},
|
|
27
17
|
{
|
|
28
18
|
name: "list_subskill_prompts",
|
|
29
19
|
description: "List available Sellable subskill prompts with visibility controls. Use this when the host does not expose prompts/list.",
|
|
@@ -315,7 +305,7 @@ export function getPostFindLeadsScoutRegistry() {
|
|
|
315
305
|
"basis.workflowTableId",
|
|
316
306
|
"basis.reviewSampleRowHash or basis.reviewSampleRowIds",
|
|
317
307
|
],
|
|
318
|
-
promptRequired: 'get_subskill_prompt({ subskillName: "generate-messages
|
|
308
|
+
promptRequired: 'get_subskill_prompt({ subskillName: "generate-messages" }); the full generate-messages prompt is required for normal message drafting',
|
|
319
309
|
basisFields: [
|
|
320
310
|
"campaign revision or updatedAt",
|
|
321
311
|
"brief hash",
|
|
@@ -325,7 +315,7 @@ export function getPostFindLeadsScoutRegistry() {
|
|
|
325
315
|
"filter choice",
|
|
326
316
|
"filter/rubric basis when present",
|
|
327
317
|
],
|
|
328
|
-
|
|
318
|
+
messageDraftOutputFields: [
|
|
329
319
|
"templateRecommendation",
|
|
330
320
|
"tokenFillRules",
|
|
331
321
|
"renderedSample",
|
|
@@ -342,14 +332,10 @@ export function getPostFindLeadsScoutRegistry() {
|
|
|
342
332
|
usage: {
|
|
343
333
|
codex: "After confirm_lead_list copies source rows and the initial campaign-table execution slice exists, ask the filter-choice question immediately. Do not spawn returned post-lead scout names before that question. Once the user answers, spawn Message Draft Builder from the same campaign/table basis. If the user chooses filters, also spawn Lead Fit Builder, move to Filter Rules, save rubrics, ask for filter approval, then keep the browser on Filter Leads and show `Filters saved + waiting for message approval` while the message recommendation is reviewed. If filters are skipped, move to Messages/message review.",
|
|
344
334
|
claude: "After confirm_lead_list copies source rows and the initial campaign-table execution slice exists, ask the filter-choice question immediately. Do not invoke returned post-lead Task/Agent names before that question. Once the user answers, invoke Message Draft Builder from the same campaign/table basis. If the user chooses filters, also invoke Lead Fit Builder, move to Filter Rules, save rubrics, ask for filter approval, then keep the browser on Filter Leads and show `Filters saved + waiting for message approval` while the message recommendation is reviewed. If filters are skipped, move to Messages/message review.",
|
|
345
|
-
parentThreadRule: 'Named agents are optional acceleration, but message drafting is not optional. If post-find-leads-message-scout is available, run it as the background Message Draft Builder after the filter-choice answer. If it is absent, do not customer-surface install status; the main thread must execute the same message branch from CampaignOffer state, selected source state, workflowTableId, and initial campaign-table execution slice rows. Local markdown/json files are not normal-path inputs. The filter-choice question is the first post-import user gate; do not load post-lead registries
|
|
335
|
+
parentThreadRule: 'Named agents are optional acceleration, but message drafting is not optional. If post-find-leads-message-scout is available, run it as the background Message Draft Builder after the filter-choice answer. If it is absent, do not customer-surface install status; the main thread must execute the same message branch from CampaignOffer state, selected source state, workflowTableId, and initial campaign-table execution slice rows. Local markdown/json files are not normal-path inputs. The filter-choice question is the first post-import user gate; do not load post-lead registries or filter references before it. Message drafting starts after the filter-choice answer, must load get_subskill_prompt({ subskillName: "generate-messages" }), must read live campaign table state through scoped MCP/product tools, and must reject mismatched selectedLeadListId/workflowTableId/campaign/workspace input. Do not use any alternate or examples-only message prompt. On the filter path, keep the browser on Filter Rules after save_rubrics so the user can approve the saved criteria; only then move to Filter Leads, show `Filters saved + waiting for message approval`, and wait there for message approval. Enrichment, filtering, Generate Message cells, sender setup, sequence attach, and launch wait for template approval on the Use Template path. On the skip path, move to Messages/message review and wait for message approval before enrichment or Settings. Do not render message review from checklist or shortcut instructions; message review requires a messageDraftRecommendation whose basis proves the generate-messages prompt ran for the current campaign/table execution slice. Do not automatically rerun Message Draft Builder after filters/enrichment finish; show the initial draft by default and offer an enriched rewrite only with explicit user opt-in.',
|
|
346
336
|
},
|
|
347
337
|
};
|
|
348
338
|
}
|
|
349
|
-
export async function getMessagePrompt() {
|
|
350
|
-
const api = getApi();
|
|
351
|
-
return api.get("/api/v3/prompts/edit-message");
|
|
352
|
-
}
|
|
353
339
|
function filterSubskills(includePublic = false, includeInternal = true) {
|
|
354
340
|
return listSkills().filter((skill) => {
|
|
355
341
|
if (skill.visibility === "public")
|
package/dist/tools/registry.d.ts
CHANGED
|
@@ -214,29 +214,6 @@ export declare const allTools: ({
|
|
|
214
214
|
required: string[];
|
|
215
215
|
additionalProperties: boolean;
|
|
216
216
|
};
|
|
217
|
-
} | {
|
|
218
|
-
name: string;
|
|
219
|
-
description: string;
|
|
220
|
-
inputSchema: {
|
|
221
|
-
type: string;
|
|
222
|
-
properties: {
|
|
223
|
-
limit?: undefined;
|
|
224
|
-
includePublic?: undefined;
|
|
225
|
-
includeInternal?: undefined;
|
|
226
|
-
subskillName?: undefined;
|
|
227
|
-
offset?: undefined;
|
|
228
|
-
assetPath?: undefined;
|
|
229
|
-
query?: undefined;
|
|
230
|
-
depth?: undefined;
|
|
231
|
-
proofItemsFound?: undefined;
|
|
232
|
-
caseStudyItemsFound?: undefined;
|
|
233
|
-
credibilitySignalsFound?: undefined;
|
|
234
|
-
notes?: undefined;
|
|
235
|
-
};
|
|
236
|
-
required: never[];
|
|
237
|
-
additionalProperties?: undefined;
|
|
238
|
-
};
|
|
239
|
-
_meta?: undefined;
|
|
240
217
|
} | {
|
|
241
218
|
name: string;
|
|
242
219
|
description: string;
|
package/package.json
CHANGED
|
@@ -755,8 +755,8 @@ updates.
|
|
|
755
755
|
3. Follow that prompt and workflow config exactly.
|
|
756
756
|
4. For message generation, use the `post-find-leads-message-scout` agent when
|
|
757
757
|
available. The worker and parent-thread fallback must load
|
|
758
|
-
`mcp__sellable__get_subskill_prompt({ subskillName: "generate-messages
|
|
759
|
-
|
|
758
|
+
`mcp__sellable__get_subskill_prompt({ subskillName: "generate-messages" })`.
|
|
759
|
+
Do not use any alternate or examples-only message prompt. Message review requires Message Draft Builder output:
|
|
760
760
|
do not draft from a checklist, local markdown artifact, or parent-thread
|
|
761
761
|
intuition. Use campaign state, campaign brief content, selected source state, and
|
|
762
762
|
initial campaign-table execution slice rows as the source of truth; do not read stale local
|
|
@@ -316,12 +316,12 @@ Run `post-find-leads-message-scout` as the background Message Draft Builder
|
|
|
316
316
|
whenever the host exposes it. It must load:
|
|
317
317
|
|
|
318
318
|
```text
|
|
319
|
-
get_subskill_prompt({ subskillName: "generate-messages
|
|
319
|
+
get_subskill_prompt({ subskillName: "generate-messages" })
|
|
320
320
|
```
|
|
321
321
|
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
322
|
+
Do not use any alternate or examples-only message prompt. If the host cannot
|
|
323
|
+
launch the agent, the parent fallback must run the same full prompt from live
|
|
324
|
+
campaign state. Do not render message
|
|
325
325
|
review until `messageDraftRecommendation` proves current campaign/table basis.
|
|
326
326
|
|
|
327
327
|
## Hard Gates
|
|
@@ -80,7 +80,7 @@ into the campaign).
|
|
|
80
80
|
synthesizer re-runs the Phase 84 finalizer pass on its rewrite.
|
|
81
81
|
This is the last guardrail against critic-introduced token drift.
|
|
82
82
|
Default stays `true`.
|
|
83
|
-
- **`critique.opus.enabled`** — Flag gating Opus
|
|
83
|
+
- **`critique.opus.enabled`** — Flag gating Opus refinement
|
|
84
84
|
usage. Opus is reserved for the highest-value subset only. Stays
|
|
85
85
|
off by default; flip on only when the targeting critic flags a
|
|
86
86
|
high-priority row and budget allows.
|
|
@@ -158,7 +158,7 @@ logs and adjust:
|
|
|
158
158
|
critique protocol: bounded sample, parallel critics, synthesis,
|
|
159
159
|
budget caps, and token-contract enforcement.
|
|
160
160
|
- `references/thomas-variant-selection.md` — Plan 85-03 rules for
|
|
161
|
-
which rows earn an Opus
|
|
161
|
+
which rows earn an Opus refinement rewrite.
|
|
162
162
|
- `references/thomas-revision-filters.md` — the positive-target and
|
|
163
163
|
automatic-revision filters that gate any critique rewrite.
|
|
164
164
|
|
|
@@ -87,7 +87,7 @@ messaging:
|
|
|
87
87
|
# guardrail against critic-introduced token drift.
|
|
88
88
|
enforceFinalizerPass: true
|
|
89
89
|
opus:
|
|
90
|
-
# Opus
|
|
90
|
+
# Opus refinement is reserved for the highest-value subset
|
|
91
91
|
# only. Count is a hard cap per critique pass. Selection lives
|
|
92
92
|
# in references/thomas-variant-selection.md.
|
|
93
93
|
enabled: false
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":"v2.1-compact","workflow":"create-campaign-v2","principle":"CampaignOffer state and watch link are canonical. Create the watched shell, approve source, confirm the source list, process the first campaign-table slice, approve filters/message, then run the bounded cascade before Settings and explicit start.","normalCustomerPath":"Use campaign state, MCP responses, and concise watchNarration. Do not create, read, link, or surface local draft files. Print the exact tokenized create_campaign.watchUrl only once at the initial brief handoff.","legacyCompatibility":{"validationSubskill":"create-campaign-v2-validation","tailSubskill":"create-campaign-v2-tail","rule":"Legacy validation/rehearsal files are opt-in diagnostics only and are not active flow gates."},"commitGateChoices":["approve","revise-brief","revise-leads","revise-rubric","revise-messaging","abort"],"canonicalStateFields":["campaignId","watchUrl","campaignBrief","currentStep","watchNarration","interactionMode","providerSearchAssociation","selectedLeadListId","workflowTableId","filterChoice","leadScoringRubrics","messageDraftRecommendation","approvedMessageTemplate","senderIds","sequenceTemplate","runningState"],"watchNarrationTransitionContract":{"rule":"Every watched currentStep switch must include fresh watchNarration.","requiredFields":["stage","headline","visibleState","agentIntent","nextAction"],"copyMustInclude":["what just happened","current visible page/state","next user action"],"appliesToTools":["create_campaign","update_campaign","attach_recommended_sequence","start_campaign"],"oneTimeWatchLinkPolicy":"After the initial brief handoff, customer-facing turns must not print another watch link unless the user asks or link recovery is needed."},"lazyReferences":{"watch":["references/watch-link-handoff.md","references/watch-guide-narration.md"],"source":["references/lead-validation-preview.md","references/step-13-import-leads.md"],"filter":["references/filter-leads.md"],"message":[],"tail":["references/sample-validation-loop.md","references/step-15-re-cascade.md","references/final-handoff-contract.md"]},"safetyBoundaries":["Do not call list_senders before Settings after message approval.","Do not import leads until the source decision is approved.","Do not queue cells until confirmed source rows exist in the campaign and the message/filter gates are satisfied.","Do not call start_campaign until the user explicitly confirms launch.","Do not use local files as durable state in normal customer runs."],"yoloMode":{"triggerPhrases":["yolo","--yolo","mode=yolo","autopilot","use best guesses","use best estimates","answer for me","just run it"],"identityRequestOnlyWhenMissing":true,"operatorDirectionsRule":"Treat freeform directions supplied at invocation or later as durable operator directions; newest conflicting direction wins.","setInteractionModeAfterCampaignCreate":"autonomous","autoSelectsPreLaunchChoices":["campaign focus","brief approval","source plan","source review/import approval","filters vs skip filters","filter rubric approval","message template approval when recommendation is approve-message","generated message review when quality floor passes","sender selection when exactly one connected sender is safe"],"mustPauseFor":["missing LinkedIn profile URL or handle","missing credentials or required data","ambiguous choice with no reasonable estimate","source/message/filter quality floor failure","final live launch confirmation"],"neverAutoStart":true},"steps":[{"id":"bootstrap","label":"Bootstrap","onEnter":[{"tool":"bootstrap_create_campaign","requiredValues":{"flowVersion":"v2"}},{"tool":"get_subskill_prompt","requiredValues":{"subskillName":"create-campaign-v2"},"purpose":"load the compact entry prompt once"}],"allowedTools":["bootstrap_create_campaign","get_auth_status","get_active_workspace","get_subskill_prompt","AskUserQuestion","request_user_input"],"doNotAllow":["create_campaign","list_senders","save_rubrics","import_leads","confirm_lead_list","update_campaign","queue_cells","start_campaign"],"waitFor":"bootstrap_complete","transitions":{"bootstrap_complete":"brief-interview"}},{"id":"brief-interview","label":"Client, offer, research, and brief","onEnter":[{"action":"resolve_campaign_identity_before_strategy_packet","allowedTools":["fetch_company","fetch_linkedin_profile","WebFetch","WebSearch"],"mustNotInferFromNameOnly":true,"doNotPresentSenderPickerBeforeIdentityInference":true,"fallback":"Ask only: \"What is your LinkedIn profile URL or handle?\" Normalize bare handles or `/in/...` paths to `https://www.linkedin.com/in/{handle}/`. If the input is not a profile, ask again for the person profile URL before strategy questions.","requiresLinkedInProfileUrl":true,"doNotAcceptAsIdentityInput":["non-profile URL","company page","company/name-only input without a profile handle"],"acceptsLinkedInProfileHandle":true,"normalizeLinkedInProfileHandleToUrl":true},{"action":"run_campaign_identity_research_before_strategy_packet","target":"research-sender","requiredCompletion":"complete_sender_research","allowedTools":["get_subskill_prompt","fetch_linkedin_profile","fetch_company","fetch_linkedin_posts","fetch_company_posts","WebFetch","WebSearch","complete_sender_research"],"requiredInput":"LinkedIn profile URL or handle"},{"action":"confirm_target_before_strategy_packet","uses":"request_user_input","singleChoice":true,"question":"Who should we target first?","options":["Use Sellable's researched recommendation","Choose a different buyer","I'm not sure yet"],"copyMustInclude":["research basis","recommendation is editable","who to target"],"requiredOption":"Other / custom","neverCollectOpenTextWithStructuredQuestion":true,"skipIf":"user already supplied a clear target or yolo_mode with any reasonable best-estimate target"},{"action":"confirm_current_offer_before_strategy_packet","uses":"request_user_input","question":"What should we pitch to prospects first?","options":["Use Sellable's researched recommendation","Use my current pitch","Shape the pitch together"],"copyMustInclude":["research basis","public LinkedIn research may be stale","recommendation is editable","pitch to prospects"],"skipIf":"user already supplied a clear current offer or yolo_mode with any reasonable researched recommendation"},{"action":"confirm_trust_proof_before_strategy_packet","uses":"request_user_input","singleChoice":true,"question":"What is the strongest credibility signal we can lead with?","options":["Use Sellable's recommended proof","Use customer proof or a case study","Use founder or company credibility"],"copyMustInclude":["build trust with prospects","recommendation is editable","proof to use or avoid"],"requiredOption":"Other / custom","skipIf":"user already supplied clear proof to use or avoid, or yolo_mode with any reasonable researched proof"},{"action":"choose_prospect_source_path_before_strategy_packet","uses":"request_user_input","singleChoice":true,"question":"How should we find prospects?","options":["Find prospects for me","Use a CSV of LinkedIn profiles","Use a CSV of company domains"],"copyMustInclude":["prospects","no one added yet","find prospects for me"],"skipIf":"user already supplied a source path or yolo_mode with any reasonable source path"},{"action":"infer_strategy_packet_in_yolo_mode","when":"yolo_mode","skipStructuredSetupQuestions":true,"inferFields":["buyer segment","offer/CTA","proof to use or avoid","lead source","filter choice","message direction"],"sourceOfTruth":"identity/company lookup plus operator directions","mustStateAssumptionsInBrief":true},{"action":"render_supplied_setup_receipt_before_brief_when_setup_questions_skip","when":"any setup question is skipped because the user already supplied or yolo mode inferred identity, target, offer, proof, or source","output":"normal chat receipt before the campaign brief or watch-link handoff","copyMustInclude":["Accepted LinkedIn input: normalized to the required LinkedIn profile URL.","Offer path: supplied current offer or Sellable's researched recommendation; you can replace it with your current offer."],"copyMustNotInclude":["Campaign Identity"]},{"action":"create_watchable_campaign_shell_with_v1_brief","tool":"create_campaign","requiredFields":["name","campaignBrief","clientProspectId or senderLinkedinUrl","currentStep","watchNarration"],"requiredValues":{"currentStep":"create-offer","watchNarration.stage":"brief"},"capture":["campaignId","watchUrl"],"canonicalStateWrites":["campaignId","watchUrl","campaignBrief","currentStep:create-offer"]},{"action":"surface_campaign_shell_watch_link","watchUrlSource":"create_campaign.watchUrl","requiredWatchUrlShape":"exact create_campaign.watchUrl; direct /campaign-builder/{campaignId}?mode={claude|codex} URL with workspaceId and token","copyMustInclude":["We'll keep building the campaign in this chat.","You can watch it being built in real time in the app here:","I'll ask for your approval whenever I need your expertise or taste before moving forward.","Send changes here."],"immediateNextMainChatLine":"Ask for brief approval now. After approval, say: \"Brief approved. Next, we'll choose where to find buyers. I won't add anyone yet.\"","codexBrowserHandoff":{"openWhenAvailable":false,"printWatchLinkOnly":true,"tellUserCommandEnterOrClick":false,"mustNotUseBrowserAutomation":true,"fallbackMustNotClaimInspection":true},"copyMustAvoid":["bare /campaign-builder/{campaignId} URL","derived watch URL","second Watch link:","Next, we'll choose where to find buyers"],"oneTimeOnly":true}],"allowedTools":["get_subskill_prompt","fetch_company","fetch_company_posts","fetch_linkedin_profile","fetch_linkedin_posts","complete_sender_research","create_campaign","AskUserQuestion","request_user_input"],"doNotAllow":["list_senders","save_rubrics","import_leads","confirm_lead_list","queue_cells","start_campaign"],"waitFor":["campaign_shell_created","brief_ready","confirm_with_user"],"transitions":{"campaign_shell_created":"brief-review","brief_ready":"brief-review","confirm_with_user":"brief-review"}},{"id":"brief-review","label":"Brief review","onEnter":[{"action":"render_brief_inline","minimumVisibleDetail":"campaign direction, buyer, offer, proof, source plan, safety gates","copyMustInclude":["This brief is based on Sellable's research and your answers.","If your site or LinkedIn is stale","Do you agree with this researched campaign direction?"],"copyMustNotInclude":["quoted first-message copy","Message Angle as final proof","This approval covers","does not approve adding people","does not approve scraping posts","selecting a sender","attaching a sequence","Campaign Identity","what should this campaign sell first","sell first","[from you]","[from LinkedIn]","[from website]","[from case study]","[Sellable recommendation]"],"messageHintRule":"If any message-shape hint remains, keep it as non-final bullets and say real examples come after leads are found and filtered.","approvalBoundaryRule":"Keep this gate positive and short. Do not list later steps this approval does not cover; ask approve/revise and then move to choosing where to find buyers.","sectionLabelRule":"Use ## Sender and Company for the person/company context; never render ## Campaign Identity.","skipIf":"the full brief was already rendered in the current shell-creation handoff before create_campaign returned","renderOnceRule":"The brief must be visible exactly once before approval. Do not render it again immediately after shell creation; append the one watch-link handoff and ask the approval question.","copyMustAvoid":["duplicate brief render after shell creation","second Watch link:"]},{"action":"ask_brief_choice","uses":"request_user_input","choices":["Approve brief","Revise brief","Pause here"],"skipIf":"yolo_mode; auto_continue after rendering assumptions unless brief quality floor fails"}],"requiredCampaignState":["campaignId","watchUrl","campaignBrief","currentStep"],"allowedTools":["AskUserQuestion","request_user_input","update_campaign"],"doNotAllow":["create_campaign","list_senders","import_leads","confirm_lead_list","queue_cells","start_campaign"],"waitFor":["user_brief_confirmed","revise_brief","auto_continue"],"transitions":{"user_brief_confirmed":"find-leads","revise_brief":"brief-interview","auto_continue":"find-leads"}},{"id":"find-leads","label":"Find leads","sourceSelectionFunnel":{"preScoutRecommendationGate":{"required":true,"label":"Find buyers plan approval","mustHappenBefore":["get_provider_prompt","search_signals","search_sales_nav","search_prospeo","fetch_post_engagers","source-scout dispatch"],"show":["buyer groups or places we could check","best place to start","why the right buyers are likely to be there","what signs the next search will check","where to look next if the first place is too thin","what approval authorizes"],"approvalAuthorizes":"looking for the best places to find buyers; no one added yet","explanationMustInclude":["where the right buyers are already talking","why that place fits this campaign","enough likely prospects","what counts as a good sign","where to look next if thin","I won't add anyone yet","choose where to find buyers"],"yoloMode":{"autoApproveRecommendedLane":true,"mustShowAssumedChoice":true,"pauseIfConfidenceLow":true},"customerLanguage":{"readability":"grade-5","requiredHeading":"Find Buyers Plan","prefer":["place to look","best place to start","people to check","prospects","I won't add anyone yet"],"avoid":["lane","source scouting","provider","precision/scale tradeoff","evidence quality","pilot volume","ICP","workflow pain","lead-source scouting","not importing leads"],"example":"Brief approved. Next, we'll choose where to find buyers. I won't add anyone yet. I recommend starting with LinkedIn posts about [topic]; fallback is [place].","termRule":{"buyers":"target market","peopleToCheck":"raw reactions/comments before fit is known","prospects":"likely usable people after fit","leads":"campaign rows"}},"questionOrder":"update_campaign to pick-provider, render ## Find Buyers Plan in normal chat without repeating the watch link, then open the structured approval question; never ask first or show the plan after approval","copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"],"approvalFreshnessRule":"Only an approval question opened after the visible Find Buyers Plan, or an explicit source choice made after that plan, can set source_lane_approved. Do not reuse brief approval or pre-plan provider state."},"defaultWhenSourceUnspecified":["signal-discovery","sales-nav-recent-active","sales-nav-general","prospeo"],"userFacingFallbackOrder":["LinkedIn posts where the right buyers are already talking","people with the right titles who recently posted on LinkedIn","people with the right titles from a broader LinkedIn search","a broader company and contact search"],"parallelAllowedOnlyWhen":["user explicitly requested source comparison","resuming already-started parallel scouts","first viable source is borderline and one cheap fallback check is needed"]},"onEnter":[{"tool":"update_campaign","requiredValues":{"currentStep":"pick-provider","watchNarration.stage":"find-leads","watchNarration.headline":"Review where to find buyers","watchNarration.visibleState":"The app is showing source selection before lead finding starts.","watchNarration.agentIntent":"Codex is explaining where it recommends looking first and where it will look next if that is too thin.","watchNarration.nextAction":"Approve where to look first or choose another place","watchNarration.safety":"Only deciding where to look. No one is added yet."},"purpose":"show the visible source-plan approval checkpoint before provider lanes"},{"tool":"get_source_scout_registry","purpose":"load canonical source scout names before optional branch launch"},{"action":"render_find_buyers_plan_inline","oneShot":true,"requiredPrecondition":"currentStep=pick-provider has been saved with update_campaign","mustHappenAfter":["update_campaign currentStep=pick-provider"],"mustHappenBefore":["ask_find_buyers_plan_choice","get_provider_prompt","search_signals","search_sales_nav","search_prospeo","fetch_post_engagers","source-scout dispatch"],"output":"normal chat text before any request_user_input approval panel","requiredHeading":"Find Buyers Plan","requiredInlineFields":["plain-language buyer groups or places for this campaign","best place to start","why the right buyers are likely to be there","what signs the next search will check","where to look next if the first place is too thin","what approval authorizes"],"copyMustInclude":["Find Buyers Plan","best place to start","I won't add anyone","choose where to find buyers","I won't add anyone yet"],"copyMustAvoid":["lane","source scouting","provider","precision/scale tradeoff","evidence quality","pilot volume","ICP","workflow pain","lead-source scouting","not importing leads","Watch link:","campaign-builder URL","repeat the watch URL"],"approvalBoundary":"Explain what approval authorizes before asking: looking for the best places to find buyers; no one is added yet.","linkPolicy":"Do not include another watch link. The first brief handoff already gave the user the live app URL."},{"action":"ask_find_buyers_plan_choice","uses":"request_user_input","oneShot":true,"requiredPrecondition":"render_find_buyers_plan_inline completed in normal chat after pick-provider state was saved; no earlier approval can satisfy this","skipIf":"source_lane_approved was set by ask_find_buyers_plan_choice after the visible Find Buyers Plan, leadSourceProvider was explicitly chosen by the user after that plan, or yolo_mode auto-selected source_lane_approved after showing the assumed choice","choices":["Start with LinkedIn posts","Start with active LinkedIn profiles","Start with company/contact search","Choose a different place","Pause here"],"approvalChoiceLabelsByProvider":{"signal-discovery":"Start with LinkedIn posts","sales-nav":"Start with active LinkedIn profiles","prospeo":"Start with company/contact search"},"approvalState":"source_lane_approved","copyMustInclude":["Approve this plan for where to find buyers?","Start with LinkedIn posts"],"mustNotHappenBefore":["render_find_buyers_plan_inline"],"approvalStateRule":"Set source_lane_approved only from this question after the visible plan, never from brief approval, generic Approve plan, or pre-plan provider state."},{"action":"persist_provider_search_step_before_search","tool":"update_campaign","requiredPrecondition":"source_lane_approved set after the visible Find Buyers Plan approval question","providerCurrentStepMap":{"signal-discovery":"signal-discovery","sales-nav":"sales-nav","prospeo":"prospeo"},"requiredValues":{"leadSourceType":"new","leadSourceProvider":"approved provider","currentStep":"provider-specific current step","watchNarration.stage":"find-leads","watchNarration.headline":"Searching the approved place","watchNarration.safety":"Looking only. No one is added yet."},"mustRunBefore":["search_signals","search_sales_nav","search_prospeo","fetch_post_engagers"]},{"action":"run_sequential_source_funnel","requiredPrecondition":"source_lane_approved set after the visible Find Buyers Plan approval question","defaultOrder":["source-scout-linkedin-engagement","source-scout-sales-nav","source-scout-prospeo-contact"],"campaignOfferIdRequired":true,"stopOnFirstViableUnlessComparisonRequested":true}],"requiredCampaignState":["campaignId","campaignBrief","currentStep"],"allowedTools":["get_source_scout_registry","get_provider_prompt","lookup_sales_nav_filter","search_sales_nav","search_prospeo","search_signals","select_promising_posts","fetch_post_engagers","fetch_company","fetch_linkedin_profile","load_csv_linkedin_leads","load_csv_domains","get_rows_minimal","update_campaign","AskUserQuestion","request_user_input","get_campaign_navigation_state"],"doNotAllow":["create_campaign","list_senders","save_rubrics","import_leads","confirm_lead_list","queue_cells","start_campaign"],"waitFor":["lead_review_ready","revise_brief","confirm_with_user"],"transitions":{"lead_review_ready":"lead-review","revise_brief":"brief-interview","confirm_with_user":"lead-review"}},{"id":"lead-review","label":"Source approval","onEnter":[{"action":"show_source_decision_card","requiredInlineFields":["primary source and exact filters/recipe","specific source action awaiting approval","for Signal Discovery: compact Source Recommendation in plain language with selected posts, people to check, likely prospects, first review, and fallback","for Signal Discovery: recommended scrape post count and target people-to-check volume","first campaign review size, clearly separate from source sampling","runner-up and why it lost","raw volume","sampled people","sampled fits as n/N plus percentage/range","estimated usable prospects","cleanup risk","what will happen after approval"],"copyMustAvoid":["lead-source scouting","source scouting","headline-fit","engagers needed","execution slice","ICP","good buyers","real buyers","This approval covers","It does not approve","does not approve filters","filters, messages, sender selection","sender selection, sequence, or sending","Watch link:","campaign-builder URL","repeat the watch URL"],"approvalBoundaryRule":"Use positive source-action copy only. Include: \"After approval, I will build the source list, add it to the campaign, and review the first 15 leads before we scale.\" Never list future non-approvals.","linkPolicy":"Do not include another watch link in Source Recommendation; describe the source decision only."},{"action":"ask_source_review_choice","uses":"request_user_input","choices":["Approve scraping N recommended LinkedIn posts","Run the approved source import","Revise source","Pause here"],"approvalChoiceLabelsByProvider":{"signal-discovery":"Approve scraping {scrapePostCount} recommended LinkedIn posts?","sales-nav":"Import the approved Sales Nav source list","prospeo":"Import the approved Prospeo source list"},"postApprovalContract":{"singleUseApproval":true,"doNotRepeatAfterApproval":["Source Recommendation","show_source_decision_card","ask_source_review_choice"],"requiredNextActionAfterApproval":"ack once; call import_leads immediately","signalDiscoveryNextTool":"import_leads({ provider: \"signal-discovery\", targetEngagerCount, maxPostsToScrape, confirmed: true })"},"autoSelectIf":"yolo_mode and projected usable pool clears the source quality floor","copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"]}],"requiredCampaignState":["campaignId","campaignBrief","providerSearchAssociation"],"allowedTools":["AskUserQuestion","request_user_input","update_campaign"],"doNotAllow":["create_campaign","list_senders","save_rubrics","import_leads","confirm_lead_list","queue_cells","start_campaign"],"waitFor":["lead_review_confirmed","revise_leads","confirm_with_user","auto_continue"],"transitions":{"lead_review_confirmed":"auto-execute-leads","revise_leads":"find-leads","confirm_with_user":"auto-execute-leads","auto_continue":"auto-execute-leads"}},{"id":"auto-execute-leads","label":"Materialize confirmed source list","currentStepValue":"auto-execute-leads","reference":"references/step-13-import-leads.md","onEnter":[{"tool":"get_subskill_prompt","requiredValues":{"subskillName":"create-campaign-v2-tail"},"purpose":"load tail contract before execution tools"},{"tool":"import_leads","requiredFields":["campaignOfferId","selected source/list","sourceListTarget or targetEngagerCount"],"requiredValues":{"salesNavProspeoDefaultSourceListTarget":1000,"signalDiscoveryDefaultEngagerTarget":1500},"modeAddHandshake":{"firstCallReturns":"needsModeSelection when adding to an existing campaign-attached list","requiredFollowup":"retry with mode=add after explicit user/source-state compatibility is confirmed"},"surfaceDedupRatio":true},{"tool":"wait_for_lead_list_ready","purpose":"poll until the source lead-list import reaches a terminal complete/failed/cancelled state before copying rows into the campaign table","repeatUntil":"ready_true_or_cancelled_or_import_failed","requiredValues":{"requireComplete":true},"onImportStillRunning":"re-run wait_for_lead_list_ready; do not call confirm_lead_list just because rows exist","partialOverrideRule":"Only if the user explicitly asks to keep going early may the next confirm_lead_list call pass allowPartialSourceList: true."},{"tool":"confirm_lead_list","requiredFields":["campaignOfferId","selectedLeadListId","reviewBatchLimit"],"requiredValues":{"reviewBatchLimit":15},"capture":["workflowTableId","reviewBatchRowIds"],"requiredPrecondition":"wait_for_lead_list_ready returned ready:true, unless the user explicitly asked to keep going early with a partial list","partialOverrideField":"allowPartialSourceList:true only after explicit user early-continue instruction","mustNotRunWhen":["wait_for_lead_list_ready reason=import_still_running and no explicit early-continue instruction","wait_for_lead_list_ready reason=cancelled","wait_for_lead_list_ready reason=import_failed","missing import job metadata"]},{"tool":"wait_for_campaign_table_ready"},{"tool":"get_rows_minimal","requiredValues":{"tableId":"{workflowTableId}","limit":15},"capture":["reviewBatchRowHash"]},{"action":"summarize_review_batch_and_advance_to_filter_choice","purpose":"ask filter choice immediately; no post-lead registries, filter refs, or message prompts first","maxCustomerCopyLines":4,"copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"],"linkPolicy":"Ask add filters vs skip filters without repeating the watch link."}],"requiredCampaignState":["campaignId","providerSearchAssociation","selectedLeadListId"],"allowedTools":["get_subskill_prompt","import_leads","wait_for_lead_list_ready","confirm_lead_list","wait_for_campaign_table_ready","get_rows_minimal","update_campaign","AskUserQuestion","request_user_input"],"doNotAllow":["get_post_find_leads_scout_registry","Task","spawn_agent","list_senders","queue_cells","start_campaign","enrich_with_prospeo","bulk_enrich_with_prospeo","save_rubrics"],"waitFor":"source_list_confirmed_and_review_sample_ready","transitions":{"source_list_confirmed_and_review_sample_ready":"filter-choice","escalation_triggered":"escalation"},"hardRules":["import_leads_then_repeated_wait_for_lead_list_ready_then_confirm_lead_list_then_wait_for_campaign_table_ready","partial_source_rows_do_not_unblock_confirm_lead_list_without_explicit_user_early_continue","cancelled_or_failed_source_import_stops_before_campaign_table_copy"]},{"id":"filter-choice","label":"Filter choice","currentStepValue":"filter-choice","onEnter":[{"tool":"update_campaign","requiredValues":{"currentStep":"filter-choice","watchNarration.stage":"fit-message"}},{"action":"ask_filter_choice","uses":"request_user_input","choices":["Use filters","Skip filters","Revise source"],"autoSelectIf":"yolo_mode; choose filters unless source is tightly curated or user directed otherwise","copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"],"copyMustInclude":["source rows are in the campaign","add fit filters before message generation or skip filters"]}],"hardRules":["ask_filter_choice_immediately_after_review_batch_import","do_not_call_get_subskill_prompt_before_filter_choice","do_not_call_get_subskill_asset_before_filter_choice","do_not_call_get_post_find_leads_scout_registry_before_filter_choice","do_not_spawn_post_lead_agents_before_filter_choice"],"requiredCampaignState":["campaignId","campaignBrief","selectedLeadListId","workflowTableId"],"allowedTools":["AskUserQuestion","request_user_input","update_campaign","get_campaign_navigation_state"],"doNotAllow":["get_subskill_prompt","get_subskill_asset","get_post_find_leads_scout_registry","Task","spawn_agent","create_campaign","list_senders","import_leads","confirm_lead_list","queue_cells","start_campaign","generate_messages"],"waitFor":["filters_enabled","filters_skipped","revise_leads"],"transitions":{"filters_enabled":"post-lead-workstreams","filters_skipped":"message-generation","revise_leads":"find-leads"}},{"id":"post-lead-workstreams","label":"Filter workstream","onEnter":[{"action":"persist_add_filters_approval","tool":"update_campaign","when":"filters_enabled","requiredFields":["campaignId","enableICPFilters","currentStep","watchNarration"],"requiredValues":{"enableICPFilters":true,"currentStep":"create-icp-rubric","watchNarration.stage":"fit-message","watchNarration.headline":"Create filter rules","watchNarration.visibleState":"Filters are enabled and the browser is showing Filter Rules while Codex defines the rubric.","watchNarration.nextAction":"Review saved filter rules"},"mustRunBefore":["get_post_find_leads_scout_registry","launch_lead_fit_builder_after_filter_choice","save_rubrics"]},{"tool":"get_post_find_leads_scout_registry","purpose":"load canonical post-lead worker names only after the user has chosen filters"},{"action":"launch_lead_fit_builder_after_filter_choice","mode":"parallel_when_host_supports_subagents","target":"post-find-leads-filter-scout","inputs":["campaignId","campaignBrief","selectedLeadListId","workflowTableId","reviewBatchRowIds/hash","filterChoice"],"stateSource":"live campaign/table","debugFilesOptionalOnly":true},{"action":"launch_message_draft_builder_after_filter_decision","mode":"parallel_when_host_supports_subagents","target":"post-find-leads-message-scout","when":"filters_enabled","doesNotQueueCells":true,"customerNarration":"Say message agent is drafting."},{"action":"save_filter_rubrics_to_campaign","tool":"save_rubrics","when":"filters_enabled and rubrics are production-shaped","requiredFields":["campaignOfferId","leadScoringRubrics"],"writesCampaignState":"leadScoringRubrics","requiredSideEffects":{"enableICPFilters":true,"currentStep":"create-icp-rubric","watchNarration.headline":"Filter rules saved for review"}},{"action":"ask_filter_rubric_review_choice","uses":"request_user_input","choices":["Approve filters","Revise filters","Pause"],"purpose":"let the user read saved rubrics before Filter Leads or enrichment","copyMustInclude":["These rules prevent wasted sends before I score/import the list.","Recommended because of the researched ICP, source sample, and repeated false-positive patterns.","one pass example and one block example when available","approval is for the filter rules","Approve these filter rules."],"autoSelectIf":"yolo_mode and rubrics are production-shaped","copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"]}],"requiredCampaignState":["campaignId","campaignBrief","selectedLeadListId","workflowTableId"],"allowedTools":["get_subskill_prompt","get_subskill_asset","get_post_find_leads_scout_registry","save_rubrics","get_campaign","get_rows_minimal","update_campaign","get_campaign_navigation_state","AskUserQuestion","request_user_input","Task","spawn_agent"],"doNotAllow":["create_campaign","list_senders","import_leads","confirm_lead_list","queue_cells","start_campaign","check_rubric","generate_messages"],"waitFor":["filter_rubrics_approved","revise_leads","revise_rubric","revise_messaging"],"hardRules":["after_save_rubrics_currentStep_must_stay_create-icp-rubric_until_filter_approval","filter_approval_required_before_apply-icp-rubric_or_queue_campaign_cells","do_not_move_browser_to_messages_until_filter_leads_step_is_current_or_filters_are_explicitly_skipped","no_post_lead_worker_or_deep_prompt_before_filter_choice","lead_fit_builder_starts_only_after_filters_enabled","msg_draft_after_filter_choice","msg_draft_no_cells"],"transitions":{"filter_rubrics_approved":"message-generation","revise_leads":"find-leads","revise_rubric":"filter-rubric","revise_messaging":"message-generation","confirm_with_user":"message-review"}},{"id":"filter-rubric","label":"Rubric revision","onEnter":[{"tool":"get_subskill_asset","requiredValues":{"subskillName":"create-campaign-v2","assetPath":"references/filter-leads.md"}},{"tool":"save_rubrics","requiredFields":["campaignOfferId","leadScoringRubrics"],"writesCampaignState":"leadScoringRubrics","requiredSideEffects":{"enableICPFilters":true,"currentStep":"create-icp-rubric","watchNarration.headline":"Filter rules saved for review"}},{"action":"ask_filter_rubric_review_choice","uses":"request_user_input","choices":["Approve filters","Revise filters","Pause"],"copyMustInclude":["These rules prevent wasted sends before I score/import the list.","approval object is filter rules only","approval is for the filter rules","Approve these filter rules."],"autoSelectIf":"yolo_mode and rubrics are production-shaped","copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"]}],"requiredCampaignState":["campaignId","workflowTableId"],"allowedTools":["get_subskill_asset","save_rubrics","AskUserQuestion","request_user_input"],"doNotAllow":["create_campaign","list_senders","import_leads","confirm_lead_list","update_campaign","queue_cells","start_campaign","check_rubric"],"waitFor":["filter_rubrics_approved","revise_leads","confirm_with_user","auto_continue"],"transitions":{"filter_rubrics_approved":"message-generation","revise_leads":"find-leads","confirm_with_user":"message-generation","auto_continue":"message-generation"}},{"id":"message-generation","label":"Message generation","onEnter":[{"action":"set_message_review_visible_step_by_filter_choice","tool":"update_campaign","branchRules":["yes after filter approval: currentStep=apply-icp-rubric; wait on Filter Leads","no: currentStep=messages; message review"],"watchNarration.stage":"fit-message"},{"action":"run_or_reconcile_message_draft_builder","target":"post-find-leads-message-scout","toolCallRequiredBeforeDraft":["run background post-find-leads-message-scout when available","get_subskill_prompt({ subskillName: \"generate-messages-compact\" }) for the required normal-path contract","load generate-messages-compact/references/examples-critique-revision.md only for critique, revision, or close-call examples"],"stateSource":"campaignBrief, source, selectedLeadListId, workflowTableId, execution-slice row ids/hash","outputState":"messageDraftRecommendation","revisionMode":{"when":"revise_messaging or latest user message requests template changes","requiredInputs":["latest user feedback","current messageDraftRecommendation","campaign/table execution-slice state"],"output":"revised messageDraftRecommendation rendered before request_user_input"}}],"requiredCampaignState":["campaignId","campaignBrief","selectedLeadListId","workflowTableId"],"allowedTools":["get_subskill_prompt","get_campaign","get_rows_minimal","update_campaign"],"toolRules":["Run post-find-leads-message-scout when available; otherwise load get_subskill_prompt({ subskillName: \"generate-messages-compact\" }) from live state and lazy examples only when needed.","brief.md, lead-review.md, and lead-sample.json are optional debug context only.","messageDraftRecommendation returns templateRecommendation, tokenFillRules, renderedSample, concerns, status, basisToken, outputAt, outputHash, and error/retry detail.","If campaign/source/table/execution-slice basis does not match, classify the output stale or blocked."],"doNotAllow":["create_campaign","list_senders","save_rubrics","import_leads","confirm_lead_list","queue_cells","start_campaign","generate_messages","AskUserQuestion","request_user_input"],"waitFor":["message_validation_ready","revise_rubric","revise_messaging"],"transitions":{"message_validation_ready":"message-review","revise_rubric":"filter-rubric","revise_messaging":"message-generation"},"revisionLoop":{"trigger":"user gives copy feedback, asks for an update, or chooses revise-messaging","required":["apply latest user feedback to current messageDraftRecommendation and live campaign/table state","produce a new messageDraftRecommendation with revisionNotes","render revised ## Message Template, ## Rendered Example, and What changed before asking approval"],"blocked":["asking whether an unseen new version is better","reusing the old template after acknowledging feedback","update_campaign_brief before approve-message","request_user_input or AskUserQuestion during message-generation"],"outputState":"messageDraftRecommendation"},"hardRules":["message_revision_must_render_new_template_before_approval_question","message_revision_saves_only_after_approve_message","message_generation_must_not_call_request_user_input","message_generation_must_transition_to_message_review_only_after_renderable_draft_state"]},{"id":"message-review","label":"Message review","onEnter":[{"action":"render_message_review_from_state","requiredState":["campaignBrief","selectedLeadListId","workflowTableId","messageDraftRecommendation"],"requiredVisibleLabels":["## Message Template","## Rendered Example","Good token fill:","My take:","Question: approve-message or revise-messaging?","Recommendation:"],"doNotShowByDefault":["Token Notes","Good omit / fallback","Bad fill to avoid","Token Adherence Table"],"internalPersistenceOnly":["Token Fill Rules","Token Fill Examples","fallback guidance","bad-fill avoidance notes"],"copyMustInclude":["QA receipt before approval","tokens resolved","company/person match","proof claim","prospect angle","language/tone when known","obvious bad fits","Would I take this call?","weak personalization can burn the sender's reputation","full list remains paused"],"copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"],"revisionRenderRule":"After revise_messaging, show revised template, rendered example, and What changed before ask_message_review_choice; never ask if an unseen version is better."},{"action":"ask_message_review_choice","uses":"request_user_input","choices":["approve-message","revise-messaging"],"copyMustInclude":["approve this message template and continue","nothing sends from this approval"],"autoSelectIf":"yolo_mode and Recommendation is approve-message; revise autonomously when Recommendation is revise-messaging","copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"],"requiredPrecondition":"current or revised template and rendered example are visible in this turn","revisionPrecondition":"after revise_messaging, revised template is visible before this question"},{"action":"sync_approved_message_set_to_campaign_brief","tool":"update_campaign_brief","when":"after approve-message","requiredFields":["campaignId","approvedMessageTemplate","Token Fill Rules","Token Fill Examples"],"writesCampaignState":"approvedMessageTemplate","revisionApprovalRule":"If the user requested revisions, write only the latest approved revised template and token rules to campaignBrief."}],"requiredCampaignState":["campaignId","workflowTableId","messageDraftRecommendation"],"allowedTools":["AskUserQuestion","request_user_input","update_campaign_brief","update_campaign","get_rows_minimal"],"doNotAllow":["create_campaign","list_senders","save_rubrics","import_leads","confirm_lead_list","start_campaign"],"waitFor":["message_approved","revise_messaging"],"transitions":{"message_approved":"validate-sample","revise_messaging":"message-generation"}},{"id":"validate-sample","label":"Validate campaign-table execution slice","currentStepValue":"apply-icp-rubric","reference":"references/sample-validation-loop.md","visibleStepRule":"Filter Leads is reached only after saved-filter approval; on approve-message, save the template and queue bounded Enrich Prospect cells through selector-based campaign processing.","onEnter":[{"tool":"update_campaign","requiredValues":{"currentStep":"apply-icp-rubric","watchNarration.stage":"fit-message"},"purpose":"keep the watched UI on Filter Leads while the approved template starts the bounded cascade","watchNarrationRule":"Say the template is saved and Filter Leads is now running the bounded enrichment and scoring pass."},{"tool":"queue_campaign_cells","purpose":"queue Enrich Prospect cells for the stored review batch without fetching bulky row payloads","requiredFields":["workflowTableId","columnRole","rowSelector"],"requiredValues":{"columnRole":"enrich","rowSelector":{"type":"reviewBatch"},"forceRerun":false},"targetCountSource":"WorkflowTable.config.mcp.reviewBatch.rowCount (default 15)"},{"tool":"wait_for_campaign_processing","purpose":"wait_for_first_passed_review_batch_row","requiredFields":["workflowTableId","minPassedCount"],"requiredValues":{"minPassedCount":1},"readVia":"stats_only_tool_result","timeoutGuidance":"If this times out, do not repoll identical args; use partial diagnostics to revise filters or surface blocked sample state.","customerSummaryPattern":["{checked} leads checked","{passed} passed fit scoring","{blocked} blocked before sending","full list remains paused until approval"]},{"action":"handle_partial_or_timeout_sample","customerStatus":"sample-needs-revision","rule":"Do not repeat waits indefinitely; surface partial status and route to revision.","copyMustInclude":"completed, passed, pending, blocked before sending, and full list remains paused"}],"requiredCampaignState":["campaignId","workflowTableId","approvedMessageTemplate","filterRubricsApproved when enableICPFilters=true"],"allowedTools":["get_subskill_asset","queue_campaign_cells","select_campaign_cells","get_campaign_table_schema","wait_for_campaign_processing","update_campaign","AskUserQuestion","request_user_input"],"doNotAllow":["import_leads","list_senders","start_campaign","enrich_with_prospeo","bulk_enrich_with_prospeo","check_rubric"],"hardRules":["campaign_processing_waits_are_stats_only_by_default","queue_review_batch_by_selector_never_fetch_rows_for_cell_ids","timeout_never_repeats_without_customer_handoff","timeout_or_underfloor_sample_never_advances_to_settings"],"waitFor":["sample_validated","sample_revision_required"],"transitions":{"sample_validated":"auto-execute-messaging","sample_revision_required":"lead-review","revise_leads":"find-leads","revise_rubric":"filter-rubric","escalation_triggered":"escalation"}},{"id":"auto-execute-messaging","label":"Generate initial campaign-row messages","currentStepValue":"auto-execute-messaging","references":["references/parallel-critique-protocol.md","references/thomas-variant-selection.md","references/thomas-revision-filters.md","references/step-15-re-cascade.md"],"onEnter":[{"tool":"queue_campaign_cells","batchSize":100,"when":"any passing row has pending, empty, or stale Generate Message cell","requiredFields":["workflowTableId","columnRole","rowSelector"],"requiredValues":{"columnRole":"generateMessage","rowSelector":{"type":"needsGeneratedMessage"},"forceRerun":false},"cellSource":"selector-engine needsGeneratedMessage rows using approved campaign brief template"},{"tool":"wait_for_campaign_processing","purpose":"wait_for_first_current_revision_generated_message","requiredValues":{"minGeneratedMessages":1,"templateRevision":"current"},"readVia":"stats_only_tool_result","invariant":"A generated message implies the pass gate; stale or wrong-revision messages are excluded from readiness."},{"action":"observe_generate_message_results","reference":"references/step-15-re-cascade.md"},{"action":"enforce_token_contract","modeFromConfig":"messaging.tokenContract"},{"action":"optional_critique_pass","enabledFromConfig":"messaging.critique.enabled","plan":"85-03","protocol":"references/parallel-critique-protocol.md","sampleSizeFromConfig":"messaging.critique.sampleSize","budgetUsdCapFromConfig":"messaging.critique.budgetUsdCap","perCriticTimeoutFromConfig":"messaging.critique.perCriticTimeoutSeconds","totalTimeoutFromConfig":"messaging.critique.totalTimeoutSeconds","criticsFromConfig":"messaging.critique.critics","enforceFinalizerPassFromConfig":"messaging.critique.synthesis.enforceFinalizerPass","rejectOnFakeProofFromConfig":"messaging.critique.rejectOnFakeProof","rejectOnUnsupportedTokenFromConfig":"messaging.critique.rejectOnUnsupportedToken","onBudgetTrip":"halt_critique_continue_plain_tail","onPerCriticTimeout":"drop_critic_proceed_with_remaining","onTotalTimeout":"persist_plain_message_for_row","onTokenContractViolation":"persist_plain_message_for_row","onFakeProof":"persist_plain_message_for_row"},{"action":"optional_opus_subset","enabledFromConfig":"messaging.critique.opus.enabled","selection":"references/thomas-variant-selection.md","maxMessagesPerPassFromConfig":"messaging.critique.opus.maxMessagesPerPass","budgetUsdCapFromConfig":"messaging.critique.opus.budgetUsdCap","onOpusBudgetTrip":"halt_opus_continue_non_opus","onOpusTokenContractViolation":"fallback_to_non_opus_rewrite"},{"tool":"update_campaign","requiredValues":{"currentStep":"auto-execute-messaging","watchNarration.stage":"review-ready"},"watchNarrationRule":"Say the first passing generated message is ready in Messages; next is review before Settings."},{"action":"ask_generated_message_review_choice","uses":"request_user_input","choices":["Approve reviewed draft rows and continue to Settings","Revise filters","Revise message template","Pause here"],"copyMustInclude":["approve the reviewed draft rows and continue to Settings","tokens resolved","company/person match","proof claim","prospect angle","language/tone when known","obvious bad fits","Would I take this call?","weak personalization can burn the sender's reputation","full list remains paused","nothing sends from this approval"],"autoSelectIf":"yolo_mode and the first passing generated message clears the quality floor","copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"]}],"allowedTools":["get_subskill_asset","get_campaign_table_schema","select_campaign_cells","queue_campaign_cells","wait_for_campaign_processing","revise_message_template_and_rerun","update_campaign","AskUserQuestion","request_user_input"],"doNotAllow":["import_leads","list_senders","start_campaign","generate_messages"],"hardRules":["generated_message_count_excludes_stale_or_wrong_revision_messages","message_template_revision_uses_brief_update_then_generate_message_rerun","do_not_overwrite_generated_message_cells_directly","critique_failure_never_escalates","critique_sample_size_bounded_by_config","first_passing_generated_message_unblocks_review","critics_fixed_at_targeting_copy_voice","synthesis_enforces_phase_84_token_contract","opus_reserved_for_highest_value_subset","proposed_token_never_persisted_in_rewrite"],"waitFor":["generated_messages_approved","sample_revision_required"],"transitions":{"generated_messages_approved":"awaiting-user-greenlight","revise_filters":"filter-rubric","revise_messaging":"message-generation","escalation_triggered":"escalation"}},{"id":"awaiting-user-greenlight","label":"Settings, sender, sequence, and greenlight","currentStepValue":"awaiting-user-greenlight","reference":"references/final-handoff-contract.md","onEnter":[{"tool":"get_campaign","purpose":"verify senderIds and sequence state before final handoff"},{"tool":"list_senders","purpose":"surface available connected senders only at Settings"},{"tool":"update_campaign","requiredValues":{"currentStep":"settings","watchNarration.stage":"review-ready"},"purpose":"park the watched UI on Settings before sender selection","watchNarrationRule":"Say message review is complete and the app is on Settings for sender choice. Mention lead research/filtering already happened outside the user's LinkedIn account and nothing sends until Start."},{"action":"surface_sender_and_slack_handoff","requiredVisibleContent":["connect or select a LinkedIn sender","Lead research and filtering already happened outside your LinkedIn account.","This account is only used for approved sending after final launch.","Nothing sends until the final Start step.","Slack reply review","recommended sequence","final launch confirmation is still ahead"],"copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"]},{"action":"ask_sender_selection","uses":"request_user_input","singleChoice":true,"choices":["Use this connected sender","Connect a different sender in Settings","Pause here"],"autoSelectIf":"yolo_mode and exactly one safe connected sender is available","copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"]},{"action":"attach_selected_sender","tool":"update_campaign","when":"user selected an available connected sender","requiredValues":{"senderIds":["{selectedSenderId}"],"currentStep":"sequence","watchNarration.stage":"review-ready"},"watchNarrationRule":"Say the sender was attached and the app is moving to Sequence review; sequence remains editable and nothing sends until Start."},{"tool":"attach_recommended_sequence","when":"after senderIds are attached","requiredValues":{"campaignId":"{campaignId}","currentStep":"send","watchNarration.stage":"review-ready"},"watchNarrationRule":"The sequence tool owns this visible beat: say the recommended follow-up sequence is attached, the app is on final launch review, and it is still not sending until Start."},{"action":"ask_final_launch_greenlight","uses":"request_user_input","singleChoice":true,"choices":["Start campaign","Review campaign first","Pause here"],"copyMustInclude":["quality confidence means sample messages and prospect angle were checked","launch confidence means sender, sequence, and Start are ready","approved messages begin sending according to the sequence","replies and meetings follow connected settings","you can monitor and pause","no hidden extra approval disappears after clicking Start"],"onUserStart":"claude-greenlight","neverAutoSelectInYolo":true,"copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"]}],"allowedTools":["get_campaign","get_campaign_navigation_state","list_senders","update_campaign","attach_recommended_sequence","AskUserQuestion","request_user_input"],"doNotAllow":["start_campaign","import_leads"],"autoStart":false,"watchRequired":true,"waitFor":["sender_connection_required","sender_attached","sequence_attached","ready_to_launch","user_greenlight","ui_start_detected"],"transitions":{"sender_connection_required":"awaiting-user-greenlight","sender_attached":"awaiting-user-greenlight","sequence_attached":"awaiting-user-greenlight","ready_to_launch":"awaiting-user-greenlight","user_greenlight":"claude-greenlight","ui_start_detected":"running"}},{"id":"claude-greenlight","label":"Explicit launch","reference":"references/final-handoff-contract.md","onEnter":[{"tool":"get_campaign","purpose":"detect_already_running"},{"action":"verify_sequence_and_current_step_before_start","requiredState":["workflowTableId","senderIds","sequenceTemplate","at least one approved generated message","currentStep in awaiting-user-greenlight|claude-greenlight|send"]},{"tool":"start_campaign","requiredFields":["campaignId"],"persistsCurrentStep":"running","watchNarrationRule":"After start_campaign succeeds, the running state must say the final greenlight was accepted, the campaign is now live/running, and the user can watch progress from the campaign."}],"allowedTools":["get_campaign","attach_recommended_sequence","start_campaign","AskUserQuestion","request_user_input"],"watchRequired":true,"waitFor":"campaign_started","transitions":{"campaign_started":"running"}},{"id":"running","label":"Campaign is live","currentStepValue":"running","onEnter":[{"action":"surface_campaign_live_confirmation_without_watch_link","copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"]}],"allowedTools":["get_campaign","AskUserQuestion","request_user_input"],"doNotAllow":["start_campaign"],"terminal":true},{"id":"escalation","label":"Escalation","reference":"references/escalation-ladder.md","allowedTools":["AskUserQuestion","request_user_input"],"doNotAllow":["start_campaign","import_leads"],"transitions":{"revise_brief":"brief-interview","revise_leads":"find-leads","revise_rubric":"filter-rubric","revise_messaging":"message-generation","abort":"abort"}},{"id":"abort","label":"Abort","allowedTools":["AskUserQuestion","request_user_input"],"terminal":true}]}
|
|
1
|
+
{"version":"v2.1-compact","workflow":"create-campaign-v2","principle":"CampaignOffer state and watch link are canonical. Create the watched shell, approve source, confirm the source list, process the first campaign-table slice, approve filters/message, then run the bounded cascade before Settings and explicit start.","normalCustomerPath":"Use campaign state, MCP responses, and concise watchNarration. Do not create, read, link, or surface local draft files. Print the exact tokenized create_campaign.watchUrl only once at the initial brief handoff.","legacyCompatibility":{"validationSubskill":"create-campaign-v2-validation","tailSubskill":"create-campaign-v2-tail","rule":"Legacy validation/rehearsal files are opt-in diagnostics only and are not active flow gates."},"commitGateChoices":["approve","revise-brief","revise-leads","revise-rubric","revise-messaging","abort"],"canonicalStateFields":["campaignId","watchUrl","campaignBrief","currentStep","watchNarration","interactionMode","providerSearchAssociation","selectedLeadListId","workflowTableId","filterChoice","leadScoringRubrics","messageDraftRecommendation","approvedMessageTemplate","senderIds","sequenceTemplate","runningState"],"watchNarrationTransitionContract":{"rule":"Every watched currentStep switch must include fresh watchNarration.","requiredFields":["stage","headline","visibleState","agentIntent","nextAction"],"copyMustInclude":["what just happened","current visible page/state","next user action"],"appliesToTools":["create_campaign","update_campaign","attach_recommended_sequence","start_campaign"],"oneTimeWatchLinkPolicy":"After the initial brief handoff, customer-facing turns must not print another watch link unless the user asks or link recovery is needed."},"lazyReferences":{"watch":["references/watch-link-handoff.md","references/watch-guide-narration.md"],"source":["references/lead-validation-preview.md","references/step-13-import-leads.md"],"filter":["references/filter-leads.md"],"message":[],"tail":["references/sample-validation-loop.md","references/step-15-re-cascade.md","references/final-handoff-contract.md"]},"safetyBoundaries":["Do not call list_senders before Settings after message approval.","Do not import leads until the source decision is approved.","Do not queue cells until confirmed source rows exist in the campaign and the message/filter gates are satisfied.","Do not call start_campaign until the user explicitly confirms launch.","Do not use local files as durable state in normal customer runs."],"yoloMode":{"triggerPhrases":["yolo","--yolo","mode=yolo","autopilot","use best guesses","use best estimates","answer for me","just run it"],"identityRequestOnlyWhenMissing":true,"operatorDirectionsRule":"Treat freeform directions supplied at invocation or later as durable operator directions; newest conflicting direction wins.","setInteractionModeAfterCampaignCreate":"autonomous","autoSelectsPreLaunchChoices":["campaign focus","brief approval","source plan","source review/import approval","filters vs skip filters","filter rubric approval","message template approval when recommendation is approve-message","generated message review when quality floor passes","sender selection when exactly one connected sender is safe"],"mustPauseFor":["missing LinkedIn profile URL or handle","missing credentials or required data","ambiguous choice with no reasonable estimate","source/message/filter quality floor failure","final live launch confirmation"],"neverAutoStart":true},"steps":[{"id":"bootstrap","label":"Bootstrap","onEnter":[{"tool":"bootstrap_create_campaign","requiredValues":{"flowVersion":"v2"}},{"tool":"get_subskill_prompt","requiredValues":{"subskillName":"create-campaign-v2"},"purpose":"load the entry prompt once"}],"allowedTools":["bootstrap_create_campaign","get_auth_status","get_active_workspace","get_subskill_prompt","AskUserQuestion","request_user_input"],"doNotAllow":["create_campaign","list_senders","save_rubrics","import_leads","confirm_lead_list","update_campaign","queue_cells","start_campaign"],"waitFor":"bootstrap_complete","transitions":{"bootstrap_complete":"brief-interview"}},{"id":"brief-interview","label":"Client, offer, research, and brief","onEnter":[{"action":"resolve_campaign_identity_before_strategy_packet","allowedTools":["fetch_company","fetch_linkedin_profile","WebFetch","WebSearch"],"mustNotInferFromNameOnly":true,"doNotPresentSenderPickerBeforeIdentityInference":true,"fallback":"Ask only: \"What is your LinkedIn profile URL or handle?\" Normalize bare handles or `/in/...` paths to `https://www.linkedin.com/in/{handle}/`. If the input is not a profile, ask again for the person profile URL before strategy questions.","requiresLinkedInProfileUrl":true,"doNotAcceptAsIdentityInput":["non-profile URL","company page","company/name-only input without a profile handle"],"acceptsLinkedInProfileHandle":true,"normalizeLinkedInProfileHandleToUrl":true},{"action":"run_campaign_identity_research_before_strategy_packet","target":"research-sender","requiredCompletion":"complete_sender_research","allowedTools":["get_subskill_prompt","fetch_linkedin_profile","fetch_company","fetch_linkedin_posts","fetch_company_posts","WebFetch","WebSearch","complete_sender_research"],"requiredInput":"LinkedIn profile URL or handle"},{"action":"confirm_target_before_strategy_packet","uses":"request_user_input","singleChoice":true,"question":"Who should we target first?","options":["Use Sellable's researched recommendation","Choose a different buyer","I'm not sure yet"],"copyMustInclude":["research basis","recommendation is editable","who to target"],"requiredOption":"Other / custom","neverCollectOpenTextWithStructuredQuestion":true,"skipIf":"user already supplied a clear target or yolo_mode with any reasonable best-estimate target"},{"action":"confirm_current_offer_before_strategy_packet","uses":"request_user_input","question":"What should we pitch to prospects first?","options":["Use Sellable's researched recommendation","Use my current pitch","Shape the pitch together"],"copyMustInclude":["research basis","public LinkedIn research may be stale","recommendation is editable","pitch to prospects"],"skipIf":"user already supplied a clear current offer or yolo_mode with any reasonable researched recommendation"},{"action":"confirm_trust_proof_before_strategy_packet","uses":"request_user_input","singleChoice":true,"question":"What is the strongest credibility signal we can lead with?","options":["Use Sellable's recommended proof","Use customer proof or a case study","Use founder or company credibility"],"copyMustInclude":["build trust with prospects","recommendation is editable","proof to use or avoid"],"requiredOption":"Other / custom","skipIf":"user already supplied clear proof to use or avoid, or yolo_mode with any reasonable researched proof"},{"action":"choose_prospect_source_path_before_strategy_packet","uses":"request_user_input","singleChoice":true,"question":"How should we find prospects?","options":["Find prospects for me","Use a CSV of LinkedIn profiles","Use a CSV of company domains"],"copyMustInclude":["prospects","no one added yet","find prospects for me"],"skipIf":"user already supplied a source path or yolo_mode with any reasonable source path"},{"action":"infer_strategy_packet_in_yolo_mode","when":"yolo_mode","skipStructuredSetupQuestions":true,"inferFields":["buyer segment","offer/CTA","proof to use or avoid","lead source","filter choice","message direction"],"sourceOfTruth":"identity/company lookup plus operator directions","mustStateAssumptionsInBrief":true},{"action":"render_supplied_setup_receipt_before_brief_when_setup_questions_skip","when":"any setup question is skipped because the user already supplied or yolo mode inferred identity, target, offer, proof, or source","output":"normal chat receipt before the campaign brief or watch-link handoff","copyMustInclude":["Accepted LinkedIn input: normalized to the required LinkedIn profile URL.","Offer path: supplied current offer or Sellable's researched recommendation; you can replace it with your current offer."],"copyMustNotInclude":["Campaign Identity"]},{"action":"create_watchable_campaign_shell_with_v1_brief","tool":"create_campaign","requiredFields":["name","campaignBrief","clientProspectId or senderLinkedinUrl","currentStep","watchNarration"],"requiredValues":{"currentStep":"create-offer","watchNarration.stage":"brief"},"capture":["campaignId","watchUrl"],"canonicalStateWrites":["campaignId","watchUrl","campaignBrief","currentStep:create-offer"]},{"action":"surface_campaign_shell_watch_link","watchUrlSource":"create_campaign.watchUrl","requiredWatchUrlShape":"exact create_campaign.watchUrl; direct /campaign-builder/{campaignId}?mode={claude|codex} URL with workspaceId and token","copyMustInclude":["We'll keep building the campaign in this chat.","You can watch it being built in real time in the app here:","I'll ask for your approval whenever I need your expertise or taste before moving forward.","Send changes here."],"immediateNextMainChatLine":"Ask for brief approval now. After approval, say: \"Brief approved. Next, we'll choose where to find buyers. I won't add anyone yet.\"","codexBrowserHandoff":{"openWhenAvailable":false,"printWatchLinkOnly":true,"tellUserCommandEnterOrClick":false,"mustNotUseBrowserAutomation":true,"fallbackMustNotClaimInspection":true},"copyMustAvoid":["bare /campaign-builder/{campaignId} URL","derived watch URL","second Watch link:","Next, we'll choose where to find buyers"],"oneTimeOnly":true}],"allowedTools":["get_subskill_prompt","fetch_company","fetch_company_posts","fetch_linkedin_profile","fetch_linkedin_posts","complete_sender_research","create_campaign","AskUserQuestion","request_user_input"],"doNotAllow":["list_senders","save_rubrics","import_leads","confirm_lead_list","queue_cells","start_campaign"],"waitFor":["campaign_shell_created","brief_ready","confirm_with_user"],"transitions":{"campaign_shell_created":"brief-review","brief_ready":"brief-review","confirm_with_user":"brief-review"}},{"id":"brief-review","label":"Brief review","onEnter":[{"action":"render_brief_inline","minimumVisibleDetail":"campaign direction, buyer, offer, proof, source plan, safety gates","copyMustInclude":["This brief is based on Sellable's research and your answers.","If your site or LinkedIn is stale","Do you agree with this researched campaign direction?"],"copyMustNotInclude":["quoted first-message copy","Message Angle as final proof","This approval covers","does not approve adding people","does not approve scraping posts","selecting a sender","attaching a sequence","Campaign Identity","what should this campaign sell first","sell first","[from you]","[from LinkedIn]","[from website]","[from case study]","[Sellable recommendation]"],"messageHintRule":"If any message-shape hint remains, keep it as non-final bullets and say real examples come after leads are found and filtered.","approvalBoundaryRule":"Keep this gate positive and short. Do not list later steps this approval does not cover; ask approve/revise and then move to choosing where to find buyers.","sectionLabelRule":"Use ## Sender and Company for the person/company context; never render ## Campaign Identity.","skipIf":"the full brief was already rendered in the current shell-creation handoff before create_campaign returned","renderOnceRule":"The brief must be visible exactly once before approval. Do not render it again immediately after shell creation; append the one watch-link handoff and ask the approval question.","copyMustAvoid":["duplicate brief render after shell creation","second Watch link:"]},{"action":"ask_brief_choice","uses":"request_user_input","choices":["Approve brief","Revise brief","Pause here"],"skipIf":"yolo_mode; auto_continue after rendering assumptions unless brief quality floor fails"}],"requiredCampaignState":["campaignId","watchUrl","campaignBrief","currentStep"],"allowedTools":["AskUserQuestion","request_user_input","update_campaign"],"doNotAllow":["create_campaign","list_senders","import_leads","confirm_lead_list","queue_cells","start_campaign"],"waitFor":["user_brief_confirmed","revise_brief","auto_continue"],"transitions":{"user_brief_confirmed":"find-leads","revise_brief":"brief-interview","auto_continue":"find-leads"}},{"id":"find-leads","label":"Find leads","sourceSelectionFunnel":{"preScoutRecommendationGate":{"required":true,"label":"Find buyers plan approval","mustHappenBefore":["get_provider_prompt","search_signals","search_sales_nav","search_prospeo","fetch_post_engagers","source-scout dispatch"],"show":["buyer groups or places we could check","best place to start","why the right buyers are likely to be there","what signs the next search will check","where to look next if the first place is too thin","what approval authorizes"],"approvalAuthorizes":"looking for the best places to find buyers; no one added yet","explanationMustInclude":["where the right buyers are already talking","why that place fits this campaign","enough likely prospects","what counts as a good sign","where to look next if thin","I won't add anyone yet","choose where to find buyers"],"yoloMode":{"autoApproveRecommendedLane":true,"mustShowAssumedChoice":true,"pauseIfConfidenceLow":true},"customerLanguage":{"readability":"grade-5","requiredHeading":"Find Buyers Plan","prefer":["place to look","best place to start","people to check","prospects","I won't add anyone yet"],"avoid":["lane","source scouting","provider","precision/scale tradeoff","evidence quality","pilot volume","ICP","workflow pain","lead-source scouting","not importing leads"],"example":"Brief approved. Next, we'll choose where to find buyers. I won't add anyone yet. I recommend starting with LinkedIn posts about [topic]; fallback is [place].","termRule":{"buyers":"target market","peopleToCheck":"raw reactions/comments before fit is known","prospects":"likely usable people after fit","leads":"campaign rows"}},"questionOrder":"update_campaign to pick-provider, render ## Find Buyers Plan in normal chat without repeating the watch link, then open the structured approval question; never ask first or show the plan after approval","copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"],"approvalFreshnessRule":"Only an approval question opened after the visible Find Buyers Plan, or an explicit source choice made after that plan, can set source_lane_approved. Do not reuse brief approval or pre-plan provider state."},"defaultWhenSourceUnspecified":["signal-discovery","sales-nav-recent-active","sales-nav-general","prospeo"],"userFacingFallbackOrder":["LinkedIn posts where the right buyers are already talking","people with the right titles who recently posted on LinkedIn","people with the right titles from a broader LinkedIn search","a broader company and contact search"],"parallelAllowedOnlyWhen":["user explicitly requested source comparison","resuming already-started parallel scouts","first viable source is borderline and one cheap fallback check is needed"]},"onEnter":[{"tool":"update_campaign","requiredValues":{"currentStep":"pick-provider","watchNarration.stage":"find-leads","watchNarration.headline":"Review where to find buyers","watchNarration.visibleState":"The app is showing source selection before lead finding starts.","watchNarration.agentIntent":"Codex is explaining where it recommends looking first and where it will look next if that is too thin.","watchNarration.nextAction":"Approve where to look first or choose another place","watchNarration.safety":"Only deciding where to look. No one is added yet."},"purpose":"show the visible source-plan approval checkpoint before provider lanes"},{"tool":"get_source_scout_registry","purpose":"load canonical source scout names before optional branch launch"},{"action":"render_find_buyers_plan_inline","oneShot":true,"requiredPrecondition":"currentStep=pick-provider has been saved with update_campaign","mustHappenAfter":["update_campaign currentStep=pick-provider"],"mustHappenBefore":["ask_find_buyers_plan_choice","get_provider_prompt","search_signals","search_sales_nav","search_prospeo","fetch_post_engagers","source-scout dispatch"],"output":"normal chat text before any request_user_input approval panel","requiredHeading":"Find Buyers Plan","requiredInlineFields":["plain-language buyer groups or places for this campaign","best place to start","why the right buyers are likely to be there","what signs the next search will check","where to look next if the first place is too thin","what approval authorizes"],"copyMustInclude":["Find Buyers Plan","best place to start","I won't add anyone","choose where to find buyers","I won't add anyone yet"],"copyMustAvoid":["lane","source scouting","provider","precision/scale tradeoff","evidence quality","pilot volume","ICP","workflow pain","lead-source scouting","not importing leads","Watch link:","campaign-builder URL","repeat the watch URL"],"approvalBoundary":"Explain what approval authorizes before asking: looking for the best places to find buyers; no one is added yet.","linkPolicy":"Do not include another watch link. The first brief handoff already gave the user the live app URL."},{"action":"ask_find_buyers_plan_choice","uses":"request_user_input","oneShot":true,"requiredPrecondition":"render_find_buyers_plan_inline completed in normal chat after pick-provider state was saved; no earlier approval can satisfy this","skipIf":"source_lane_approved was set by ask_find_buyers_plan_choice after the visible Find Buyers Plan, leadSourceProvider was explicitly chosen by the user after that plan, or yolo_mode auto-selected source_lane_approved after showing the assumed choice","choices":["Start with LinkedIn posts","Start with active LinkedIn profiles","Start with company/contact search","Choose a different place","Pause here"],"approvalChoiceLabelsByProvider":{"signal-discovery":"Start with LinkedIn posts","sales-nav":"Start with active LinkedIn profiles","prospeo":"Start with company/contact search"},"approvalState":"source_lane_approved","copyMustInclude":["Approve this plan for where to find buyers?","Start with LinkedIn posts"],"mustNotHappenBefore":["render_find_buyers_plan_inline"],"approvalStateRule":"Set source_lane_approved only from this question after the visible plan, never from brief approval, generic Approve plan, or pre-plan provider state."},{"action":"persist_provider_search_step_before_search","tool":"update_campaign","requiredPrecondition":"source_lane_approved set after the visible Find Buyers Plan approval question","providerCurrentStepMap":{"signal-discovery":"signal-discovery","sales-nav":"sales-nav","prospeo":"prospeo"},"requiredValues":{"leadSourceType":"new","leadSourceProvider":"approved provider","currentStep":"provider-specific current step","watchNarration.stage":"find-leads","watchNarration.headline":"Searching the approved place","watchNarration.safety":"Looking only. No one is added yet."},"mustRunBefore":["search_signals","search_sales_nav","search_prospeo","fetch_post_engagers"]},{"action":"run_sequential_source_funnel","requiredPrecondition":"source_lane_approved set after the visible Find Buyers Plan approval question","defaultOrder":["source-scout-linkedin-engagement","source-scout-sales-nav","source-scout-prospeo-contact"],"campaignOfferIdRequired":true,"stopOnFirstViableUnlessComparisonRequested":true}],"requiredCampaignState":["campaignId","campaignBrief","currentStep"],"allowedTools":["get_source_scout_registry","get_provider_prompt","lookup_sales_nav_filter","search_sales_nav","search_prospeo","search_signals","select_promising_posts","fetch_post_engagers","fetch_company","fetch_linkedin_profile","load_csv_linkedin_leads","load_csv_domains","get_rows_minimal","update_campaign","AskUserQuestion","request_user_input","get_campaign_navigation_state"],"doNotAllow":["create_campaign","list_senders","save_rubrics","import_leads","confirm_lead_list","queue_cells","start_campaign"],"waitFor":["lead_review_ready","revise_brief","confirm_with_user"],"transitions":{"lead_review_ready":"lead-review","revise_brief":"brief-interview","confirm_with_user":"lead-review"}},{"id":"lead-review","label":"Source approval","onEnter":[{"action":"show_source_decision_card","requiredInlineFields":["primary source and exact filters/recipe","specific source action awaiting approval","for Signal Discovery: compact Source Recommendation in plain language with selected posts, people to check, likely prospects, first review, and fallback","for Signal Discovery: recommended scrape post count and target people-to-check volume","first campaign review size, clearly separate from source sampling","runner-up and why it lost","raw volume","sampled people","sampled fits as n/N plus percentage/range","estimated usable prospects","cleanup risk","what will happen after approval"],"copyMustAvoid":["lead-source scouting","source scouting","headline-fit","engagers needed","execution slice","ICP","good buyers","real buyers","This approval covers","It does not approve","does not approve filters","filters, messages, sender selection","sender selection, sequence, or sending","Watch link:","campaign-builder URL","repeat the watch URL"],"approvalBoundaryRule":"Use positive source-action copy only. Include: \"After approval, I will build the source list, add it to the campaign, and review the first 15 leads before we scale.\" Never list future non-approvals.","linkPolicy":"Do not include another watch link in Source Recommendation; describe the source decision only."},{"action":"ask_source_review_choice","uses":"request_user_input","choices":["Approve scraping N recommended LinkedIn posts","Run the approved source import","Revise source","Pause here"],"approvalChoiceLabelsByProvider":{"signal-discovery":"Approve scraping {scrapePostCount} recommended LinkedIn posts?","sales-nav":"Import the approved Sales Nav source list","prospeo":"Import the approved Prospeo source list"},"postApprovalContract":{"singleUseApproval":true,"doNotRepeatAfterApproval":["Source Recommendation","show_source_decision_card","ask_source_review_choice"],"requiredNextActionAfterApproval":"ack once; call import_leads immediately","signalDiscoveryNextTool":"import_leads({ provider: \"signal-discovery\", targetEngagerCount, maxPostsToScrape, confirmed: true })"},"autoSelectIf":"yolo_mode and projected usable pool clears the source quality floor","copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"]}],"requiredCampaignState":["campaignId","campaignBrief","providerSearchAssociation"],"allowedTools":["AskUserQuestion","request_user_input","update_campaign"],"doNotAllow":["create_campaign","list_senders","save_rubrics","import_leads","confirm_lead_list","queue_cells","start_campaign"],"waitFor":["lead_review_confirmed","revise_leads","confirm_with_user","auto_continue"],"transitions":{"lead_review_confirmed":"auto-execute-leads","revise_leads":"find-leads","confirm_with_user":"auto-execute-leads","auto_continue":"auto-execute-leads"}},{"id":"auto-execute-leads","label":"Materialize confirmed source list","currentStepValue":"auto-execute-leads","reference":"references/step-13-import-leads.md","onEnter":[{"tool":"get_subskill_prompt","requiredValues":{"subskillName":"create-campaign-v2-tail"},"purpose":"load tail contract before execution tools"},{"tool":"import_leads","requiredFields":["campaignOfferId","selected source/list","sourceListTarget or targetEngagerCount"],"requiredValues":{"salesNavProspeoDefaultSourceListTarget":1000,"signalDiscoveryDefaultEngagerTarget":1500},"modeAddHandshake":{"firstCallReturns":"needsModeSelection when adding to an existing campaign-attached list","requiredFollowup":"retry with mode=add after explicit user/source-state compatibility is confirmed"},"surfaceDedupRatio":true},{"tool":"wait_for_lead_list_ready","purpose":"poll until the source lead-list import reaches a terminal complete/failed/cancelled state before copying rows into the campaign table","repeatUntil":"ready_true_or_cancelled_or_import_failed","requiredValues":{"requireComplete":true},"onImportStillRunning":"re-run wait_for_lead_list_ready; do not call confirm_lead_list just because rows exist","partialOverrideRule":"Only if the user explicitly asks to keep going early may the next confirm_lead_list call pass allowPartialSourceList: true."},{"tool":"confirm_lead_list","requiredFields":["campaignOfferId","selectedLeadListId","reviewBatchLimit"],"requiredValues":{"reviewBatchLimit":15},"capture":["workflowTableId","reviewBatchRowIds"],"requiredPrecondition":"wait_for_lead_list_ready returned ready:true, unless the user explicitly asked to keep going early with a partial list","partialOverrideField":"allowPartialSourceList:true only after explicit user early-continue instruction","mustNotRunWhen":["wait_for_lead_list_ready reason=import_still_running and no explicit early-continue instruction","wait_for_lead_list_ready reason=cancelled","wait_for_lead_list_ready reason=import_failed","missing import job metadata"]},{"tool":"wait_for_campaign_table_ready"},{"tool":"get_rows_minimal","requiredValues":{"tableId":"{workflowTableId}","limit":15},"capture":["reviewBatchRowHash"]},{"action":"summarize_review_batch_and_advance_to_filter_choice","purpose":"ask filter choice immediately; no post-lead registries, filter refs, or message prompts first","maxCustomerCopyLines":4,"copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"],"linkPolicy":"Ask add filters vs skip filters without repeating the watch link."}],"requiredCampaignState":["campaignId","providerSearchAssociation","selectedLeadListId"],"allowedTools":["get_subskill_prompt","import_leads","wait_for_lead_list_ready","confirm_lead_list","wait_for_campaign_table_ready","get_rows_minimal","update_campaign","AskUserQuestion","request_user_input"],"doNotAllow":["get_post_find_leads_scout_registry","Task","spawn_agent","list_senders","queue_cells","start_campaign","enrich_with_prospeo","bulk_enrich_with_prospeo","save_rubrics"],"waitFor":"source_list_confirmed_and_review_sample_ready","transitions":{"source_list_confirmed_and_review_sample_ready":"filter-choice","escalation_triggered":"escalation"},"hardRules":["import_leads_then_repeated_wait_for_lead_list_ready_then_confirm_lead_list_then_wait_for_campaign_table_ready","partial_source_rows_do_not_unblock_confirm_lead_list_without_explicit_user_early_continue","cancelled_or_failed_source_import_stops_before_campaign_table_copy"]},{"id":"filter-choice","label":"Filter choice","currentStepValue":"filter-choice","onEnter":[{"tool":"update_campaign","requiredValues":{"currentStep":"filter-choice","watchNarration.stage":"fit-message"}},{"action":"ask_filter_choice","uses":"request_user_input","choices":["Use filters","Skip filters","Revise source"],"autoSelectIf":"yolo_mode; choose filters unless source is tightly curated or user directed otherwise","copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"],"copyMustInclude":["source rows are in the campaign","add fit filters before message generation or skip filters"]}],"hardRules":["ask_filter_choice_immediately_after_review_batch_import","do_not_call_get_subskill_prompt_before_filter_choice","do_not_call_get_subskill_asset_before_filter_choice","do_not_call_get_post_find_leads_scout_registry_before_filter_choice","do_not_spawn_post_lead_agents_before_filter_choice"],"requiredCampaignState":["campaignId","campaignBrief","selectedLeadListId","workflowTableId"],"allowedTools":["AskUserQuestion","request_user_input","update_campaign","get_campaign_navigation_state"],"doNotAllow":["get_subskill_prompt","get_subskill_asset","get_post_find_leads_scout_registry","Task","spawn_agent","create_campaign","list_senders","import_leads","confirm_lead_list","queue_cells","start_campaign","generate_messages"],"waitFor":["filters_enabled","filters_skipped","revise_leads"],"transitions":{"filters_enabled":"post-lead-workstreams","filters_skipped":"message-generation","revise_leads":"find-leads"}},{"id":"post-lead-workstreams","label":"Filter workstream","onEnter":[{"action":"persist_add_filters_approval","tool":"update_campaign","when":"filters_enabled","requiredFields":["campaignId","enableICPFilters","currentStep","watchNarration"],"requiredValues":{"enableICPFilters":true,"currentStep":"create-icp-rubric","watchNarration.stage":"fit-message","watchNarration.headline":"Create filter rules","watchNarration.visibleState":"Filters are enabled and the browser is showing Filter Rules while Codex defines the rubric.","watchNarration.nextAction":"Review saved filter rules"},"mustRunBefore":["get_post_find_leads_scout_registry","launch_lead_fit_builder_after_filter_choice","save_rubrics"]},{"tool":"get_post_find_leads_scout_registry","purpose":"load canonical post-lead worker names only after the user has chosen filters"},{"action":"launch_lead_fit_builder_after_filter_choice","mode":"parallel_when_host_supports_subagents","target":"post-find-leads-filter-scout","inputs":["campaignId","campaignBrief","selectedLeadListId","workflowTableId","reviewBatchRowIds/hash","filterChoice"],"stateSource":"live campaign/table","debugFilesOptionalOnly":true},{"action":"launch_message_draft_builder_after_filter_decision","mode":"parallel_when_host_supports_subagents","target":"post-find-leads-message-scout","when":"filters_enabled","doesNotQueueCells":true,"customerNarration":"Say message agent is drafting."},{"action":"save_filter_rubrics_to_campaign","tool":"save_rubrics","when":"filters_enabled and rubrics are production-shaped","requiredFields":["campaignOfferId","leadScoringRubrics"],"writesCampaignState":"leadScoringRubrics","requiredSideEffects":{"enableICPFilters":true,"currentStep":"create-icp-rubric","watchNarration.headline":"Filter rules saved for review"}},{"action":"ask_filter_rubric_review_choice","uses":"request_user_input","choices":["Approve filters","Revise filters","Pause"],"purpose":"let the user read saved rubrics before Filter Leads or enrichment","copyMustInclude":["These rules prevent wasted sends before I score/import the list.","Recommended because of the researched ICP, source sample, and repeated false-positive patterns.","one pass example and one block example when available","approval is for the filter rules","Approve these filter rules."],"autoSelectIf":"yolo_mode and rubrics are production-shaped","copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"]}],"requiredCampaignState":["campaignId","campaignBrief","selectedLeadListId","workflowTableId"],"allowedTools":["get_subskill_prompt","get_subskill_asset","get_post_find_leads_scout_registry","save_rubrics","get_campaign","get_rows_minimal","update_campaign","get_campaign_navigation_state","AskUserQuestion","request_user_input","Task","spawn_agent"],"doNotAllow":["create_campaign","list_senders","import_leads","confirm_lead_list","queue_cells","start_campaign","check_rubric","generate_messages"],"waitFor":["filter_rubrics_approved","revise_leads","revise_rubric","revise_messaging"],"hardRules":["after_save_rubrics_currentStep_must_stay_create-icp-rubric_until_filter_approval","filter_approval_required_before_apply-icp-rubric_or_queue_campaign_cells","do_not_move_browser_to_messages_until_filter_leads_step_is_current_or_filters_are_explicitly_skipped","no_post_lead_worker_or_deep_prompt_before_filter_choice","lead_fit_builder_starts_only_after_filters_enabled","msg_draft_after_filter_choice","msg_draft_no_cells"],"transitions":{"filter_rubrics_approved":"message-generation","revise_leads":"find-leads","revise_rubric":"filter-rubric","revise_messaging":"message-generation","confirm_with_user":"message-review"}},{"id":"filter-rubric","label":"Rubric revision","onEnter":[{"tool":"get_subskill_asset","requiredValues":{"subskillName":"create-campaign-v2","assetPath":"references/filter-leads.md"}},{"tool":"save_rubrics","requiredFields":["campaignOfferId","leadScoringRubrics"],"writesCampaignState":"leadScoringRubrics","requiredSideEffects":{"enableICPFilters":true,"currentStep":"create-icp-rubric","watchNarration.headline":"Filter rules saved for review"}},{"action":"ask_filter_rubric_review_choice","uses":"request_user_input","choices":["Approve filters","Revise filters","Pause"],"copyMustInclude":["These rules prevent wasted sends before I score/import the list.","approval object is filter rules only","approval is for the filter rules","Approve these filter rules."],"autoSelectIf":"yolo_mode and rubrics are production-shaped","copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"]}],"requiredCampaignState":["campaignId","workflowTableId"],"allowedTools":["get_subskill_asset","save_rubrics","AskUserQuestion","request_user_input"],"doNotAllow":["create_campaign","list_senders","import_leads","confirm_lead_list","update_campaign","queue_cells","start_campaign","check_rubric"],"waitFor":["filter_rubrics_approved","revise_leads","confirm_with_user","auto_continue"],"transitions":{"filter_rubrics_approved":"message-generation","revise_leads":"find-leads","confirm_with_user":"message-generation","auto_continue":"message-generation"}},{"id":"message-generation","label":"Message generation","onEnter":[{"action":"set_message_review_visible_step_by_filter_choice","tool":"update_campaign","branchRules":["yes after filter approval: currentStep=apply-icp-rubric; wait on Filter Leads","no: currentStep=messages; message review"],"watchNarration.stage":"fit-message"},{"action":"run_or_reconcile_message_draft_builder","target":"post-find-leads-message-scout","toolCallRequiredBeforeDraft":["run background post-find-leads-message-scout when available","get_subskill_prompt({ subskillName: \"generate-messages\" }) for the required normal-path contract","do not use any alternate or examples-only message prompt"],"stateSource":"campaignBrief, source, selectedLeadListId, workflowTableId, execution-slice row ids/hash","outputState":"messageDraftRecommendation","revisionMode":{"when":"revise_messaging or latest user message requests template changes","requiredInputs":["latest user feedback","current messageDraftRecommendation","campaign/table execution-slice state"],"output":"revised messageDraftRecommendation rendered before request_user_input"}}],"requiredCampaignState":["campaignId","campaignBrief","selectedLeadListId","workflowTableId"],"allowedTools":["get_subskill_prompt","get_campaign","get_rows_minimal","update_campaign"],"toolRules":["Run post-find-leads-message-scout when available; otherwise load get_subskill_prompt({ subskillName: \"generate-messages\" }) from live state.","brief.md, lead-review.md, and lead-sample.json are optional debug context only.","messageDraftRecommendation returns templateRecommendation, tokenFillRules, renderedSample, concerns, status, basisToken, outputAt, outputHash, and error/retry detail.","If campaign/source/table/execution-slice basis does not match, classify the output stale or blocked."],"doNotAllow":["create_campaign","list_senders","save_rubrics","import_leads","confirm_lead_list","queue_cells","start_campaign","generate_messages","AskUserQuestion","request_user_input"],"waitFor":["message_validation_ready","revise_rubric","revise_messaging"],"transitions":{"message_validation_ready":"message-review","revise_rubric":"filter-rubric","revise_messaging":"message-generation"},"revisionLoop":{"trigger":"user gives copy feedback, asks for an update, or chooses revise-messaging","required":["apply latest user feedback to current messageDraftRecommendation and live campaign/table state","produce a new messageDraftRecommendation with revisionNotes","render revised ## Message Template, ## Rendered Example, and What changed before asking approval"],"blocked":["asking whether an unseen new version is better","reusing the old template after acknowledging feedback","update_campaign_brief before approve-message","request_user_input or AskUserQuestion during message-generation"],"outputState":"messageDraftRecommendation"},"hardRules":["message_revision_must_render_new_template_before_approval_question","message_revision_saves_only_after_approve_message","message_generation_must_not_call_request_user_input","message_generation_must_transition_to_message_review_only_after_renderable_draft_state"]},{"id":"message-review","label":"Message review","onEnter":[{"action":"render_message_review_from_state","requiredState":["campaignBrief","selectedLeadListId","workflowTableId","messageDraftRecommendation"],"requiredVisibleLabels":["## Message Template","## Rendered Example","Good token fill:","My take:","Question: approve-message or revise-messaging?","Recommendation:"],"doNotShowByDefault":["Token Notes","Good omit / fallback","Bad fill to avoid","Token Adherence Table"],"internalPersistenceOnly":["Token Fill Rules","Token Fill Examples","fallback guidance","bad-fill avoidance notes"],"copyMustInclude":["QA receipt before approval","tokens resolved","company/person match","proof claim","prospect angle","language/tone when known","obvious bad fits","Would I take this call?","weak personalization can burn the sender's reputation","full list remains paused"],"copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"],"revisionRenderRule":"After revise_messaging, show revised template, rendered example, and What changed before ask_message_review_choice; never ask if an unseen version is better."},{"action":"ask_message_review_choice","uses":"request_user_input","choices":["approve-message","revise-messaging"],"copyMustInclude":["approve this message template and continue","nothing sends from this approval"],"autoSelectIf":"yolo_mode and Recommendation is approve-message; revise autonomously when Recommendation is revise-messaging","copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"],"requiredPrecondition":"current or revised template and rendered example are visible in this turn","revisionPrecondition":"after revise_messaging, revised template is visible before this question"},{"action":"sync_approved_message_set_to_campaign_brief","tool":"update_campaign_brief","when":"after approve-message","requiredFields":["campaignId","approvedMessageTemplate","Token Fill Rules","Token Fill Examples"],"writesCampaignState":"approvedMessageTemplate","revisionApprovalRule":"If the user requested revisions, write only the latest approved revised template and token rules to campaignBrief."}],"requiredCampaignState":["campaignId","workflowTableId","messageDraftRecommendation"],"allowedTools":["AskUserQuestion","request_user_input","update_campaign_brief","update_campaign","get_rows_minimal"],"doNotAllow":["create_campaign","list_senders","save_rubrics","import_leads","confirm_lead_list","start_campaign"],"waitFor":["message_approved","revise_messaging"],"transitions":{"message_approved":"validate-sample","revise_messaging":"message-generation"}},{"id":"validate-sample","label":"Validate campaign-table execution slice","currentStepValue":"apply-icp-rubric","reference":"references/sample-validation-loop.md","visibleStepRule":"Filter Leads is reached only after saved-filter approval; on approve-message, save the template and queue bounded Enrich Prospect cells through selector-based campaign processing.","onEnter":[{"tool":"update_campaign","requiredValues":{"currentStep":"apply-icp-rubric","watchNarration.stage":"fit-message"},"purpose":"keep the watched UI on Filter Leads while the approved template starts the bounded cascade","watchNarrationRule":"Say the template is saved and Filter Leads is now running the bounded enrichment and scoring pass."},{"tool":"queue_campaign_cells","purpose":"queue Enrich Prospect cells for the stored review batch without fetching bulky row payloads","requiredFields":["workflowTableId","columnRole","rowSelector"],"requiredValues":{"columnRole":"enrich","rowSelector":{"type":"reviewBatch"},"forceRerun":false},"targetCountSource":"WorkflowTable.config.mcp.reviewBatch.rowCount (default 15)"},{"tool":"wait_for_campaign_processing","purpose":"wait_for_first_passed_review_batch_row","requiredFields":["workflowTableId","minPassedCount"],"requiredValues":{"minPassedCount":1},"readVia":"stats_only_tool_result","timeoutGuidance":"If this times out, do not repoll identical args; use partial diagnostics to revise filters or surface blocked sample state.","customerSummaryPattern":["{checked} leads checked","{passed} passed fit scoring","{blocked} blocked before sending","full list remains paused until approval"]},{"action":"handle_partial_or_timeout_sample","customerStatus":"sample-needs-revision","rule":"Do not repeat waits indefinitely; surface partial status and route to revision.","copyMustInclude":"completed, passed, pending, blocked before sending, and full list remains paused"}],"requiredCampaignState":["campaignId","workflowTableId","approvedMessageTemplate","filterRubricsApproved when enableICPFilters=true"],"allowedTools":["get_subskill_asset","queue_campaign_cells","select_campaign_cells","get_campaign_table_schema","wait_for_campaign_processing","update_campaign","AskUserQuestion","request_user_input"],"doNotAllow":["import_leads","list_senders","start_campaign","enrich_with_prospeo","bulk_enrich_with_prospeo","check_rubric"],"hardRules":["campaign_processing_waits_are_stats_only_by_default","queue_review_batch_by_selector_never_fetch_rows_for_cell_ids","timeout_never_repeats_without_customer_handoff","timeout_or_underfloor_sample_never_advances_to_settings"],"waitFor":["sample_validated","sample_revision_required"],"transitions":{"sample_validated":"auto-execute-messaging","sample_revision_required":"lead-review","revise_leads":"find-leads","revise_rubric":"filter-rubric","escalation_triggered":"escalation"}},{"id":"auto-execute-messaging","label":"Generate initial campaign-row messages","currentStepValue":"auto-execute-messaging","references":["references/parallel-critique-protocol.md","references/thomas-variant-selection.md","references/thomas-revision-filters.md","references/step-15-re-cascade.md"],"onEnter":[{"tool":"queue_campaign_cells","batchSize":100,"when":"any passing row has pending, empty, or stale Generate Message cell","requiredFields":["workflowTableId","columnRole","rowSelector"],"requiredValues":{"columnRole":"generateMessage","rowSelector":{"type":"needsGeneratedMessage"},"forceRerun":false},"cellSource":"selector-engine needsGeneratedMessage rows using approved campaign brief template"},{"tool":"wait_for_campaign_processing","purpose":"wait_for_first_current_revision_generated_message","requiredValues":{"minGeneratedMessages":1,"templateRevision":"current"},"readVia":"stats_only_tool_result","invariant":"A generated message implies the pass gate; stale or wrong-revision messages are excluded from readiness."},{"action":"observe_generate_message_results","reference":"references/step-15-re-cascade.md"},{"action":"enforce_token_contract","modeFromConfig":"messaging.tokenContract"},{"action":"optional_critique_pass","enabledFromConfig":"messaging.critique.enabled","plan":"85-03","protocol":"references/parallel-critique-protocol.md","sampleSizeFromConfig":"messaging.critique.sampleSize","budgetUsdCapFromConfig":"messaging.critique.budgetUsdCap","perCriticTimeoutFromConfig":"messaging.critique.perCriticTimeoutSeconds","totalTimeoutFromConfig":"messaging.critique.totalTimeoutSeconds","criticsFromConfig":"messaging.critique.critics","enforceFinalizerPassFromConfig":"messaging.critique.synthesis.enforceFinalizerPass","rejectOnFakeProofFromConfig":"messaging.critique.rejectOnFakeProof","rejectOnUnsupportedTokenFromConfig":"messaging.critique.rejectOnUnsupportedToken","onBudgetTrip":"halt_critique_continue_plain_tail","onPerCriticTimeout":"drop_critic_proceed_with_remaining","onTotalTimeout":"persist_plain_message_for_row","onTokenContractViolation":"persist_plain_message_for_row","onFakeProof":"persist_plain_message_for_row"},{"action":"optional_opus_subset","enabledFromConfig":"messaging.critique.opus.enabled","selection":"references/thomas-variant-selection.md","maxMessagesPerPassFromConfig":"messaging.critique.opus.maxMessagesPerPass","budgetUsdCapFromConfig":"messaging.critique.opus.budgetUsdCap","onOpusBudgetTrip":"halt_opus_continue_non_opus","onOpusTokenContractViolation":"fallback_to_non_opus_rewrite"},{"tool":"update_campaign","requiredValues":{"currentStep":"auto-execute-messaging","watchNarration.stage":"review-ready"},"watchNarrationRule":"Say the first passing generated message is ready in Messages; next is review before Settings."},{"action":"ask_generated_message_review_choice","uses":"request_user_input","choices":["Approve reviewed draft rows and continue to Settings","Revise filters","Revise message template","Pause here"],"copyMustInclude":["approve the reviewed draft rows and continue to Settings","tokens resolved","company/person match","proof claim","prospect angle","language/tone when known","obvious bad fits","Would I take this call?","weak personalization can burn the sender's reputation","full list remains paused","nothing sends from this approval"],"autoSelectIf":"yolo_mode and the first passing generated message clears the quality floor","copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"]}],"allowedTools":["get_subskill_asset","get_campaign_table_schema","select_campaign_cells","queue_campaign_cells","wait_for_campaign_processing","revise_message_template_and_rerun","update_campaign","AskUserQuestion","request_user_input"],"doNotAllow":["import_leads","list_senders","start_campaign","generate_messages"],"hardRules":["generated_message_count_excludes_stale_or_wrong_revision_messages","message_template_revision_uses_brief_update_then_generate_message_rerun","do_not_overwrite_generated_message_cells_directly","critique_failure_never_escalates","critique_sample_size_bounded_by_config","first_passing_generated_message_unblocks_review","critics_fixed_at_targeting_copy_voice","synthesis_enforces_phase_84_token_contract","opus_reserved_for_highest_value_subset","proposed_token_never_persisted_in_rewrite"],"waitFor":["generated_messages_approved","sample_revision_required"],"transitions":{"generated_messages_approved":"awaiting-user-greenlight","revise_filters":"filter-rubric","revise_messaging":"message-generation","escalation_triggered":"escalation"}},{"id":"awaiting-user-greenlight","label":"Settings, sender, sequence, and greenlight","currentStepValue":"awaiting-user-greenlight","reference":"references/final-handoff-contract.md","onEnter":[{"tool":"get_campaign","purpose":"verify senderIds and sequence state before final handoff"},{"tool":"list_senders","purpose":"surface available connected senders only at Settings"},{"tool":"update_campaign","requiredValues":{"currentStep":"settings","watchNarration.stage":"review-ready"},"purpose":"park the watched UI on Settings before sender selection","watchNarrationRule":"Say message review is complete and the app is on Settings for sender choice. Mention lead research/filtering already happened outside the user's LinkedIn account and nothing sends until Start."},{"action":"surface_sender_and_slack_handoff","requiredVisibleContent":["connect or select a LinkedIn sender","Lead research and filtering already happened outside your LinkedIn account.","This account is only used for approved sending after final launch.","Nothing sends until the final Start step.","Slack reply review","recommended sequence","final launch confirmation is still ahead"],"copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"]},{"action":"ask_sender_selection","uses":"request_user_input","singleChoice":true,"choices":["Use this connected sender","Connect a different sender in Settings","Pause here"],"autoSelectIf":"yolo_mode and exactly one safe connected sender is available","copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"]},{"action":"attach_selected_sender","tool":"update_campaign","when":"user selected an available connected sender","requiredValues":{"senderIds":["{selectedSenderId}"],"currentStep":"sequence","watchNarration.stage":"review-ready"},"watchNarrationRule":"Say the sender was attached and the app is moving to Sequence review; sequence remains editable and nothing sends until Start."},{"tool":"attach_recommended_sequence","when":"after senderIds are attached","requiredValues":{"campaignId":"{campaignId}","currentStep":"send","watchNarration.stage":"review-ready"},"watchNarrationRule":"The sequence tool owns this visible beat: say the recommended follow-up sequence is attached, the app is on final launch review, and it is still not sending until Start."},{"action":"ask_final_launch_greenlight","uses":"request_user_input","singleChoice":true,"choices":["Start campaign","Review campaign first","Pause here"],"copyMustInclude":["quality confidence means sample messages and prospect angle were checked","launch confidence means sender, sequence, and Start are ready","approved messages begin sending according to the sequence","replies and meetings follow connected settings","you can monitor and pause","no hidden extra approval disappears after clicking Start"],"onUserStart":"claude-greenlight","neverAutoSelectInYolo":true,"copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"]}],"allowedTools":["get_campaign","get_campaign_navigation_state","list_senders","update_campaign","attach_recommended_sequence","AskUserQuestion","request_user_input"],"doNotAllow":["start_campaign","import_leads"],"autoStart":false,"watchRequired":true,"waitFor":["sender_connection_required","sender_attached","sequence_attached","ready_to_launch","user_greenlight","ui_start_detected"],"transitions":{"sender_connection_required":"awaiting-user-greenlight","sender_attached":"awaiting-user-greenlight","sequence_attached":"awaiting-user-greenlight","ready_to_launch":"awaiting-user-greenlight","user_greenlight":"claude-greenlight","ui_start_detected":"running"}},{"id":"claude-greenlight","label":"Explicit launch","reference":"references/final-handoff-contract.md","onEnter":[{"tool":"get_campaign","purpose":"detect_already_running"},{"action":"verify_sequence_and_current_step_before_start","requiredState":["workflowTableId","senderIds","sequenceTemplate","at least one approved generated message","currentStep in awaiting-user-greenlight|claude-greenlight|send"]},{"tool":"start_campaign","requiredFields":["campaignId"],"persistsCurrentStep":"running","watchNarrationRule":"After start_campaign succeeds, the running state must say the final greenlight was accepted, the campaign is now live/running, and the user can watch progress from the campaign."}],"allowedTools":["get_campaign","attach_recommended_sequence","start_campaign","AskUserQuestion","request_user_input"],"watchRequired":true,"waitFor":"campaign_started","transitions":{"campaign_started":"running"}},{"id":"running","label":"Campaign is live","currentStepValue":"running","onEnter":[{"action":"surface_campaign_live_confirmation_without_watch_link","copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"]}],"allowedTools":["get_campaign","AskUserQuestion","request_user_input"],"doNotAllow":["start_campaign"],"terminal":true},{"id":"escalation","label":"Escalation","reference":"references/escalation-ladder.md","allowedTools":["AskUserQuestion","request_user_input"],"doNotAllow":["start_campaign","import_leads"],"transitions":{"revise_brief":"brief-interview","revise_leads":"find-leads","revise_rubric":"filter-rubric","revise_messaging":"message-generation","abort":"abort"}},{"id":"abort","label":"Abort","allowedTools":["AskUserQuestion","request_user_input"],"terminal":true}]}
|
|
@@ -37,7 +37,7 @@ Before showing the commit gate, the draft directory must contain all of:
|
|
|
37
37
|
The current run must also have retrieved the message-generation rules
|
|
38
38
|
before `message-validation.md`, `approval-packet.md`, or the commit gate is
|
|
39
39
|
written. In `create-campaign-v2`, use the embedded
|
|
40
|
-
`post-find-leads-message-scout` prompt or load `generate-messages
|
|
40
|
+
`post-find-leads-message-scout` prompt or load `generate-messages`. If
|
|
41
41
|
the message template was written directly from memory, checklist instructions,
|
|
42
42
|
or planning artifacts, do not show the commit gate; route back to message
|
|
43
43
|
generation.
|
|
@@ -108,7 +108,7 @@ return results
|
|
|
108
108
|
`is_highest_value(...)` and Opus eligibility live in
|
|
109
109
|
`references/thomas-variant-selection.md`. The synthesis step's
|
|
110
110
|
finalizer pass is the same quality gate required by
|
|
111
|
-
`generate-messages
|
|
111
|
+
`generate-messages`.
|
|
112
112
|
|
|
113
113
|
## Critic Groups
|
|
114
114
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Thomas Variant Selection
|
|
2
2
|
|
|
3
3
|
This reference governs which rows in a Plan 85-03 critique sample earn
|
|
4
|
-
an Opus
|
|
4
|
+
an Opus refinement rewrite. Load this file only when
|
|
5
5
|
`messaging.critique.enabled === true` AND
|
|
6
6
|
`messaging.critique.opus.enabled === true` in
|
|
7
7
|
`core/auto-execute.yaml`. When Opus is off, the protocol never
|
|
@@ -131,7 +131,7 @@ All subsequent steps read the already-parsed config. Do not re-load mid-run,
|
|
|
131
131
|
and do not read repo-local config files; packaged Claude Code and Codex runs
|
|
132
132
|
must use the MCP asset loader.
|
|
133
133
|
Load each subskill prompt (`create-campaign-v2`, `research-sender`,
|
|
134
|
-
`generate-messages
|
|
134
|
+
`generate-messages`) at most once per run. If a tool result already told
|
|
135
135
|
you to load a prompt, remember it; do not restart the same prompt later.
|
|
136
136
|
|
|
137
137
|
After every `update_campaign({ currentStep: ... })` in the tail, narrate
|
|
@@ -350,8 +350,8 @@ Entered on `CampaignOffer.currentStep === "auto-execute-messaging"`.
|
|
|
350
350
|
|
|
351
351
|
**Messages are produced by the `Generate Message` column cascade, not
|
|
352
352
|
by a separate tool.** Do NOT call a `generate_messages` MCP tool —
|
|
353
|
-
that tool does not exist; `generate-messages
|
|
354
|
-
Builder
|
|
353
|
+
that tool does not exist; the full `generate-messages` subskill prompt is the
|
|
354
|
+
Message Draft Builder contract for the approved template. In the autonomous tail, messages
|
|
355
355
|
flow through the column pipeline: `Enrich Prospect` →
|
|
356
356
|
`DNC Check` → `ICP Score` → `Passes Rubric` → `Generate Message`.
|
|
357
357
|
Each column's http_request auto-fires when its upstream dependency
|
|
@@ -416,7 +416,7 @@ strings, which is why Step 16 requires Step 15 to be complete.
|
|
|
416
416
|
the flag but never flips it; critique stays off until the flag is
|
|
417
417
|
deliberately enabled.
|
|
418
418
|
3. If `messaging.critique.opus.enabled` is also true, route the
|
|
419
|
-
highest-value subset through the Opus
|
|
419
|
+
highest-value subset through the Opus refinement path per
|
|
420
420
|
`references/thomas-variant-selection.md`. Opus is capped by
|
|
421
421
|
`messaging.critique.opus.maxMessagesPerPass` and
|
|
422
422
|
`messaging.critique.opus.budgetUsdCap`; tripping either cap keeps
|
|
@@ -17,7 +17,6 @@ You craft messages in one of two modes:
|
|
|
17
17
|
- `mcp__sellable__update_campaign_brief` (live campaign mode only)
|
|
18
18
|
- `mcp__sellable__get_rows` (live campaign mode only)
|
|
19
19
|
- `mcp__sellable__update_cell` (live campaign mode only)
|
|
20
|
-
- `mcp__sellable__get_message_prompt`
|
|
21
20
|
- `WebSearch` (live campaign mode only)
|
|
22
21
|
- `mcp__sellable__fetch_linkedin_profile` (live campaign mode only)
|
|
23
22
|
- `mcp__sellable__fetch_linkedin_posts` (live campaign mode only)
|
|
@@ -46,8 +45,8 @@ Reject the branch as blocked if the campaign id, selected lead list, workflow
|
|
|
46
45
|
table, or review-batch row ids/hash do not match. Do not reconstruct state from
|
|
47
46
|
stale local markdown artifacts or direct database reads.
|
|
48
47
|
|
|
49
|
-
Return
|
|
50
|
-
sample, concerns, status, basis token, output timestamp/hash, and error or
|
|
48
|
+
Return message-draft output only: template recommendation, token fill rules,
|
|
49
|
+
rendered sample, concerns, status, basis token, output timestamp/hash, and error or
|
|
51
50
|
retry detail. Do not write message cells, enrich rows, update the campaign
|
|
52
51
|
brief, attach sequence, or imply send readiness from this branch.
|
|
53
52
|
|
|
@@ -2425,8 +2424,8 @@ borrow the closest message shape from
|
|
|
2425
2424
|
### Single Row
|
|
2426
2425
|
|
|
2427
2426
|
1. Load campaign examples, calibration notes, and bounded copy from the brief first.
|
|
2428
|
-
2.
|
|
2429
|
-
|
|
2427
|
+
2. Use the quality gates and fallback REPLY framework embedded in this
|
|
2428
|
+
`generate-messages` prompt. Do not call a separate message-prompt tool.
|
|
2430
2429
|
3. Fetch lead details via `get_rows`.
|
|
2431
2430
|
4. Draft a message, show it with a full 12-gate check.
|
|
2432
2431
|
5. If the message cell is empty, auto-save the draft with `update_cell` and say "Draft saved (not approved)."
|
|
@@ -2444,7 +2443,8 @@ Code and Codex; host subagents are optional acceleration only when the user
|
|
|
2444
2443
|
explicitly asked for agent fan-out.**
|
|
2445
2444
|
|
|
2446
2445
|
1. Load campaign examples, calibration notes, and bounded copy from the brief first.
|
|
2447
|
-
2.
|
|
2446
|
+
2. Use the quality gates and fallback REPLY framework embedded in this
|
|
2447
|
+
`generate-messages` prompt.
|
|
2448
2448
|
3. If the user explicitly asked for agent fan-out and the host exposes subagents,
|
|
2449
2449
|
spawn parallel workers with explicit model/config. Otherwise batch independent
|
|
2450
2450
|
MCP/tool reads where available or run rows sequentially with honest progress
|
|
@@ -2533,7 +2533,7 @@ research → condense → 10 angle agents in parallel → finalizer combines bes
|
|
|
2533
2533
|
{event-led, signal-led, job-post-led, and proof-led specialist fallback rules from gold-standard-message-patterns.md}
|
|
2534
2534
|
|
|
2535
2535
|
## Fallback REPLY Framework
|
|
2536
|
-
{
|
|
2536
|
+
{quality gates and fallback REPLY rules embedded in this generate-messages prompt}
|
|
2537
2537
|
|
|
2538
2538
|
## Instructions
|
|
2539
2539
|
1. Research the prospect (WebSearch, LinkedIn tools if needed)
|
package/skills/research/SKILL.md
CHANGED
|
@@ -6,7 +6,7 @@ visibility: internal
|
|
|
6
6
|
|
|
7
7
|
# Research Protocol (Fast + Parallel)
|
|
8
8
|
|
|
9
|
-
This is the shared research flow for **all**
|
|
9
|
+
This is the shared research flow for **all** active MCP workflows (generate-messages, enrich-prospects, create-campaign).
|
|
10
10
|
The orchestrator remains the main model (e.g., Opus). Use host-aware parallel execution:
|
|
11
11
|
|
|
12
12
|
- Claude host: `Task` subagents on Sonnet-4.5
|
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: generate-messages-compact
|
|
3
|
-
description: Compact required Message Draft Builder prompt for create-campaign-v2; load lazy references only for examples, critique, or revision.
|
|
4
|
-
visibility: internal
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
# Generate Messages Compact
|
|
8
|
-
|
|
9
|
-
Use this prompt for create-campaign-v2 Message Draft Builder. It replaces the
|
|
10
|
-
old normal-path requirement to load the full long-form `generate-messages`
|
|
11
|
-
prompt before writing a reusable template. The full prompt remains available for
|
|
12
|
-
legacy dry-mode validation and deep debugging.
|
|
13
|
-
|
|
14
|
-
## Mode
|
|
15
|
-
|
|
16
|
-
Draft one reusable first-message template for a live campaign after:
|
|
17
|
-
|
|
18
|
-
- `confirm_lead_list` copied a non-empty source into the campaign table
|
|
19
|
-
- the first review batch exists in campaign state
|
|
20
|
-
- the parent recorded the filter choice
|
|
21
|
-
|
|
22
|
-
This is not the product Generate Message cell path. Do not write row messages,
|
|
23
|
-
queue cells, update the campaign brief, attach sequence, or launch.
|
|
24
|
-
|
|
25
|
-
## Source Of Truth
|
|
26
|
-
|
|
27
|
-
Use only live campaign inputs supplied by the parent or scoped MCP tools:
|
|
28
|
-
|
|
29
|
-
- `campaignId`
|
|
30
|
-
- campaign revision or `updatedAt`
|
|
31
|
-
- campaign brief content and brief hash
|
|
32
|
-
- selected source decision and `selectedLeadListId`
|
|
33
|
-
- `workflowTableId`
|
|
34
|
-
- review-batch row ids/hash and compact sample rows
|
|
35
|
-
- filter choice and saved rubric summary when available
|
|
36
|
-
- explicit user/operator message direction
|
|
37
|
-
|
|
38
|
-
Reject as `blocked` or `stale` if campaign id, selected list, table id, brief
|
|
39
|
-
hash, or review-batch basis do not match. Do not reconstruct state from local
|
|
40
|
-
markdown/json files, database reads, or stale tool output.
|
|
41
|
-
|
|
42
|
-
## Required Output
|
|
43
|
-
|
|
44
|
-
Return compact structured output to the parent:
|
|
45
|
-
|
|
46
|
-
- `templateRecommendation`: tokenized first-send template
|
|
47
|
-
- `tokenFillRules`: safe fill rules, fallbacks, and unsupported fills
|
|
48
|
-
- `renderedSample`: one good passing-row example
|
|
49
|
-
- `omitSample`: one example where weak personalization is omitted
|
|
50
|
-
- `concerns`: quality risks or blockers
|
|
51
|
-
- `status`: `ready`, `blocked`, `retry-needed`, or `stale`
|
|
52
|
-
- `basisStatus`, `basisToken`, `outputAt`, `outputHash`
|
|
53
|
-
|
|
54
|
-
The parent owns approval and `update_campaign_brief`.
|
|
55
|
-
|
|
56
|
-
## Drafting Rules
|
|
57
|
-
|
|
58
|
-
- Keep it clear, concrete, and product-specific.
|
|
59
|
-
- Use simple language and short lines.
|
|
60
|
-
- Default to one blank line per sentence in the body.
|
|
61
|
-
- Explain what the product is before what it does.
|
|
62
|
-
- Use proof only when the campaign brief provides it.
|
|
63
|
-
- Use the sender's voice, not third-person notes about the sender.
|
|
64
|
-
- Use supported tokens such as `{{first_name}}` only.
|
|
65
|
-
- Prefer omitting weak personalization over forcing a creepy line.
|
|
66
|
-
- Do not claim the prospect needs outbound, automation, AI, or the product.
|
|
67
|
-
- Do not claim private intent from a reaction/comment.
|
|
68
|
-
- Do not write "saw you engaging", "clearly focused", or similar certainty.
|
|
69
|
-
- Do not include bracketed instructions in the outbound message body.
|
|
70
|
-
- Do not include sequence, follow-up, post-accept DM, launch, or sender setup.
|
|
71
|
-
|
|
72
|
-
Engagement-source personalization is allowed only when it is cautious and
|
|
73
|
-
self-aware, for example: `you might not remember the thread, but I found you
|
|
74
|
-
through a [topic] discussion and your [role/company context] looked close to
|
|
75
|
-
[problem]`. Otherwise omit the source signal and start from role, company,
|
|
76
|
-
problem, or product relevance.
|
|
77
|
-
|
|
78
|
-
## Quality Gate
|
|
79
|
-
|
|
80
|
-
Before returning `ready`, verify:
|
|
81
|
-
|
|
82
|
-
- first name and company/person match
|
|
83
|
-
- tokens have deterministic fallbacks
|
|
84
|
-
- proof claims are in the brief or omitted
|
|
85
|
-
- the prospect angle follows from role/source context
|
|
86
|
-
- unsupported claims are removed
|
|
87
|
-
- the template is usable for the whole review batch, not only one row
|
|
88
|
-
- at least one rendered example reads like a message a real sender would send
|
|
89
|
-
|
|
90
|
-
## Lazy References
|
|
91
|
-
|
|
92
|
-
Load `references/examples-critique-revision.md` only when:
|
|
93
|
-
|
|
94
|
-
- the parent explicitly asks for a critique pass
|
|
95
|
-
- the first draft fails the quality gate
|
|
96
|
-
- the user asks to revise the message template
|
|
97
|
-
- you need more examples to resolve a close call
|
|
98
|
-
|
|
99
|
-
Do not load the old full `generate-messages` prompt in the normal create-campaign
|
|
100
|
-
path unless the parent asks for legacy dry-mode validation or deep debugging.
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
# Examples, Critique, And Revision
|
|
2
|
-
|
|
3
|
-
Load this only when the compact prompt is insufficient.
|
|
4
|
-
|
|
5
|
-
## Good Pattern
|
|
6
|
-
|
|
7
|
-
```text
|
|
8
|
-
Hey {{first_name}},
|
|
9
|
-
|
|
10
|
-
You might not remember the thread, but I found you through a Claude GTM
|
|
11
|
-
discussion and your founder/operator background looked close enough to send this.
|
|
12
|
-
|
|
13
|
-
I am building Sellable so Claude Code can run LinkedIn outbound without
|
|
14
|
-
stitching together Clay, Apollo, HeyReach, and spreadsheets.
|
|
15
|
-
|
|
16
|
-
It finds people from live LinkedIn signals.
|
|
17
|
-
|
|
18
|
-
It filters for fit.
|
|
19
|
-
|
|
20
|
-
It writes the sequence and keeps the campaign moving from chat.
|
|
21
|
-
|
|
22
|
-
Open to seeing what this would look like for your own outbound workflow?
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
## Bad Patterns To Block
|
|
26
|
-
|
|
27
|
-
- "Saw you engaging with Debbie's post, so AI-GTM is clearly on your mind."
|
|
28
|
-
- "You need help with outbound."
|
|
29
|
-
- "I noticed you are struggling with..."
|
|
30
|
-
- "[PERSONALIZATION_LINE]"
|
|
31
|
-
- "Christian's approach is..."
|
|
32
|
-
|
|
33
|
-
## Revision Rule
|
|
34
|
-
|
|
35
|
-
When the user asks to change the message after generation, update the approved
|
|
36
|
-
template in the campaign brief and rerun Generate Message cells. Do not directly
|
|
37
|
-
overwrite generated row messages.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|