@sellable/mcp 0.1.190 → 0.1.191
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 +1 -1
- package/agents/post-find-leads-message-scout.md +7 -8
- package/agents/registry.json +2 -2
- package/dist/auth.js +1 -1
- package/dist/index-dev.js +0 -0
- package/dist/index.js +0 -0
- package/dist/server.js +0 -22
- package/dist/tools/leads.d.ts +19 -63
- package/dist/tools/leads.js +3 -54
- package/dist/tools/prompts.js +2 -2
- package/dist/tools/registry.d.ts +37 -289
- package/dist/tools/registry.js +0 -2
- package/package.json +1 -1
- package/skills/create-campaign/SKILL.md +6 -8
- package/skills/create-campaign-v2/SKILL.md +9 -8
- package/skills/create-campaign-v2/SOUL.md +2 -2
- package/skills/create-campaign-v2/core/flow.v2.json +1 -1
- package/skills/create-campaign-v2/core/policy.md +3 -3
- package/skills/create-campaign-v2/references/approval-gate-framing.md +5 -5
- package/skills/create-campaign-v2/references/filter-leads.md +3 -5
- package/skills/create-campaign-v2/references/parallel-critique-protocol.md +2 -2
- package/skills/create-campaign-v2/references/sample-validation-loop.md +66 -46
- package/skills/create-campaign-v2/references/step-15-re-cascade.md +13 -20
- package/skills/create-campaign-v2-tail/SKILL.md +65 -52
- package/dist/tools/campaign-processing.d.ts +0 -383
- package/dist/tools/campaign-processing.js +0 -304
- package/skills/generate-messages-compact/SKILL.md +0 -100
- package/skills/generate-messages-compact/references/examples-critique-revision.md +0 -37
|
@@ -1,304 +0,0 @@
|
|
|
1
|
-
import { getApi } from "../api.js";
|
|
2
|
-
import { resolveWaitTimeout } from "./readiness.js";
|
|
3
|
-
const DEFAULT_TIMEOUT_MS = 90000;
|
|
4
|
-
const DEFAULT_INTERVAL_MS = 2000;
|
|
5
|
-
function sleep(ms) {
|
|
6
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
7
|
-
}
|
|
8
|
-
function stableHash(value) {
|
|
9
|
-
const text = JSON.stringify(value, Object.keys(value).sort());
|
|
10
|
-
let hash = 2166136261;
|
|
11
|
-
for (let index = 0; index < text.length; index += 1) {
|
|
12
|
-
hash ^= text.charCodeAt(index);
|
|
13
|
-
hash = Math.imul(hash, 16777619);
|
|
14
|
-
}
|
|
15
|
-
return (hash >>> 0).toString(16).padStart(8, "0");
|
|
16
|
-
}
|
|
17
|
-
async function postCampaignProcessing(body) {
|
|
18
|
-
const api = getApi();
|
|
19
|
-
return api.post("/api/v3/mcp/campaign-processing", body);
|
|
20
|
-
}
|
|
21
|
-
async function resolveSchema(input) {
|
|
22
|
-
return postCampaignProcessing({
|
|
23
|
-
action: "schema",
|
|
24
|
-
campaignId: input.campaignId,
|
|
25
|
-
tableId: input.tableId,
|
|
26
|
-
});
|
|
27
|
-
}
|
|
28
|
-
export const campaignProcessingToolDefinitions = [
|
|
29
|
-
{
|
|
30
|
-
name: "get_campaign_table_schema",
|
|
31
|
-
description: "Return compact semantic campaign-table schema: column roles, row count, review-batch metadata, and current approved brief hash. Use for debugging or recovery, not normal queueing.",
|
|
32
|
-
inputSchema: {
|
|
33
|
-
type: "object",
|
|
34
|
-
properties: {
|
|
35
|
-
campaignId: { type: "string" },
|
|
36
|
-
tableId: { type: "string" },
|
|
37
|
-
},
|
|
38
|
-
additionalProperties: false,
|
|
39
|
-
},
|
|
40
|
-
},
|
|
41
|
-
{
|
|
42
|
-
name: "select_campaign_cells",
|
|
43
|
-
description: "Dry-run semantic campaign-cell selection. Returns compact counts and a tiny sample, never bulky row data. Use before queue_campaign_cells only when debugging or explaining a recovery action.",
|
|
44
|
-
inputSchema: {
|
|
45
|
-
type: "object",
|
|
46
|
-
properties: {
|
|
47
|
-
campaignId: { type: "string" },
|
|
48
|
-
tableId: { type: "string" },
|
|
49
|
-
columnRole: {
|
|
50
|
-
type: "string",
|
|
51
|
-
enum: [
|
|
52
|
-
"enrich",
|
|
53
|
-
"passesRubric",
|
|
54
|
-
"icpScore",
|
|
55
|
-
"generateMessage",
|
|
56
|
-
"approved",
|
|
57
|
-
],
|
|
58
|
-
},
|
|
59
|
-
rowSelector: {
|
|
60
|
-
type: "object",
|
|
61
|
-
properties: {
|
|
62
|
-
type: {
|
|
63
|
-
type: "string",
|
|
64
|
-
enum: [
|
|
65
|
-
"reviewBatch",
|
|
66
|
-
"rowIds",
|
|
67
|
-
"passedRows",
|
|
68
|
-
"needsGeneratedMessage",
|
|
69
|
-
"staleGeneratedMessages",
|
|
70
|
-
],
|
|
71
|
-
},
|
|
72
|
-
basisHash: { type: "string" },
|
|
73
|
-
rowIds: { type: "array", items: { type: "string" } },
|
|
74
|
-
limit: { type: "number" },
|
|
75
|
-
},
|
|
76
|
-
required: ["type"],
|
|
77
|
-
additionalProperties: false,
|
|
78
|
-
},
|
|
79
|
-
limit: { type: "number" },
|
|
80
|
-
},
|
|
81
|
-
required: ["columnRole", "rowSelector"],
|
|
82
|
-
additionalProperties: false,
|
|
83
|
-
},
|
|
84
|
-
},
|
|
85
|
-
{
|
|
86
|
-
name: "queue_campaign_cells",
|
|
87
|
-
description: "Resolve and queue campaign cells by semantic role and row selector. Normal create-campaign tail should use this instead of fetching rows only to discover cell IDs. Use forceRerun:true for message-template revisions.",
|
|
88
|
-
inputSchema: {
|
|
89
|
-
type: "object",
|
|
90
|
-
properties: {
|
|
91
|
-
campaignId: { type: "string" },
|
|
92
|
-
tableId: { type: "string" },
|
|
93
|
-
columnRole: {
|
|
94
|
-
type: "string",
|
|
95
|
-
enum: ["enrich", "generateMessage", "approved"],
|
|
96
|
-
},
|
|
97
|
-
rowSelector: {
|
|
98
|
-
type: "object",
|
|
99
|
-
properties: {
|
|
100
|
-
type: {
|
|
101
|
-
type: "string",
|
|
102
|
-
enum: [
|
|
103
|
-
"reviewBatch",
|
|
104
|
-
"rowIds",
|
|
105
|
-
"passedRows",
|
|
106
|
-
"needsGeneratedMessage",
|
|
107
|
-
"staleGeneratedMessages",
|
|
108
|
-
],
|
|
109
|
-
},
|
|
110
|
-
basisHash: { type: "string" },
|
|
111
|
-
rowIds: { type: "array", items: { type: "string" } },
|
|
112
|
-
limit: { type: "number" },
|
|
113
|
-
},
|
|
114
|
-
required: ["type"],
|
|
115
|
-
additionalProperties: false,
|
|
116
|
-
},
|
|
117
|
-
forceRerun: { type: "boolean" },
|
|
118
|
-
limit: { type: "number" },
|
|
119
|
-
reason: { type: "string" },
|
|
120
|
-
},
|
|
121
|
-
required: ["columnRole", "rowSelector"],
|
|
122
|
-
additionalProperties: false,
|
|
123
|
-
},
|
|
124
|
-
},
|
|
125
|
-
{
|
|
126
|
-
name: "wait_for_campaign_processing",
|
|
127
|
-
description: "Stats-only campaign processing wait. Use for fit/message readiness instead of wait_for_rubric_results when waiting on campaign-table processing. Generated messages are counted separately from rubric passes; current template revision is enforced through messageDraftBasis.approvedBriefHash.",
|
|
128
|
-
inputSchema: {
|
|
129
|
-
type: "object",
|
|
130
|
-
properties: {
|
|
131
|
-
campaignId: { type: "string" },
|
|
132
|
-
tableId: { type: "string" },
|
|
133
|
-
minPassedCount: { type: "number" },
|
|
134
|
-
minGeneratedMessages: { type: "number" },
|
|
135
|
-
templateRevision: {
|
|
136
|
-
type: "string",
|
|
137
|
-
description: '"current" (default when minGeneratedMessages is set) or an explicit current approved brief hash.',
|
|
138
|
-
},
|
|
139
|
-
timeoutMs: { type: "number" },
|
|
140
|
-
intervalMs: { type: "number" },
|
|
141
|
-
},
|
|
142
|
-
additionalProperties: false,
|
|
143
|
-
},
|
|
144
|
-
},
|
|
145
|
-
{
|
|
146
|
-
name: "revise_message_template_and_rerun",
|
|
147
|
-
description: "Update the approved message template in the campaign brief, mark prior generated messages stale, reset unsent approvals, and force-rerun Generate Message cells. Does not directly overwrite row message cells.",
|
|
148
|
-
inputSchema: {
|
|
149
|
-
type: "object",
|
|
150
|
-
properties: {
|
|
151
|
-
campaignId: { type: "string" },
|
|
152
|
-
tableId: { type: "string" },
|
|
153
|
-
templateMarkdown: { type: "string" },
|
|
154
|
-
approvedMessageTemplate: { type: "string" },
|
|
155
|
-
rowSelector: {
|
|
156
|
-
type: "object",
|
|
157
|
-
properties: {
|
|
158
|
-
type: {
|
|
159
|
-
type: "string",
|
|
160
|
-
enum: [
|
|
161
|
-
"passedRows",
|
|
162
|
-
"needsGeneratedMessage",
|
|
163
|
-
"staleGeneratedMessages",
|
|
164
|
-
"rowIds",
|
|
165
|
-
],
|
|
166
|
-
},
|
|
167
|
-
rowIds: { type: "array", items: { type: "string" } },
|
|
168
|
-
limit: { type: "number" },
|
|
169
|
-
},
|
|
170
|
-
required: ["type"],
|
|
171
|
-
additionalProperties: false,
|
|
172
|
-
},
|
|
173
|
-
limit: { type: "number" },
|
|
174
|
-
},
|
|
175
|
-
required: ["campaignId"],
|
|
176
|
-
additionalProperties: false,
|
|
177
|
-
},
|
|
178
|
-
},
|
|
179
|
-
];
|
|
180
|
-
export async function getCampaignTableSchema(input) {
|
|
181
|
-
return resolveSchema(input);
|
|
182
|
-
}
|
|
183
|
-
export async function selectCampaignCells(input) {
|
|
184
|
-
return postCampaignProcessing({
|
|
185
|
-
action: "select",
|
|
186
|
-
...input,
|
|
187
|
-
});
|
|
188
|
-
}
|
|
189
|
-
export async function queueCampaignCells(input) {
|
|
190
|
-
return postCampaignProcessing({
|
|
191
|
-
action: "queue",
|
|
192
|
-
...input,
|
|
193
|
-
});
|
|
194
|
-
}
|
|
195
|
-
export async function reviseMessageTemplateAndRerun(input) {
|
|
196
|
-
return postCampaignProcessing({
|
|
197
|
-
action: "reviseTemplateAndRerun",
|
|
198
|
-
...input,
|
|
199
|
-
});
|
|
200
|
-
}
|
|
201
|
-
export async function waitForCampaignProcessing(input) {
|
|
202
|
-
const api = getApi();
|
|
203
|
-
const schema = await resolveSchema(input);
|
|
204
|
-
const { effectiveTimeoutMs, guardApplied, requestedTimeoutMs } = resolveWaitTimeout(input.timeoutMs ?? DEFAULT_TIMEOUT_MS);
|
|
205
|
-
const intervalMs = Math.max(500, input.intervalMs ?? DEFAULT_INTERVAL_MS);
|
|
206
|
-
const minPassedCount = typeof input.minPassedCount === "number" &&
|
|
207
|
-
Number.isFinite(input.minPassedCount)
|
|
208
|
-
? Math.max(1, Math.floor(input.minPassedCount))
|
|
209
|
-
: null;
|
|
210
|
-
const minGeneratedMessages = typeof input.minGeneratedMessages === "number" &&
|
|
211
|
-
Number.isFinite(input.minGeneratedMessages)
|
|
212
|
-
? Math.max(1, Math.floor(input.minGeneratedMessages))
|
|
213
|
-
: null;
|
|
214
|
-
const templateRevision = input.templateRevision ?? (minGeneratedMessages !== null ? "current" : null);
|
|
215
|
-
const pollKey = stableHash({
|
|
216
|
-
tableId: schema.tableId,
|
|
217
|
-
minPassedCount,
|
|
218
|
-
minGeneratedMessages,
|
|
219
|
-
templateRevision,
|
|
220
|
-
});
|
|
221
|
-
const start = Date.now();
|
|
222
|
-
let attempts = 0;
|
|
223
|
-
let lastStats = null;
|
|
224
|
-
while (Date.now() - start <= effectiveTimeoutMs) {
|
|
225
|
-
attempts += 1;
|
|
226
|
-
const stats = await api.get(`/api/v3/workflow-tables/${schema.tableId}/stats`);
|
|
227
|
-
lastStats = stats;
|
|
228
|
-
const currentRevisionHash = stats.currentTemplateRevisionHash ?? null;
|
|
229
|
-
if (templateRevision &&
|
|
230
|
-
templateRevision !== "current" &&
|
|
231
|
-
currentRevisionHash &&
|
|
232
|
-
templateRevision !== currentRevisionHash) {
|
|
233
|
-
return {
|
|
234
|
-
ready: false,
|
|
235
|
-
reason: "template_revision_mismatch",
|
|
236
|
-
attempts,
|
|
237
|
-
elapsedMs: Date.now() - start,
|
|
238
|
-
tableId: schema.tableId,
|
|
239
|
-
pollKey,
|
|
240
|
-
requestedTemplateRevision: templateRevision,
|
|
241
|
-
currentTemplateRevisionHash: currentRevisionHash,
|
|
242
|
-
guidance: "The requested templateRevision is not current. Re-read campaign state or wait using templateRevision:'current'.",
|
|
243
|
-
stats,
|
|
244
|
-
};
|
|
245
|
-
}
|
|
246
|
-
const passed = stats.passRate?.passed ?? 0;
|
|
247
|
-
const generated = templateRevision === "current" || templateRevision !== null
|
|
248
|
-
? (stats.currentRevisionGeneratedMessagesCount ?? 0)
|
|
249
|
-
: (stats.generatedMessagesCount ?? stats.messagesCount ?? 0);
|
|
250
|
-
const passFloorMet = minPassedCount === null || passed >= minPassedCount;
|
|
251
|
-
const generatedFloorMet = minGeneratedMessages === null || generated >= minGeneratedMessages;
|
|
252
|
-
if (passFloorMet && generatedFloorMet) {
|
|
253
|
-
return {
|
|
254
|
-
ready: true,
|
|
255
|
-
attempts,
|
|
256
|
-
elapsedMs: Date.now() - start,
|
|
257
|
-
tableId: schema.tableId,
|
|
258
|
-
pollKey,
|
|
259
|
-
floors: {
|
|
260
|
-
minPassedCount,
|
|
261
|
-
minGeneratedMessages,
|
|
262
|
-
templateRevision,
|
|
263
|
-
},
|
|
264
|
-
result: {
|
|
265
|
-
passedCount: passed,
|
|
266
|
-
generatedMessagesCount: stats.generatedMessagesCount ?? stats.messagesCount ?? 0,
|
|
267
|
-
currentRevisionGeneratedMessagesCount: stats.currentRevisionGeneratedMessagesCount ?? 0,
|
|
268
|
-
staleGeneratedMessagesCount: stats.staleGeneratedMessagesCount ?? 0,
|
|
269
|
-
generatedWithoutPassCount: stats.generatedWithoutPassCount ?? 0,
|
|
270
|
-
currentTemplateRevisionHash: currentRevisionHash,
|
|
271
|
-
},
|
|
272
|
-
stats,
|
|
273
|
-
};
|
|
274
|
-
}
|
|
275
|
-
await sleep(intervalMs);
|
|
276
|
-
}
|
|
277
|
-
const passed = lastStats?.passRate?.passed ?? 0;
|
|
278
|
-
return {
|
|
279
|
-
ready: false,
|
|
280
|
-
reason: guardApplied ? "tool_timeout_guard" : "timeout",
|
|
281
|
-
attempts,
|
|
282
|
-
elapsedMs: Date.now() - start,
|
|
283
|
-
tableId: schema.tableId,
|
|
284
|
-
pollKey,
|
|
285
|
-
warning: guardApplied
|
|
286
|
-
? `Stopped after ${effectiveTimeoutMs}ms to avoid host tool timeout (requested ${requestedTimeoutMs}ms).`
|
|
287
|
-
: undefined,
|
|
288
|
-
partialResult: {
|
|
289
|
-
passedCount: passed,
|
|
290
|
-
generatedMessagesCount: lastStats?.generatedMessagesCount ?? lastStats?.messagesCount ?? 0,
|
|
291
|
-
currentRevisionGeneratedMessagesCount: lastStats?.currentRevisionGeneratedMessagesCount ?? 0,
|
|
292
|
-
staleGeneratedMessagesCount: lastStats?.staleGeneratedMessagesCount ?? 0,
|
|
293
|
-
generatedWithoutPassCount: lastStats?.generatedWithoutPassCount ?? 0,
|
|
294
|
-
currentTemplateRevisionHash: lastStats?.currentTemplateRevisionHash ??
|
|
295
|
-
schema.currentApprovedBriefHash,
|
|
296
|
-
passFloorMet: minPassedCount === null || passed >= minPassedCount,
|
|
297
|
-
generatedFloorMet: minGeneratedMessages === null ||
|
|
298
|
-
(lastStats?.currentRevisionGeneratedMessagesCount ?? 0) >=
|
|
299
|
-
minGeneratedMessages,
|
|
300
|
-
},
|
|
301
|
-
guidance: "Do not repoll with identical arguments after this timeout. Use the partial diagnostics to revise filters/messages, queue missing work, or surface the blocked sample state.",
|
|
302
|
-
stats: lastStats,
|
|
303
|
-
};
|
|
304
|
-
}
|
|
@@ -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.
|