@sellable/mcp 0.1.149 → 0.1.151
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -4
- package/agents/post-find-leads-message-scout.md +2 -2
- package/agents/source-scout-prospeo-contact.md +12 -5
- package/agents/source-scout-sales-nav.md +13 -3
- package/dist/tools/leads.d.ts +2 -1
- package/dist/tools/leads.js +162 -15
- package/dist/tools/prompts.d.ts +1 -0
- package/dist/tools/prompts.js +4 -3
- package/dist/tools/registry.d.ts +9 -1
- package/dist/tools/rows.d.ts +1 -0
- package/dist/tools/rows.js +2 -0
- package/dist/tools/rubrics.d.ts +55 -23
- package/dist/tools/rubrics.js +95 -10
- package/package.json +1 -1
- package/skills/create-campaign/SKILL.md +57 -29
- package/skills/create-campaign/core/providers/apollo.json +4 -2
- package/skills/create-campaign/core/providers/prospeo.json +3 -2
- package/skills/create-campaign/references/provider-selection-strategy.md +66 -25
- package/skills/create-campaign-v2/SKILL.md +67 -37
- package/skills/create-campaign-v2/SOUL.md +11 -7
- package/skills/create-campaign-v2/core/flow.v2.json +70 -47
- package/skills/create-campaign-v2/references/approval-gate-framing.md +7 -8
- package/skills/create-campaign-v2/references/sample-validation-loop.md +9 -5
- package/skills/create-campaign-v2/references/step-13-import-leads.md +10 -4
- package/skills/create-campaign-v2/references/step-15-re-cascade.md +12 -7
- package/skills/create-campaign-v2/references/watch-guide-narration.md +16 -13
- package/skills/create-campaign-v2-tail/SKILL.md +24 -16
- package/skills/providers/apollo.md +8 -1
- package/skills/providers/prospeo.md +11 -1
- package/skills/providers/sales-nav.md +1 -1
- package/skills/research/config.json +9 -0
- package/skills/create-campaign-v2/references/message-review-safety-gate.md +0 -162
|
@@ -33,14 +33,13 @@ Before showing the commit gate, the draft directory must contain all of:
|
|
|
33
33
|
- `message-validation.md`
|
|
34
34
|
- `approval-packet.md`
|
|
35
35
|
|
|
36
|
-
The current run must also have retrieved message-generation rules
|
|
37
|
-
`message-validation.md`, `approval-packet.md`, or the commit gate is
|
|
38
|
-
`create-campaign-v2`, use the
|
|
39
|
-
`
|
|
40
|
-
`
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
commit gate; route back to message generation.
|
|
36
|
+
The current run must also have retrieved the full message-generation rules
|
|
37
|
+
before `message-validation.md`, `approval-packet.md`, or the commit gate is
|
|
38
|
+
written. In `create-campaign-v2`, use the embedded
|
|
39
|
+
`post-find-leads-message-scout` prompt or load the full long-form
|
|
40
|
+
`generate-messages` subskill. If the message template was written directly from
|
|
41
|
+
memory, checklist instructions, or planning artifacts, do not show the commit
|
|
42
|
+
gate; route back to message generation.
|
|
44
43
|
|
|
45
44
|
`rubric.json` is optional but strongly preferred — when missing, derive it from
|
|
46
45
|
`lead-filter.md` in the atomic mint step.
|
|
@@ -60,9 +60,10 @@ auto-revise leads.
|
|
|
60
60
|
explicit batch count anyway so future larger expansion batches do not
|
|
61
61
|
accidentally stop early
|
|
62
62
|
(see §Known Tool Behaviors #3)
|
|
63
|
-
- minPassedCount=1 means one passing filtered row unblocks Step 15
|
|
64
|
-
|
|
65
|
-
before
|
|
63
|
+
- minPassedCount=1 means one passing filtered row unblocks Step 15 Generate
|
|
64
|
+
Message observation. Step 15 then waits only for one generated message
|
|
65
|
+
(`minMessagesCount=1`) before review. Do not wait for all sample rows to
|
|
66
|
+
finish before messages start.
|
|
66
67
|
|
|
67
68
|
7. call `wait_for_rubric_results` with `includeRows=false`; extract ONLY:
|
|
68
69
|
- ready: boolean
|
|
@@ -101,7 +102,9 @@ auto-revise leads.
|
|
|
101
102
|
10. branch:
|
|
102
103
|
if passInSample >= 1:
|
|
103
104
|
proceed to Step 15 (auto-execute-messaging) with currently passing rows
|
|
104
|
-
so Generate Message can start without waiting for the full sample
|
|
105
|
+
so Generate Message can start without waiting for the full sample. Once
|
|
106
|
+
one passing generated message is ready, stop for user review instead of
|
|
107
|
+
waiting for a stronger sample.
|
|
105
108
|
else:
|
|
106
109
|
diagnose (see Brief-vs-List Diagnosis below)
|
|
107
110
|
revisionRound += 1
|
|
@@ -179,7 +182,8 @@ processing makes the experience feel frozen.
|
|
|
179
182
|
|
|
180
183
|
Workaround: treat timeout stats as a partial sample. If at least one row has
|
|
181
184
|
passed, move to Step 15 and observe or queue Generate Message for the passing
|
|
182
|
-
rows
|
|
185
|
+
rows; Step 15 stops when one generated message is ready. If zero rows have
|
|
186
|
+
passed and no active processing is visible, stop at
|
|
183
187
|
`Status: sample-needs-revision` before Settings. Show the completed / passed /
|
|
184
188
|
pending counts and ask whether to revise source, revise filter/rubric, or wait
|
|
185
189
|
once only if active processing is still visible.
|
|
@@ -106,7 +106,7 @@ fills the source lead list; it does **not** clone rows into the campaign table:
|
|
|
106
106
|
```text
|
|
107
107
|
import_leads({
|
|
108
108
|
campaignOfferId,
|
|
109
|
-
targetLeadCount: <sourceCandidateTarget
|
|
109
|
+
targetLeadCount: <sourceCandidateTarget from approved good-fit math>,
|
|
110
110
|
// Signal Discovery only:
|
|
111
111
|
targetEngagerCount: <ceil(targetGoodFitLeads / sampledFitRateAfterCleanup)>,
|
|
112
112
|
maxPostsToScrape: <postsNeeded from approved math>
|
|
@@ -114,9 +114,15 @@ import_leads({
|
|
|
114
114
|
```
|
|
115
115
|
|
|
116
116
|
Provider is inherited from the selected source decision (not re-selected here)
|
|
117
|
-
and should already be saved on the campaign before import.
|
|
118
|
-
|
|
119
|
-
|
|
117
|
+
and should already be saved on the campaign before import. For Sales Nav and
|
|
118
|
+
Prospeo, `<sourceCandidateTarget>` is not the 25-row review batch. It is the
|
|
119
|
+
source-list export/materialization count from the approved source math:
|
|
120
|
+
`min(rawResultCount, providerMax, ceil(targetGoodFitLeads /
|
|
121
|
+
projectedFitRateAfterCleanup))`. If projected good fits remain below the target
|
|
122
|
+
(often about 150+ usable prospects unless the campaign/source defaults specify
|
|
123
|
+
otherwise), return to find-leads and refine filters before import. Response
|
|
124
|
+
returns `{ imported, skipped, duplicates }` — surface all three to the
|
|
125
|
+
escalation logic so dedup ratios are visible.
|
|
120
126
|
|
|
121
127
|
For Signal Discovery, do not scrape every currently selected/promoted sample
|
|
122
128
|
post by default. Before `import_leads`, reconcile selected posts with the
|
|
@@ -4,15 +4,16 @@ This reference governs Step 15 (`auto-execute-messaging`) re-cascade
|
|
|
4
4
|
behavior when Step 14's validate-sample loop graduates additional rows
|
|
5
5
|
from pending → passed after the first Generate Message cells have already run.
|
|
6
6
|
|
|
7
|
-
Load whenever Step 15 is about to transition
|
|
8
|
-
`awaiting-user-greenlight`, and on every resume into Step 15.
|
|
7
|
+
Load whenever Step 15 is about to ask for generated-message review or transition
|
|
8
|
+
to `awaiting-user-greenlight`, and on every resume into Step 15.
|
|
9
9
|
|
|
10
10
|
## Principle
|
|
11
11
|
|
|
12
|
-
Generate Message cells are cascade-scoped. If Step 14's rubric flips rows
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
Generate Message cells are cascade-scoped. If Step 14's rubric flips rows from
|
|
13
|
+
pending → passed AFTER Step 15 first observes messages for the review batch, the
|
|
14
|
+
new rows can sit pending with no message. That must not block the first review
|
|
15
|
+
handoff. Step 15 opens review as soon as one passing generated message exists;
|
|
16
|
+
late-passed rows can be re-cascaded after explicit user continuation or resume.
|
|
16
17
|
|
|
17
18
|
Observed on manual Phase-85 signal-discovery run (2026-04-20): 15 new
|
|
18
19
|
rows graduated pending → passed mid-tail while `messagesCount` stayed
|
|
@@ -22,13 +23,17 @@ flat at 257. The new rows never got messages.
|
|
|
22
23
|
|
|
23
24
|
Re-cascade runs whenever ALL of the following are true:
|
|
24
25
|
|
|
25
|
-
1.
|
|
26
|
+
1. The user has already approved the first generated message or explicitly asked
|
|
27
|
+
to continue processing more review-batch rows.
|
|
26
28
|
2. A subsequent check of rubric state shows rows that were pending at
|
|
27
29
|
first message-generation pass are now passed.
|
|
28
30
|
3. Those newly-passed rows do NOT yet have generated messages
|
|
29
31
|
(`messagesCount` flat relative to pre-cascade).
|
|
30
32
|
4. Step 15 has NOT yet transitioned to `awaiting-user-greenlight`.
|
|
31
33
|
|
|
34
|
+
Before that first generated-message approval, do not run this loop just to wait
|
|
35
|
+
for a bigger sample. One passing generated message is enough for review.
|
|
36
|
+
|
|
32
37
|
If condition 4 already fired (i.e. we're in Step 16), the re-cascade
|
|
33
38
|
runs on resume into Step 15 if validate-sample was re-entered and new
|
|
34
39
|
rows graduated.
|
|
@@ -194,10 +194,10 @@ After review batch import:
|
|
|
194
194
|
```json
|
|
195
195
|
{
|
|
196
196
|
"stage": "fit-message",
|
|
197
|
-
"headline": "
|
|
198
|
-
"visibleState": "
|
|
199
|
-
"agentIntent": "Codex is
|
|
200
|
-
"nextAction": "
|
|
197
|
+
"headline": "Filtering may not be needed",
|
|
198
|
+
"visibleState": "25 review leads are in the campaign table from 181 source candidates. Sample read: 23/25 look like senior target-role fits; 2 need cleanup or manual review. The browser is showing the filter-choice screen.",
|
|
199
|
+
"agentIntent": "Codex is not recommending filters because the sample already looks clean. Fit examples: Dave Kranowitz (Chief Revenue Officer); Marie Didominica (Head of Growth Marketing). Add filters only for a narrow exclusion.",
|
|
200
|
+
"nextAction": "Skip filters or add a narrow cleanup rule",
|
|
201
201
|
"workerStatuses": {
|
|
202
202
|
"leadFitBuilder": "idle",
|
|
203
203
|
"messageDraftBuilder": "running"
|
|
@@ -206,11 +206,13 @@ After review batch import:
|
|
|
206
206
|
```
|
|
207
207
|
|
|
208
208
|
Use this after the selected review rows are present and before filters are
|
|
209
|
-
saved.
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
209
|
+
saved. The filter recommendation must be row-specific: say why filters are
|
|
210
|
+
recommended from the sample counts and examples, or say filtering may not be
|
|
211
|
+
needed when the visible review rows already look clean. This is a user decision
|
|
212
|
+
gate, not active filtering; do not say filtering the batch before rubrics and
|
|
213
|
+
message approval are saved. The Message Draft Builder should start immediately
|
|
214
|
+
after the bounded review batch exists; mark it running only when that branch
|
|
215
|
+
actually started.
|
|
214
216
|
When the user chooses filters, immediately persist `enableICPFilters: true` and
|
|
215
217
|
move to `create-icp-rubric` so the watched app shows Filter Rules while Codex
|
|
216
218
|
defines the rules in chat. After `save_rubrics`, move to `apply-icp-rubric`
|
|
@@ -252,16 +254,17 @@ Template approved, bounded filter test running:
|
|
|
252
254
|
"stage": "fit-message",
|
|
253
255
|
"headline": "Template saved",
|
|
254
256
|
"visibleState": "The browser stays on Filter Leads while the bounded enrichment and filter test runs.",
|
|
255
|
-
"agentIntent": "Codex saved the approved message template, queued the review-batch Enrich Prospect cells, and is waiting for
|
|
256
|
-
"nextAction": "
|
|
257
|
+
"agentIntent": "Codex saved the approved message template, queued the review-batch Enrich Prospect cells, and is waiting for one passing row with one generated message before moving to review.",
|
|
258
|
+
"nextAction": "Review the first passing generated message"
|
|
257
259
|
}
|
|
258
260
|
```
|
|
259
261
|
|
|
260
262
|
Do not move to Messages immediately after `approve-message`. The visible route
|
|
261
263
|
is already Filter Leads after `save_rubrics`; approving the message only unlocks
|
|
262
264
|
the bounded cascade from that screen. Move to Messages only once at least one
|
|
263
|
-
review-batch row passes and
|
|
264
|
-
|
|
265
|
+
review-batch row passes and one generated message is ready for review. Do not
|
|
266
|
+
wait for the rest of the review batch or a stronger sample before asking the
|
|
267
|
+
user to approve the generated message.
|
|
265
268
|
|
|
266
269
|
Messages waiting for template:
|
|
267
270
|
|
|
@@ -58,11 +58,12 @@ Step 14 — kick bounded cascade + observe sample
|
|
|
58
58
|
passing filtered row exists.)
|
|
59
59
|
|
|
60
60
|
Step 15 — observe messaging
|
|
61
|
-
|
|
61
|
+
wait_for_rubric_results({ minPassedCount: 1, minMessagesCount: 1, includeRows: false })
|
|
62
|
+
get_rows_minimal # confirm the first passing generated message exists
|
|
62
63
|
(rare) queue_cells on any pending Generate Message cells
|
|
63
64
|
token-contract spot check via get_rows
|
|
64
65
|
update_campaign(currentStep=auto-execute-messaging) with review-ready narration
|
|
65
|
-
ask the user to approve generated
|
|
66
|
+
ask the user to approve the generated message before Settings
|
|
66
67
|
only after approval: update_campaign(currentStep=awaiting-user-greenlight)
|
|
67
68
|
(generate_messages is NOT an MCP tool; messages come from the cascade)
|
|
68
69
|
|
|
@@ -291,8 +292,10 @@ Do not route to a visible `validate-sample` step. Full decision tree lives in
|
|
|
291
292
|
review batch only. After `save_rubrics` and the approved message template are
|
|
292
293
|
persisted, Step 14 queues the review-batch Enrich Prospect cells, waits until
|
|
293
294
|
filter results start landing, then moves to message observation as soon as one
|
|
294
|
-
row passes.
|
|
295
|
-
|
|
295
|
+
row passes. Step 15 opens review as soon as one passing generated message
|
|
296
|
+
exists. Do not wait for a larger or stronger sample once that first passing
|
|
297
|
+
message is ready. It does NOT call `check_rubric`,
|
|
298
|
+
`bulk_enrich_with_prospeo`, or any other direct enrichment/scoring tool.
|
|
296
299
|
|
|
297
300
|
Shape:
|
|
298
301
|
|
|
@@ -307,6 +310,7 @@ projectedPass = round(passInSample / sampleSize * importLimit)
|
|
|
307
310
|
if wait_for_rubric_results.ready === true and passRate.passed >= 1:
|
|
308
311
|
advance to Step 15 to observe or queue Generate Message for currently passing rows
|
|
309
312
|
do not wait for every sample row to finish before message generation starts
|
|
313
|
+
stop for review as soon as one passing generated message is ready
|
|
310
314
|
else if wait_for_rubric_results.ready === false and reason === "timeout":
|
|
311
315
|
use the partial passRate/stats as the sample diagnostic
|
|
312
316
|
if passRate.passed >= 1:
|
|
@@ -382,17 +386,20 @@ Template`. If it does not, fail before the cascade runs. Do not repair
|
|
|
382
386
|
already (cascade auto-fired it). If it is still `pending`, queue
|
|
383
387
|
it explicitly: `queue_cells({ tableId, cellIds:
|
|
384
388
|
<generateMessageCellIds> })`.
|
|
385
|
-
3. `
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
+
3. `wait_for_rubric_results({ tableId, targetCount: reviewBatchSize,
|
|
390
|
+
minPassedCount: 1, minMessagesCount: 1, includeRows: false })` until the
|
|
391
|
+
first passing generated message is ready. Do not wait for every passing row's
|
|
392
|
+
Generate Message cell, and do not add "one more wait" for a stronger sample.
|
|
393
|
+
Remaining review-batch rows can continue processing in the background.
|
|
394
|
+
4. Read the first ready generated message back via `get_rows` (full) and
|
|
395
|
+
sanity-check that sample against the Phase 84 token contract: no unresolved
|
|
389
396
|
`{{tokens}}`, no invented proof, one sentence per line, etc.
|
|
390
397
|
5. If the sample fails the token contract, diagnose brief-vs-list
|
|
391
398
|
(same revision loop as Step 14) and escalate if over
|
|
392
399
|
`maxRevisionRounds`.
|
|
393
|
-
6. On success, keep `currentStep: "auto-execute-messaging"` and ask the user
|
|
394
|
-
|
|
395
|
-
the campaign to Settings / `awaiting-user-greenlight`.
|
|
400
|
+
6. On success, keep `currentStep: "auto-execute-messaging"` and ask the user to
|
|
401
|
+
approve the generated message before continuing to Settings. Only that
|
|
402
|
+
approval may move the campaign to Settings / `awaiting-user-greenlight`.
|
|
396
403
|
|
|
397
404
|
**Do NOT hand-write message bodies via `update_cell`.** `update_cell`
|
|
398
405
|
is reachable for legitimate operator overrides AFTER the tail hands
|
|
@@ -432,15 +439,16 @@ strings, which is why Step 16 requires Step 15 to be complete.
|
|
|
432
439
|
4. Check `messaging.tokenContract`. When `strict`, reject any sample
|
|
433
440
|
message that contains unresolved or unsupported tokens (including
|
|
434
441
|
any critique rewrite that tried to introduce one).
|
|
435
|
-
5. If the
|
|
436
|
-
stop at the review-batch handoff. Do NOT
|
|
437
|
-
|
|
442
|
+
5. If the first passing generated message passes the token contract (and
|
|
443
|
+
critique when enabled), stop at the review-batch handoff. Do NOT wait for the
|
|
444
|
+
remaining review-batch rows and do NOT scale to the full source list before
|
|
445
|
+
explicit user expansion approval.
|
|
438
446
|
6. If the sample fails the token contract or critique, diagnose +
|
|
439
447
|
loop the same way Step 14 does (brief-vs-list), subject to the same
|
|
440
448
|
`maxRevisionRounds` cap.
|
|
441
449
|
7. On success, keep `currentStep: "auto-execute-messaging"`, show that the
|
|
442
|
-
|
|
443
|
-
Settings. Only after the user approves
|
|
450
|
+
first passing generated message is ready in Messages, and ask for approval
|
|
451
|
+
before Settings. Only after the user approves the generated message should
|
|
444
452
|
`update_campaign({ campaignId, currentStep: "awaiting-user-greenlight" })`
|
|
445
453
|
run.
|
|
446
454
|
|
|
@@ -378,7 +378,14 @@ search_apollo({
|
|
|
378
378
|
|
|
379
379
|
### ABM / Domain List Targeting
|
|
380
380
|
|
|
381
|
-
For
|
|
381
|
+
For campaign routing, supplied company-domain lists should go to Prospeo first
|
|
382
|
+
using `load_csv_domains` or `save_domain_filters`, then `domainFilterId`.
|
|
383
|
+
Apollo still supports `q_organization_domains_list` as a secondary/legacy
|
|
384
|
+
capability when the user explicitly chooses Apollo or needs an Apollo-only
|
|
385
|
+
filter such as a technographic constraint.
|
|
386
|
+
|
|
387
|
+
If Apollo is explicitly selected for account-based targeting, use
|
|
388
|
+
`q_organization_domains_list` to search specific companies:
|
|
382
389
|
|
|
383
390
|
```json
|
|
384
391
|
search_apollo({
|
|
@@ -62,7 +62,7 @@ search_prospeo({
|
|
|
62
62
|
- Pass `searchId` on subsequent pages to paginate.
|
|
63
63
|
- Use `import_leads` with `provider: \"prospeo\"` and the `searchId` to create a lead list and start import.
|
|
64
64
|
- **IMPORTANT:** If `import_leads` returns `needsModeSelection: true`, use `AskUserQuestion` to ask "add to existing leads or replace?" Do NOT assume.
|
|
65
|
-
- Default source target is
|
|
65
|
+
- Default source target is about 150 good-fit leads, capped at 2,500 source candidates for now.
|
|
66
66
|
- Apply the campaign source planning floor: the sampled/projected good-fit rate
|
|
67
67
|
after cleanup should be at least 10%. Prospeo is the terminal fallback; if the
|
|
68
68
|
best reasonable Prospeo lane is still below 10%, tighten the ICP/source
|
|
@@ -95,6 +95,11 @@ Prospeo filters are split into person vs company. Most filters use `include` / `
|
|
|
95
95
|
|
|
96
96
|
Preference rules:
|
|
97
97
|
|
|
98
|
+
- For hiring-led campaigns, use `company_job_posting_hiring_for` to target the
|
|
99
|
+
open-role themes and `company_job_posting_quantity` when the campaign needs
|
|
100
|
+
an active hiring floor. Pair those company hiring filters with buyer/referrer
|
|
101
|
+
person filters such as founders, GTM leaders, talent leaders, or revenue
|
|
102
|
+
owners.
|
|
98
103
|
- For ABM lists, use `load_csv_domains` + `domainFilterId` when the accounts are already in a CSV file on disk.
|
|
99
104
|
- For pasted/raw domain lists, use `save_domain_filters` + `domainFilterId` instead of inline domains or `company.websites.include`.
|
|
100
105
|
- If user input starts as company names, resolve names to domains first, then use `save_domain_filters`.
|
|
@@ -167,6 +172,11 @@ search_prospeo({
|
|
|
167
172
|
|
|
168
173
|
## When to Use Prospeo
|
|
169
174
|
|
|
175
|
+
- **Hiring-led targeting**: Find companies hiring for specific roles with
|
|
176
|
+
`company_job_posting_hiring_for` and `company_job_posting_quantity`, then
|
|
177
|
+
identify reachable buyer/referrer contacts at those companies
|
|
178
|
+
- **Account/domain targeting**: Use `domainFilterId` from `load_csv_domains` or
|
|
179
|
+
`save_domain_filters` for ABM searches
|
|
170
180
|
- **Email Finding**: Get verified work emails from LinkedIn URLs
|
|
171
181
|
- **Lead Enrichment**: Add company data, job history, and contact info to leads
|
|
172
182
|
- **Bulk Operations**: Enrich up to 100 leads at once
|
|
@@ -387,7 +387,7 @@ User: "Save it"
|
|
|
387
387
|
<limits>
|
|
388
388
|
- 25 results per page
|
|
389
389
|
- Maximum 100 pages (2,500 leads)
|
|
390
|
-
- Default source target:
|
|
390
|
+
- Default source target: about 150 good-fit leads, capped at 2,500 source
|
|
391
391
|
candidates for now
|
|
392
392
|
- Maximum 5 search calls per session
|
|
393
393
|
- If user needs more than 2,500: explain limit, suggest splitting by region
|
|
@@ -1,162 +0,0 @@
|
|
|
1
|
-
# Message Review Safety Gate
|
|
2
|
-
|
|
3
|
-
Use this reference for `create-campaign-v2` message review only in two cases:
|
|
4
|
-
when these rules are embedded inside the `post-find-leads-message-scout` agent
|
|
5
|
-
prompt, or when the host cannot launch that agent and the parent thread needs a
|
|
6
|
-
self-contained safety gate. It is the campaign-launch subset of
|
|
7
|
-
`generate-messages`: enough to prove a truthful first-send message, rendered
|
|
8
|
-
token examples, and an approval decision without loading the full long-form
|
|
9
|
-
message-generation prompt into the main Claude/Codex thread.
|
|
10
|
-
|
|
11
|
-
In a normal installed Claude/Codex session, the message scout owns these rules.
|
|
12
|
-
The parent-thread asset is a compatibility safety path, not a fast-mode prompt
|
|
13
|
-
or a shortcut. Do not load the full `generate-messages` subskill in this flow. If
|
|
14
|
-
the safety gate is missing a needed campaign-specific rule or the draft fails
|
|
15
|
-
quality gates, stop at `revise-messaging` with the exact missing rule/failure
|
|
16
|
-
instead of pulling the long prompt into the main thread.
|
|
17
|
-
|
|
18
|
-
## Required Workflow
|
|
19
|
-
|
|
20
|
-
1. Read the live campaign basis: campaign id, campaign revision or
|
|
21
|
-
`campaignUpdatedAt`, campaign brief content, selected source decision,
|
|
22
|
-
selected lead list/source state, `workflowTableId`, imported review-batch
|
|
23
|
-
row ids/hash, and any saved rubric/filter summary supplied by the parent.
|
|
24
|
-
2. Pick 2-3 sample rows from the imported review-batch rows, preferring rows
|
|
25
|
-
that pass the saved rubrics or are likely to pass. Do not invent new rows.
|
|
26
|
-
3. Build the message from the approved campaign brief, selected source context,
|
|
27
|
-
sampled rows, and explicit user answers. Use only proof that appears in that
|
|
28
|
-
live basis. Unsupported reply-rate, meeting-rate, ROI, revenue, and
|
|
29
|
-
customer-logo claims are blocked.
|
|
30
|
-
4. Return a compact message recommendation to the parent thread. Do not write
|
|
31
|
-
local markdown/json artifacts in normal customer runs; emit debug artifacts
|
|
32
|
-
only when the parent explicitly asks for debug/UAT output.
|
|
33
|
-
5. Render the customer-facing message review in chat before asking for approval.
|
|
34
|
-
Keep chat lightweight: show the tokenized template and one strong rendered
|
|
35
|
-
good-fill example only. Do not print the token notes table, omit/fallback
|
|
36
|
-
example, or bad-fill analysis in chat unless the user explicitly asks.
|
|
37
|
-
After approval, the parent persists template, token rules, fallback guidance,
|
|
38
|
-
and bad-fill avoidance notes into campaign state with `update_campaign_brief`.
|
|
39
|
-
6. Ask exactly `approve-message` or `revise-messaging`. Do not import, queue,
|
|
40
|
-
attach sequence, or start before `approve-message`.
|
|
41
|
-
|
|
42
|
-
## Required Message Recommendation Sections
|
|
43
|
-
|
|
44
|
-
- `Status`
|
|
45
|
-
- `Mode`
|
|
46
|
-
- `Lead Sample Basis`
|
|
47
|
-
- `Strongest Reply Reason`
|
|
48
|
-
- `Campaign Element Pool`
|
|
49
|
-
- `Gold Standard Strategy Map`
|
|
50
|
-
- `Current Campaign Translation`
|
|
51
|
-
- `Token Fill Rules`
|
|
52
|
-
- `Token Adherence Table`
|
|
53
|
-
- `Angle Drafts`
|
|
54
|
-
- `Kill / Combine Review`
|
|
55
|
-
- `Finalizer Pass`
|
|
56
|
-
- `Gold-Standard Quality Gate`
|
|
57
|
-
- `Skeptical Prospect Review`
|
|
58
|
-
- `Winner Gate`
|
|
59
|
-
- `Selected Winner`
|
|
60
|
-
- `Findings`
|
|
61
|
-
- `Recommendation`
|
|
62
|
-
|
|
63
|
-
Keep this recommendation concise. It should prove the reasoning path, not
|
|
64
|
-
reproduce the full framework.
|
|
65
|
-
|
|
66
|
-
## Message Quality Gates
|
|
67
|
-
|
|
68
|
-
- The selected winner must be one first outbound send only. No post-accept DM,
|
|
69
|
-
follow-up, cadence branch, or sequence copy.
|
|
70
|
-
- The message must explain what the product is and what it does in plain
|
|
71
|
-
language before asking for a call.
|
|
72
|
-
- The message must use a truthful buyer-side reply reason, not just sender
|
|
73
|
-
biography.
|
|
74
|
-
- If using a template, include at least one supported `{{token}}` and show a
|
|
75
|
-
complete rendered good-fill example and complete rendered omit/fallback
|
|
76
|
-
example.
|
|
77
|
-
- Do not use internal tokens such as `{{profile_signal}}` in customer-facing
|
|
78
|
-
copy.
|
|
79
|
-
- Do not put bracketed instructions in the message body, such as `[ROW_BRIDGE]`,
|
|
80
|
-
`[insert]`, `[generated]`, or any instruction for a later model to fill.
|
|
81
|
-
- Optional row-specific personalization must be grounded in a row field or
|
|
82
|
-
omitted entirely. Never use generic filler like "your work" or "your team."
|
|
83
|
-
- Engagement-source personalization is a special case, not the default opener.
|
|
84
|
-
Do not write `saw you {{engagement_context}} on {{post_context}}`, `saw you
|
|
85
|
-
reacted to`, `saw you engaging with`, or equivalent source-citation copy as a
|
|
86
|
-
default hook. Only refer to the prospect's engagement when the line is
|
|
87
|
-
self-aware and low-certainty, for example `not sure if this is too specific,
|
|
88
|
-
but the [topic] thread felt close enough to send`. Otherwise omit the
|
|
89
|
-
engagement signal and use role/company/problem context.
|
|
90
|
-
- Subject lines should be short, buyer-relevant, and specific. Avoid
|
|
91
|
-
`quick question`, `demo`, `founder call`, sender names, and generic `outbound`.
|
|
92
|
-
- If the message is plausible but not ready to send, set
|
|
93
|
-
`Recommendation: revise-messaging`.
|
|
94
|
-
|
|
95
|
-
## Internal Message Recommendation
|
|
96
|
-
|
|
97
|
-
The message scout or parent safety gate should still produce complete internal
|
|
98
|
-
review data: token-fill rules, one rendered omit/fallback example, and bad-fill
|
|
99
|
-
avoidance notes. This data is for campaign-brief persistence and safety checks,
|
|
100
|
-
not for the default chat approval packet.
|
|
101
|
-
|
|
102
|
-
## Customer-Facing Message Review
|
|
103
|
-
|
|
104
|
-
Render this in chat before asking. Use Markdown structure so the approval target
|
|
105
|
-
is visually scannable and low-overhead:
|
|
106
|
-
|
|
107
|
-
````markdown
|
|
108
|
-
Status: message-review
|
|
109
|
-
|
|
110
|
-
## Message Template
|
|
111
|
-
|
|
112
|
-
**Subject**
|
|
113
|
-
|
|
114
|
-
```text
|
|
115
|
-
{{tokenized_subject}}
|
|
116
|
-
```
|
|
117
|
-
|
|
118
|
-
**Body**
|
|
119
|
-
|
|
120
|
-
```text
|
|
121
|
-
{{tokenized_message_body}}
|
|
122
|
-
```
|
|
123
|
-
|
|
124
|
-
## Rendered Example
|
|
125
|
-
|
|
126
|
-
### Example
|
|
127
|
-
|
|
128
|
-
Good token fill:
|
|
129
|
-
|
|
130
|
-
```text
|
|
131
|
-
Subject: ...
|
|
132
|
-
|
|
133
|
-
Hey First,
|
|
134
|
-
|
|
135
|
-
...
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
## Recommendation
|
|
139
|
-
|
|
140
|
-
**My take:** ...
|
|
141
|
-
|
|
142
|
-
**Suggested adjustment:** ...
|
|
143
|
-
|
|
144
|
-
**Question:** approve-message or revise-messaging?
|
|
145
|
-
|
|
146
|
-
**Recommendation:** approve-message
|
|
147
|
-
````
|
|
148
|
-
|
|
149
|
-
Formatting requirements:
|
|
150
|
-
|
|
151
|
-
- Put the tokenized template and rendered example in fenced `text` blocks
|
|
152
|
-
so chat gives them a distinct background.
|
|
153
|
-
- The chat example must contain a complete rendered subject + body, not a
|
|
154
|
-
bullet list of token names or a single bridge-line fragment.
|
|
155
|
-
- Do not show `Good omit / fallback`, `Bad fill to avoid`, or `Token Notes` in
|
|
156
|
-
the default chat approval packet. Keep those in the internal recommendation
|
|
157
|
-
and persist them to the campaign brief after approval.
|
|
158
|
-
- Keep reasoning outside the code blocks so the blocks are easy to inspect and
|
|
159
|
-
approve.
|
|
160
|
-
|
|
161
|
-
`My take` and `Suggested adjustment` must be specific. The adjustment can be
|
|
162
|
-
"approve as-is, or revise once to X if you want Y"; it must not be empty.
|