@sellable/mcp 0.1.38-rc.3 → 0.1.38
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/package.json
CHANGED
|
@@ -761,17 +761,17 @@ Do not:
|
|
|
761
761
|
|
|
762
762
|
## Step 3: Generate Message
|
|
763
763
|
|
|
764
|
-
|
|
764
|
+
Step 3 follows the canonical `generate-messages` subskill — gold-standard examples, REPLY framework fallback, Proof Inventory, Token Fill Rules, Token Adherence Table, Angle Drafts, Kill/Combine Review, Finalists, Finalizer Pass, Gold-Standard Quality Gate, Skeptical Prospect Review, Winner Gate, and the message-review-decision contract. The `generate-messages` subskill is the authoritative source — this entry prompt does not duplicate it.
|
|
765
765
|
|
|
766
|
-
When you reach Step 3,
|
|
766
|
+
When you reach Step 3, load the canonical subskill verbatim via chunked reads:
|
|
767
767
|
|
|
768
768
|
```
|
|
769
|
-
mcp__sellable__get_subskill_prompt({ subskillName: "
|
|
769
|
+
mcp__sellable__get_subskill_prompt({ subskillName: "generate-messages", offset, limit })
|
|
770
770
|
```
|
|
771
771
|
|
|
772
|
-
|
|
772
|
+
Continue chunked-read until `hasMore=false`. Treat all chunks as one prompt load. Follow it verbatim once loaded. The same directive is locked in `flow.v2.json::messageGeneration.toolRules`.
|
|
773
773
|
|
|
774
|
-
Do NOT proceed to Step 4 (message review gate) without loading and following the generate-
|
|
774
|
+
Do NOT proceed to Step 4 (message review gate) without loading and following the full generate-messages workflow. `message-validation.md` must prove the full workflow ran (all required sections present + raw sendable Selected Winner) before `message-review.md` can recommend `approve-message`. If the message review or approval packet is ready but the generate-messages prompt was not retrieved in this run, route back to message-generation instead of asking the commit gate.
|
|
775
775
|
|
|
776
776
|
## Tail (MANDATORY TOOL ORDER + Steps 13-16 + Threshold Trips + Hard Rules)
|
|
777
777
|
|
|
@@ -1,838 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: create-campaign-v2-generate-message
|
|
3
|
-
description: Step 3 (Generate Message) detailed workflow for create-campaign-v2 — full message-validation + gold-standard rules + proof inventory + candidate generation + finalizer + message-review-decision contract. Loaded on-demand via get_subskill_prompt to keep the entry SKILL.md slim. Internal subskill.
|
|
4
|
-
visibility: internal
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
# Step 3: Generate Message (Detail)
|
|
8
|
-
|
|
9
|
-
This is the full Step 3 detail extracted from the main create-campaign-v2 SKILL.md to keep the entry prompt slim. The agent loads this on-demand when entering Step 3 of the v2 flow. Follow this verbatim.
|
|
10
|
-
|
|
11
|
-
## Step 3: Generate Message
|
|
12
|
-
|
|
13
|
-
Read `brief.md`, `lead-review.md`, `lead-sample.json`, optional
|
|
14
|
-
`lead-filter.md`, optional `message-prep.md`, and optional
|
|
15
|
-
`message-candidate-drafts.md`.
|
|
16
|
-
|
|
17
|
-
This branch can run in parallel with Step 2 once `lead-review.md` and
|
|
18
|
-
`lead-sample.json` exist. If `lead-filter.md` is not ready yet, draft from the
|
|
19
|
-
lead-review message handoff rows, then reconcile after the filter lands.
|
|
20
|
-
|
|
21
|
-
Write:
|
|
22
|
-
|
|
23
|
-
- `message-validation.md`
|
|
24
|
-
|
|
25
|
-
Run the `generate-messages` skill in caller-declared `DRY MODE`. Its SKILL.md
|
|
26
|
-
holds the full drafting contract (retrieval, proof inventory, candidates,
|
|
27
|
-
finalizer pass, voice rules, safety). This step only covers orchestration.
|
|
28
|
-
This is not optional: before any write to `message-validation.md`,
|
|
29
|
-
`message-review.md`, `approval-packet.md`, or a commit-gate question, the
|
|
30
|
-
current run must load the full `generate-messages` prompt. In hosted/from-
|
|
31
|
-
scratch runs, load it with chunked `get_subskill_prompt({ subskillName:
|
|
32
|
-
"generate-messages", offset, limit })` calls so every tool result stays small
|
|
33
|
-
enough for the streamed harness. Start with `offset: 0`; the MCP will apply a
|
|
34
|
-
portable default limit when omitted. Keep calling with `offset: nextOffset`
|
|
35
|
-
until `hasMore` is false.
|
|
36
|
-
In Codex-hosted runs, this is a quality gate, not just provenance: if the
|
|
37
|
-
model cannot retrieve the complete prompt or cannot follow the required
|
|
38
|
-
gold-standard deliberation flow, stop at `message-review` and ask for
|
|
39
|
-
`revise-messaging`. Do not mint the campaign to compensate for weak copy.
|
|
40
|
-
Do not hand-write `message-validation.md` from `message-prep.md` or
|
|
41
|
-
`message-candidate-drafts.md`. Those files are planning inputs only; the final
|
|
42
|
-
message-validation artifact must come from the actual `generate-messages`
|
|
43
|
-
prompt path (`get_message_prompt` / `generate-messages`).
|
|
44
|
-
|
|
45
|
-
Orchestration requirements:
|
|
46
|
-
|
|
47
|
-
- start once real leads exist: `brief.md`, `lead-review.md`, and
|
|
48
|
-
`lead-sample.json` are enough to run generate-message
|
|
49
|
-
- use only the find-leads message handoff rows as basis examples, e.g.
|
|
50
|
-
"prospects 1, 5, and 6 are solid enough to think from"
|
|
51
|
-
- if `lead-filter.md` is ready, use it; if it arrives after message drafting,
|
|
52
|
-
reconcile before approval and rerun or revise messaging if the selected
|
|
53
|
-
winner depended on rows that fail the final filter
|
|
54
|
-
- never write `message-validation.md`, render a message review, render an
|
|
55
|
-
approval packet, or ask the commit gate until 100% of the real
|
|
56
|
-
`generate-messages` prompt has been read
|
|
57
|
-
- before `message-review` can recommend `approve-message`, verify
|
|
58
|
-
`message-validation.md` contains the full generate-messages shape:
|
|
59
|
-
`Gold Standard Strategy Map`, `Proof Inventory`, `Token Fill Rules`,
|
|
60
|
-
`Token Adherence Table`, `Angle Drafts`, `Kill / Combine Review`,
|
|
61
|
-
`Finalists`, `Finalizer Pass`, `Gold-Standard Quality Gate`,
|
|
62
|
-
`Skeptical Prospect Review`, `Winner Gate`, and a raw sendable
|
|
63
|
-
`Selected Winner`. If any are missing, recommend `revise-messaging` and do
|
|
64
|
-
not continue to approval or mint.
|
|
65
|
-
- pass no `campaignId`
|
|
66
|
-
- read only `brief.md`, `lead-review.md`, `lead-sample.json`, optional
|
|
67
|
-
`lead-filter.md`, and the `gold-standard-*` references. If
|
|
68
|
-
`message-prep.md` exists, use it as a
|
|
69
|
-
planning cache instead of redoing the same element inventory from scratch. If
|
|
70
|
-
`message-candidate-drafts.md` exists, use it as provisional draft input only
|
|
71
|
-
after checking its basis rows against the completed `lead-filter.md`, when
|
|
72
|
-
available.
|
|
73
|
-
- generate 2-3 sample messages inline
|
|
74
|
-
- start output with `Mode: DRY MODE (no DB mutation)`
|
|
75
|
-
- treat the archived examples as the **quality bar**, not a paste source;
|
|
76
|
-
write messages that could plausibly belong in the archive for this motion
|
|
77
|
-
- exact-template preservation only applies when the archived winner is the
|
|
78
|
-
same company as the brief
|
|
79
|
-
- draft 3 internal candidates and run a Finalizer Pass that combines the
|
|
80
|
-
best opener, proof sentence, bridge, and CTA across them into one winner
|
|
81
|
-
- if all finalists use the same first substantive line, treat the opener
|
|
82
|
-
test as failed; compare materially different opener jobs before selecting
|
|
83
|
-
a winner, or route to `revise-message`
|
|
84
|
-
- the Finalizer Pass must block adjacent repeated copy. Do not let a pain
|
|
85
|
-
line and mechanism line reuse the same noun stack unless the second line
|
|
86
|
-
adds plainly new buyer value. Merge, translate, bullet, or cut instead.
|
|
87
|
-
- the selected winner subject/body must not contain semicolons.
|
|
88
|
-
- the selected winner subject must not be generic category copy like
|
|
89
|
-
`first LinkedIn campaign`, `quick question`, `outbound`, or `intro`. Prefer the
|
|
90
|
-
original gold-standard A + B + C subject shape: a concrete row/company token
|
|
91
|
-
plus a concrete workflow/object plus the offer/result, for example
|
|
92
|
-
`{{company}} + LinkedIn outbound + Claude/Codex` or
|
|
93
|
-
`{{source_post_topic}} + active leads + first message test`. Keep it short,
|
|
94
|
-
buyer-native, and free of abstract category nouns.
|
|
95
|
-
- A + B + C is a quality shape, not permission to stuff tokens into the
|
|
96
|
-
subject. The subject must pique buyer interest and name a buyer-relevant
|
|
97
|
-
problem, workflow, or useful artifact. Do not use sender names, founder names,
|
|
98
|
-
"Austin's review", "founder call", "demo", "quick call", or similar
|
|
99
|
-
sender-centered language in the subject. Do not use a row token in the subject
|
|
100
|
-
unless the filled version is natural, lowercase/common-word where appropriate,
|
|
101
|
-
and more interesting than the non-tokenized version.
|
|
102
|
-
- the selected winner must not contain sender-intro fragments like
|
|
103
|
-
`Usama here`, `derm here`, `board-cert derm here`, `[credential] here`,
|
|
104
|
-
or `[role] at [company] here`.
|
|
105
|
-
- the selected winner must not use generic signal tokens such as
|
|
106
|
-
`{{recentSignal}}`, `{{recent_signal}}`, or `{{recent_signal_quote}}`.
|
|
107
|
-
Row personalization must use concrete enriched-row fields such as
|
|
108
|
-
`{{post_context}}`, `{{comment_summary}}`, `{{profile_summary}}`,
|
|
109
|
-
`{{source_post_topic}}`, `{{headline}}`, or `{{row_proof_note}}`, and it
|
|
110
|
-
must avoid source-citation phrases like `caught my eye`.
|
|
111
|
-
- do not default to founder-to-founder, MD-to-MD, doctor-to-doctor,
|
|
112
|
-
peer-call, compare-notes, or similar identity-call CTA framing unless
|
|
113
|
-
the user explicitly selected that route or the brief makes it the
|
|
114
|
-
approved offer. Even then, the CTA must name the useful return artifact,
|
|
115
|
-
preview, teardown, or working session.
|
|
116
|
-
- if the message is selling or introducing a product, the selected winner
|
|
117
|
-
must make the product plain before asking for time, but the opener must
|
|
118
|
-
not sound like homepage copy. A line shaped like `[Product] is a
|
|
119
|
-
[category] platform -- feature, feature, feature` is blocked when it reads
|
|
120
|
-
like product copy instead of a human note. For command-native products,
|
|
121
|
-
describe the buyer job (`launch a LinkedIn campaign from Claude/Codex`)
|
|
122
|
-
before category nouns like `platform` or `MCP`.
|
|
123
|
-
- pass the Thomas revision filters before writing findings
|
|
124
|
-
- immediately after `message-validation.md` is confirmed and reconciled with
|
|
125
|
-
`lead-filter.md`, write `message-review.md` and render the slim chat version
|
|
126
|
-
inline starting with `Status: message-review` as the first visible line. Do
|
|
127
|
-
not put a markdown heading, preface, or summary before that status line.
|
|
128
|
-
This is a customer checkpoint optimized for fast approve / revise judgment.
|
|
129
|
-
- Chat-vs-file split (this is the rule the user pinned):
|
|
130
|
-
- `message-review.md` (the file) keeps the full detail: `Status:
|
|
131
|
-
message-review`, `Subject:`, `Tokenized template:`, `Sample prospect fill:` + `Token fill basis:`, `Rendered examples:`, `Good token fill:`,
|
|
132
|
-
`Good omit:`, `Bad token fill:`, `Why bad:`, `Fallback if missing:`,
|
|
133
|
-
`Token notes:`, `My take:`, `Suggested adjustment:`, `Question:
|
|
134
|
-
approve-message or revise-messaging?`, `Recommendation:
|
|
135
|
-
approve-message|revise-messaging`. The detailed gold-standard/rule audit
|
|
136
|
-
stays in `message-validation.md`. Both files are unchanged in scope and
|
|
137
|
-
are still required.
|
|
138
|
-
- The chat rendering shows ONLY: `Status: message-review`, `Subject:`,
|
|
139
|
-
`Tokenized template:`, `Sample prospect fill:` (with `Token fill basis:`),
|
|
140
|
-
`My take:`, `Suggested adjustment:`, `Question: approve-message or
|
|
141
|
-
revise-messaging?`, `Recommendation:`. Then `Open artifacts:` links to
|
|
142
|
-
`message-review.md` and `message-validation.md` so the user can read the
|
|
143
|
-
full token guidance, good/omit/bad examples, and fallbacks if they want
|
|
144
|
-
to before deciding.
|
|
145
|
-
- Do NOT inline `Rendered examples:`, `Good token fill:`, `Good omit:`,
|
|
146
|
-
`Bad token fill:`, `Why bad:`, `Fallback if missing:`, or `Token notes:`
|
|
147
|
-
in chat. Those exist in the file and are linked. Keeping them out of
|
|
148
|
-
chat is what makes the gate easy to approve or revise on.
|
|
149
|
-
- Never ask the message approval question until the slim chat review is fully
|
|
150
|
-
visible. A summary like `Message review is ready` or `the draft avoids a
|
|
151
|
-
generic pitch` is not enough. The user must see the actual subject, the
|
|
152
|
-
actual tokenized message template, one filled sample prospect version with
|
|
153
|
-
its token fill basis, your take, and the suggested adjustment before any
|
|
154
|
-
`approve-message` / `revise-messaging` question appears. If you catch
|
|
155
|
-
yourself wanting to offer `show me message` as a choice, stop: render the
|
|
156
|
-
slim chat review first, then ask only `approve-message` or
|
|
157
|
-
`revise-messaging`. Include `Open artifacts:` links to `message-review.md`
|
|
158
|
-
and `message-validation.md` before the approval question.
|
|
159
|
-
- `My take:` and `Suggested adjustment:` are mandatory customer-facing decision
|
|
160
|
-
fields, not optional summary text. They must appear after `Token notes:` and
|
|
161
|
-
before the question in every rendered message review. They must each be
|
|
162
|
-
non-empty, specific to the current message, and useful for one last manual
|
|
163
|
-
feedback pass. Never render a message review that jumps straight from token
|
|
164
|
-
notes to `Question:` or `Recommendation:`.
|
|
165
|
-
- `Tokenized template:` in `message-review.md` must be a tokenized template with supported
|
|
166
|
-
enriched-row tokens such as `{{first_name}}`, `{{company}}`,
|
|
167
|
-
`{{workflow_context}}`, `{{headline}}`, `{{source_post_topic}}`, or
|
|
168
|
-
`{{row_proof_note}}`. It must not be a one-row-only raw sample. It must
|
|
169
|
-
include at least one literal `{{...}}` token. Keep only tokens that future
|
|
170
|
-
enriched rows can actually fill or safely omit. If you cannot produce a
|
|
171
|
-
tokenized template from supported enriched-row fields, stop with
|
|
172
|
-
`Recommendation: revise-messaging`.
|
|
173
|
-
- `Sample prospect fill:` must render the same template for one named sample
|
|
174
|
-
prospect from `lead-sample.json` or the selected row used in
|
|
175
|
-
`message-validation.md`. It must show the complete subject and body the
|
|
176
|
-
prospect would receive, plus a one-line `Token fill basis:` naming which row
|
|
177
|
-
fields filled each token. This is the user-facing proof that the template
|
|
178
|
-
works. Do not ask for approval if the user cannot compare the template to a
|
|
179
|
-
concrete filled message.
|
|
180
|
-
- `{{profile_signal}}` is never a supported customer-facing token. It is an
|
|
181
|
-
internal enrichment label and must not appear in `Subject:`, `Tokenized
|
|
182
|
-
template:`, `Sample prospect fill:`, rendered examples, `Token notes:`,
|
|
183
|
-
`message-validation.md` selected copy, or `approval-packet.md`. If a
|
|
184
|
-
profile-derived signal matters, translate it into a buyer-readable derived
|
|
185
|
-
token with a clear fill rule, such as `{{workflow_context}}` or
|
|
186
|
-
`{{row_proof_note}}`; otherwise omit the line.
|
|
187
|
-
- `Rendered examples:` must include at least one `Good token fill:` rendered
|
|
188
|
-
message where the row has a clean signal and one `Good omit:` rendered message
|
|
189
|
-
where an optional token line is omitted instead of forced. Each example must
|
|
190
|
-
be a complete rendered subject + body, not a bullet list of token names or a
|
|
191
|
-
single bridge-line fragment. Do not use bracketed instructions, deferred row
|
|
192
|
-
notes, or phrases like `approve the selected winner above`.
|
|
193
|
-
- `Bad token fill:` must show at least one realistic wrong fill for the current
|
|
194
|
-
campaign, followed by `Why bad:`. This teaches the user and the live campaign
|
|
195
|
-
runtime what not to do. Examples: overclaiming signal intent (`you are
|
|
196
|
-
evaluating Clay` when the only evidence is a post reaction), using forbidden
|
|
197
|
-
phrasing (`you engaged with content...` when the approved voice is `raised
|
|
198
|
-
your hand in a conversation around...`), forcing a weak optional token,
|
|
199
|
-
lowercasing proper nouns, or making row signal sound like AI mail merge.
|
|
200
|
-
- `Fallback if missing:` is required for every token in the template. Each
|
|
201
|
-
fallback must say exactly what to do when the row lacks clean data: use a
|
|
202
|
-
safe segment-level phrase, omit the sentence/line, or route to
|
|
203
|
-
`revise-messaging` if omission would break the message. It must include a
|
|
204
|
-
concrete example of the fallback behavior, such as `omit the noticed... line`,
|
|
205
|
-
`use "operators"`, or `use "your outbound workflow"`. Never leave a token
|
|
206
|
-
with "N/A", "unknown", empty braces, bracketed instructions, or a generic
|
|
207
|
-
fill that sounds robotic or creepy.
|
|
208
|
-
- rendered examples may only use token values that exist in `lead-sample.json`,
|
|
209
|
-
`lead-review.md`, `lead-filter.md`, or the selected winner's documented
|
|
210
|
-
supported-token plan. Do not invent example values to make the template look
|
|
211
|
-
better. If a token is absent, weak, or not a supported enriched-row field,
|
|
212
|
-
show the `Good omit:` version instead of filling it.
|
|
213
|
-
- token casing must look intentional. Keep proper nouns, recipient names,
|
|
214
|
-
company names, product names, acronyms, and the pronoun `I` capitalized in
|
|
215
|
-
both the template examples and rendered output. Casual lowercase is allowed
|
|
216
|
-
only for static common words when the chosen message voice uses it. Do not
|
|
217
|
-
lowercase token values or invent transform syntax like `{{company_lower}}`,
|
|
218
|
-
`{{first_name | lower}}`, or bracketed casing instructions.
|
|
219
|
-
- Token usage must be intelligent, not decorative. If a tokenized opener reads
|
|
220
|
-
like mail merge (`Reaching out because {{reporting_context}} sits in...`,
|
|
221
|
-
`your {{role_context}} work`, `saw your {{topic}}`, or similar), rewrite the
|
|
222
|
-
base line so the token either sounds like a natural noun phrase or is omitted.
|
|
223
|
-
Prefer row-derived tokens that change the reply reason; otherwise use a clean
|
|
224
|
-
segment-level line and document why the row token is omitted.
|
|
225
|
-
- founder/sender names should not be used as the hook. Mention a founder name
|
|
226
|
-
only when the buyer already has a reason to care about that person or when the
|
|
227
|
-
user explicitly wants named-founder branding. In normal first-touch copy,
|
|
228
|
-
frame the CTA around the useful return artifact (`map one reporting problem`,
|
|
229
|
-
`review one dashboard`, `pressure-test one workflow`) instead of "call with
|
|
230
|
-
Austin" or another founder-name CTA.
|
|
231
|
-
- message-review hard-fail preflight: before rendering `Status: message-review`,
|
|
232
|
-
scan the subject, tokenized template, good-fill example, good-omit example,
|
|
233
|
-
`My take`, `Suggested adjustment`, and recommendation. If any of them contain
|
|
234
|
-
`felt close enough to send`, `close enough to send`,
|
|
235
|
-
`thought this was relevant enough`, `seemed close
|
|
236
|
-
enough`, `{{profile_signal}}`, `profile signal`, `is why I thought this might
|
|
237
|
-
be relevant`, `Austin's`, a sender/founder name in the subject, a one-row raw
|
|
238
|
-
sample under `Tokenized template:`, rendered examples without full copy, a
|
|
239
|
-
mail-merge-sounding tokenized opener, missing or empty `My take:`, missing or
|
|
240
|
-
empty `Suggested adjustment:`, `My take:` that only says `looks good`,
|
|
241
|
-
`strong`, or `approved`, `Suggested adjustment:` that only says `none`, or no
|
|
242
|
-
literal `{{...}}` token in `Tokenized template:`, revise the message before
|
|
243
|
-
showing it. Do not output
|
|
244
|
-
`Recommendation: approve-message` when any hard-fail preflight item is
|
|
245
|
-
present.
|
|
246
|
-
- `My take` must be 1-3 short bullets or sentences, focused on what a founder
|
|
247
|
-
needs to decide: approve as-is, revise once, make it shorter, make it more
|
|
248
|
-
specific, change proof, change CTA, or preserve the current length because
|
|
249
|
-
cutting would remove reply reason. If a shorter version is recommended,
|
|
250
|
-
`Suggested adjustment` must say exactly what to remove or compress. Do not
|
|
251
|
-
include a long `What works`, `Gold-standard/rule check`, or internal checklist
|
|
252
|
-
in this checkpoint.
|
|
253
|
-
- `Suggested adjustment` must give the founder a concrete final-edit option even
|
|
254
|
-
when the recommendation is `approve-message`. Use a useful shape like
|
|
255
|
-
`Approve as-is, or revise once to [specific edit] if you want [effect]`. It
|
|
256
|
-
must invite the user to give manual feedback before minting, for example:
|
|
257
|
-
`Reply revise-messaging with the exact line change, proof change, CTA change,
|
|
258
|
-
or tone feedback you want before I build the approval packet.` Do not write
|
|
259
|
-
`none`.
|
|
260
|
-
- if the message is merely plausible, generic, template-shaped, or weaker than
|
|
261
|
-
the loaded gold-standard examples, set `Recommendation: revise-messaging`.
|
|
262
|
-
The next Codex UAT goal is to prove message quality before mint, so no
|
|
263
|
-
approval packet or campaign mutation should happen just because the mechanics
|
|
264
|
-
are working.
|
|
265
|
-
- immediately after rendering `message-review.md`, use the structured question
|
|
266
|
-
gate with exactly two choices, in this order: `approve-message`,
|
|
267
|
-
`revise-messaging`. Stop after the question. Do not write `approval-packet.md`,
|
|
268
|
-
`customer-roleplay.md`, or a commit-gate question until
|
|
269
|
-
`message-review-decision.md` contains `approve-message`.
|
|
270
|
-
- if the selected winner or PS uses named customer/logo proof, quantified proof,
|
|
271
|
-
team credentials, or investor/accelerator proof that is not explicitly
|
|
272
|
-
supported by `brief.md`, `lead-review.md`, `lead-sample.json`, or loaded
|
|
273
|
-
reference material, the review must set `Recommendation: revise-messaging`.
|
|
274
|
-
Do not approve speculative credibility claims.
|
|
275
|
-
- if the selected winner or PS uses vague proof wrappers like `spoken publicly
|
|
276
|
-
about`, `publicly talked about`, `trusted by`, `worked with`, `used by`, or
|
|
277
|
-
bare customer-logo lists without naming a concrete buyer-relevant result,
|
|
278
|
-
mechanism, or risk reducer, the review must set `Recommendation:
|
|
279
|
-
revise-messaging`. Supported logos are not automatically body-worthy proof.
|
|
280
|
-
Translate to a concrete proof line or omit the proof from the message.
|
|
281
|
-
- if customer/logo proof is useful mainly for credibility, prefer a concise PS
|
|
282
|
-
over interrupting the body. The PS must name what the customers used the
|
|
283
|
-
product/service for, not just list logos. If the body already carries buyer
|
|
284
|
-
pain, mechanism, and CTA clearly, adding concrete social proof in the PS is
|
|
285
|
-
allowed when it lowers vendor risk.
|
|
286
|
-
- the selected winner or PS must not use internal labels like `p.s. relevant
|
|
287
|
-
proof:`, `p.s. useful proof:`, `p.s. proof:`, or `p.s. social proof:`.
|
|
288
|
-
Rewrite as a natural aside before asking the user to approve.
|
|
289
|
-
- if the user chooses `revise-messaging`, write `message-review-decision.md`
|
|
290
|
-
with `revise-messaging`, preserve upstream artifacts, delete/regenerate
|
|
291
|
-
only `message-validation.md`, `message-review.md`,
|
|
292
|
-
`message-review-decision.md`, `customer-roleplay.md`, and
|
|
293
|
-
`approval-packet.md`, then rerun this message step.
|
|
294
|
-
- after `message-review-decision.md` contains `approve-message`, write
|
|
295
|
-
`approval-packet.md` in customer-facing order and render the same approval
|
|
296
|
-
packet inline before the commit gate. The approval packet and six-choice
|
|
297
|
-
commit question must appear in the same customer-visible turn. Do not emit a
|
|
298
|
-
standalone commit-gate question. Include a clear `## Message Review` section
|
|
299
|
-
inside the approval packet before `## Approved Message Template`. It should
|
|
300
|
-
summarize the customer checkpoint from `message-review.md` and preserve the
|
|
301
|
-
same literal `My take:`, `Suggested adjustment:`, `Good token fill:`,
|
|
302
|
-
`Good omit:`, `Bad token fill:`, `Why bad:`, `Fallback if missing:`,
|
|
303
|
-
`Token fill basis:`, and `Recommendation:` labels.
|
|
304
|
-
Include `Open artifacts:` links to `approval-packet.md`,
|
|
305
|
-
`message-review.md`, `lead-review.md`, and `brief.md` before the commit-gate
|
|
306
|
-
question.
|
|
307
|
-
- if `message-validation.md` contains an extractable `Selected Winner`, use
|
|
308
|
-
that exact winner as the `## Approved Message Template` in
|
|
309
|
-
`approval-packet.md` and in the campaign brief passed to `create_campaign`.
|
|
310
|
-
Only substitute concrete enriched-prospect-row `{{tokens}}`; do not rewrite
|
|
311
|
-
the copy inline at approval time. Do not use abstract slot tokens like
|
|
312
|
-
`{{hookLine}}`, `{{painLine}}`, `{{productLine}}`, `{{closeLine}}`, or
|
|
313
|
-
`{{psLine}}`, and do not use `{{recent_signal_quote}}`; use fields that exist
|
|
314
|
-
on the enriched prospect row instead.
|
|
315
|
-
- the live body under `## Approved Message Template` must be sender-ready copy.
|
|
316
|
-
Bracketed text in the body is allowed only when it is a fully-specified
|
|
317
|
-
AI-native token (`[ALL_CAPS_NAME — Intent. DO: ... DON'T: ... FALLBACK:
|
|
318
|
-
omit the line.]` — see
|
|
319
|
-
`mcp/sellable/skills/create-campaign/references/ai-native-tokens.md` for the
|
|
320
|
-
contract). Bracketed instruction placeholders that lack a contract are
|
|
321
|
-
BLOCKED — including `[ROW BRIDGE ...]`, `[insert ...]`, `[generated ...]`,
|
|
322
|
-
and any prose that defers per-row composition to a later step without
|
|
323
|
-
giving the model the rules. Either upgrade the placeholder to a full
|
|
324
|
-
AI-native token (Intent / DO / DON'T / FALLBACK), put the per-row rules in
|
|
325
|
-
`## Token Fill Rules` with concrete enriched-row fields, or route to
|
|
326
|
-
`revise-messaging`.
|
|
327
|
-
- `approval-packet.md` and the live campaign brief passed to `create_campaign`
|
|
328
|
-
must include `## Approved Message Template`, `## Token Fill Rules`, and
|
|
329
|
-
`## Token Fill Examples`. `## Token Fill Examples` must copy the approved
|
|
330
|
-
examples from `message-review.md` / `message-validation.md`, including
|
|
331
|
-
`Good token fill:`, `Good omit:`, `Bad token fill:`, `Why bad:`,
|
|
332
|
-
`Fallback if missing:`, and `Token fill basis:`. Do not call
|
|
333
|
-
`create_campaign` if these sections are missing from the campaign brief.
|
|
334
|
-
- `message-validation.md` must not call the winner a canonical template or
|
|
335
|
-
hide per-row generation in bracketed body text. `## Selected Winner` must
|
|
336
|
-
be a real sendable message that could be approved as-is.
|
|
337
|
-
- block awkward bridge phrasing like `felt close enough to send this`,
|
|
338
|
-
`thought this was relevant enough`, or `seemed close enough`. Replace with a
|
|
339
|
-
crisp row-backed bridge using a buyer-readable row-derived token, such as
|
|
340
|
-
`That {{workflow_context}} work is where teams usually need cleaner reporting
|
|
341
|
-
ownership.` If the signal is weak or absent, omit the bridge line entirely.
|
|
342
|
-
- if subject, tokenized template, or rendered examples do not meet the bar,
|
|
343
|
-
`message-review.md` must recommend `revise-messaging`, not `approve-message`.
|
|
344
|
-
|
|
345
|
-
Do not:
|
|
346
|
-
|
|
347
|
-
- call `get_campaign`
|
|
348
|
-
- call `get_rows`
|
|
349
|
-
- call `update_cell`
|
|
350
|
-
- call `update_campaign_brief`
|
|
351
|
-
- fetch fresh web or LinkedIn research
|
|
352
|
-
- mutate DB-backed campaign state
|
|
353
|
-
|
|
354
|
-
`message-validation.md` must contain:
|
|
355
|
-
|
|
356
|
-
- `Status`
|
|
357
|
-
- `Mode`
|
|
358
|
-
- `Template Used`
|
|
359
|
-
- `Primary Example`
|
|
360
|
-
- `Secondary Influence`
|
|
361
|
-
- `Lead Sample Basis`
|
|
362
|
-
- `Strongest Reply Reason`
|
|
363
|
-
- `Pre-Draft Buyer-Role Analysis`
|
|
364
|
-
- `Campaign Element Pool`
|
|
365
|
-
- `Gold Standard Strategy Map`
|
|
366
|
-
- `Current Campaign Translation`
|
|
367
|
-
- `Element Scoring`
|
|
368
|
-
- `Proof Inventory`
|
|
369
|
-
- `Token Fill Rules`
|
|
370
|
-
- `Token Adherence Table`
|
|
371
|
-
- `Angle Drafts`
|
|
372
|
-
- `Kill / Combine Review`
|
|
373
|
-
- `Finalists`
|
|
374
|
-
- `Candidate Messages`
|
|
375
|
-
- `Finalizer Pass`
|
|
376
|
-
- `Gold-Standard Quality Gate`
|
|
377
|
-
- `Skeptical Prospect Review`
|
|
378
|
-
- `Winner Gate`
|
|
379
|
-
- `Selected Winner`
|
|
380
|
-
- `Findings`
|
|
381
|
-
- `Recommendation`
|
|
382
|
-
|
|
383
|
-
`approval-packet.md` must contain:
|
|
384
|
-
|
|
385
|
-
- campaign direction
|
|
386
|
-
- lead source and sample
|
|
387
|
-
- filters and rubrics
|
|
388
|
-
- selected message(s)
|
|
389
|
-
- `## Message Review` with literal `My take:`, `Suggested adjustment:`, and
|
|
390
|
-
`Recommendation:` (`approve-message` or `revise-messaging`). If the message
|
|
391
|
-
is only okay, say so plainly so the user can choose `revise-messaging` at the
|
|
392
|
-
commit gate.
|
|
393
|
-
- `## Approved Message Template`; when `message-validation.md` has a selected
|
|
394
|
-
winner, this section must preserve that selected winner rather than a newly
|
|
395
|
-
drafted approval-template variant
|
|
396
|
-
- risks / caveats
|
|
397
|
-
- next action: the six-choice commit gate
|
|
398
|
-
|
|
399
|
-
`message-review.md` must contain:
|
|
400
|
-
|
|
401
|
-
- `Status: message-review`
|
|
402
|
-
- `Subject:` using the approved tokenized A + B + C subject shape
|
|
403
|
-
- `Tokenized template:` followed by the tokenized approved message template
|
|
404
|
-
- `Sample prospect fill:` with one concrete sample prospect's complete rendered
|
|
405
|
-
subject + body and a `Token fill basis:` line
|
|
406
|
-
- `Rendered examples:`
|
|
407
|
-
- `Good token fill:` with a complete concrete rendered subject + body
|
|
408
|
-
- `Good omit:` with a complete concrete rendered subject + body where optional
|
|
409
|
-
row signal is omitted cleanly
|
|
410
|
-
- `Bad token fill:` with a realistic wrong fill for the current campaign
|
|
411
|
-
- `Why bad:` explaining the exact overclaim, unsupported token, weak signal,
|
|
412
|
-
casing mistake, or mail-merge phrasing that makes the bad fill unacceptable
|
|
413
|
-
- `Fallback if missing:` with one concrete fallback or omit example for every
|
|
414
|
-
token in the template
|
|
415
|
-
- `Token notes:` naming which tokens are safe to fill, which optional token line
|
|
416
|
-
should be omitted when absent, and whether the message uses standard sentence
|
|
417
|
-
case or casual lowercase static words
|
|
418
|
-
- `My take:`
|
|
419
|
-
- `Suggested adjustment:` with a concrete final-edit option and explicit
|
|
420
|
-
invitation to give manual feedback before approval
|
|
421
|
-
- `Question: approve-message or revise-messaging?`
|
|
422
|
-
- `Recommendation:` exactly `approve-message` or `revise-messaging`
|
|
423
|
-
- The two-choice structured question gate immediately after the rendered review
|
|
424
|
-
|
|
425
|
-
`message-review-decision.md` must contain exactly one selected route:
|
|
426
|
-
`approve-message` or `revise-messaging`. It does not authorize DB mutation.
|
|
427
|
-
|
|
428
|
-
If token sourcing is weak, revise token fill rules or route back to
|
|
429
|
-
`revise-filter` or `revise-message` instead of guessing.
|
|
430
|
-
|
|
431
|
-
</step_contracts>
|
|
432
|
-
|
|
433
|
-
<resume_rules>
|
|
434
|
-
|
|
435
|
-
Phase 84 validation resume:
|
|
436
|
-
|
|
437
|
-
- No `brief-v1.md` / `brief.md` present -> run `create-campaign-brief`
|
|
438
|
-
- `brief-v1.md` present but no `brief.md` -> copy `brief-v1.md` to `brief.md`,
|
|
439
|
-
then run `find leads`
|
|
440
|
-
- Only `brief.md` present -> compatibility resume: run `find leads`
|
|
441
|
-
- `lead-review.md` + `lead-sample.json` present, but no `lead-filter.md` and
|
|
442
|
-
no `message-validation.md` -> run `filter leads` and `generate message`
|
|
443
|
-
from the same find-leads basis; do not ask the user between them when
|
|
444
|
-
lead-review is confirmed
|
|
445
|
-
- `lead-review.md` + `lead-sample.json` present, but no `lead-filter.md` -> run
|
|
446
|
-
`filter leads`
|
|
447
|
-
- `lead-review.md` + `lead-sample.json` present, but no
|
|
448
|
-
`message-validation.md` -> run `generate message`
|
|
449
|
-
- `lead-filter.md` + `message-validation.md` present -> reconcile message
|
|
450
|
-
basis rows against the final filter, then write/render `message-review.md`
|
|
451
|
-
and ask the two-choice message review gate
|
|
452
|
-
- `message-validation.md` present but no `message-review-decision.md` -> write
|
|
453
|
-
`message-review.md`, render it inline starting with `Status:
|
|
454
|
-
message-review`, then ask `approve-message` vs `revise-messaging`
|
|
455
|
-
- `message-review-decision.md` contains `revise-messaging` -> preserve
|
|
456
|
-
upstream artifacts; delete only `message-validation.md`,
|
|
457
|
-
`message-review.md`, `message-review-decision.md`,
|
|
458
|
-
`customer-roleplay.md`, and `approval-packet.md`; rerun `generate message`
|
|
459
|
-
- `message-review-decision.md` contains `approve-message` but no
|
|
460
|
-
`approval-packet.md` -> write `approval-packet.md`, render that packet inline
|
|
461
|
-
starting with `Status: approval-packet`, then ask the six-choice commit gate
|
|
462
|
-
in the same turn
|
|
463
|
-
- `message-validation.md` + `message-review-decision.md` +
|
|
464
|
-
`approval-packet.md` present -> validation complete,
|
|
465
|
-
render `approval-packet.md` inline starting with `Status: approval-packet`,
|
|
466
|
-
then ask the six-choice commit gate in the same turn
|
|
467
|
-
- `lead-review.md` without `lead-sample.json`, or vice versa -> stop with a
|
|
468
|
-
contract violation
|
|
469
|
-
- `lead-filter.md` without upstream lead artifacts -> stop with a contract violation
|
|
470
|
-
|
|
471
|
-
Phase 85 commit-gate + atomic-mint resume:
|
|
472
|
-
|
|
473
|
-
- All validation artifacts present and `message-review-decision.md` contains
|
|
474
|
-
`approve-message` but no `approval-packet.md` yet -> write the approval
|
|
475
|
-
packet, render it inline, then show the commit gate in the same turn (see
|
|
476
|
-
`<commit_gate>` below)
|
|
477
|
-
- `approval-packet.md` present but no `commit-gate-decision.md` -> show the
|
|
478
|
-
approval packet inline and then the commit gate in the same turn; an existing
|
|
479
|
-
positive `customer-roleplay.md` is advisory only
|
|
480
|
-
- `commit-gate-decision.md` is a revision choice or `abort` -> route without
|
|
481
|
-
DB mutation
|
|
482
|
-
- `commit-gate-decision.md` contains `approve` and no `CampaignOffer` minted
|
|
483
|
-
yet -> run atomic mint
|
|
484
|
-
- `CampaignOffer` exists and `currentStep = "auto-execute-leads"` -> resume
|
|
485
|
-
via `create_campaign({ campaignId })` to re-fetch state + `watchUrl`,
|
|
486
|
-
re-surface the watch link using `references/watch-link-handoff.md`, and
|
|
487
|
-
continue into the autonomous tail at Step 13
|
|
488
|
-
|
|
489
|
-
Phase 85 autonomous-tail resume (Plan 85-02):
|
|
490
|
-
|
|
491
|
-
- `CampaignOffer.currentStep === "auto-execute-leads"` -> run Step 13
|
|
492
|
-
(import) then advance to `validate-sample`
|
|
493
|
-
- `CampaignOffer.currentStep === "validate-sample"` -> run Step 14 sample
|
|
494
|
-
validation loop. Read the persisted `revisionRound` counter and do NOT
|
|
495
|
-
reset it to 0 on resume
|
|
496
|
-
- `CampaignOffer.currentStep === "auto-execute-messaging"` -> run Step 15
|
|
497
|
-
messaging scale-up
|
|
498
|
-
- `CampaignOffer.currentStep === "awaiting-user-greenlight"` -> Step 16:
|
|
499
|
-
re-surface `watchUrl` + `handoff.orientation` and STOP. Do not call
|
|
500
|
-
`start_campaign` on resume. Wait for UI start or a Claude greenlight
|
|
501
|
-
turn
|
|
502
|
-
- `CampaignOffer.currentStep === "running"` -> campaign is live. Surface
|
|
503
|
-
a "campaign is live" confirmation + `watchUrl`. Do not re-start
|
|
504
|
-
|
|
505
|
-
On every tail resume, re-surface the `watchUrl` and the current step's
|
|
506
|
-
orientation string (v1 create-campaign watch-mode pattern). Load the
|
|
507
|
-
relevant reference for the active step:
|
|
508
|
-
|
|
509
|
-
- Step 14 -> `references/sample-validation-loop.md`
|
|
510
|
-
- any escalation path -> `references/escalation-ladder.md`
|
|
511
|
-
- Step 16 -> `references/final-handoff-contract.md`
|
|
512
|
-
|
|
513
|
-
</resume_rules>
|
|
514
|
-
|
|
515
|
-
<commit_gate>
|
|
516
|
-
|
|
517
|
-
After Phase 84 artifacts are complete, show the user a commit gate before any
|
|
518
|
-
DB mutation. Load `references/approval-gate-framing.md` for the full
|
|
519
|
-
framing — especially the approval packet ordering and per-choice draft
|
|
520
|
-
directory effects.
|
|
521
|
-
|
|
522
|
-
Preconditions (block the gate when missing):
|
|
523
|
-
|
|
524
|
-
- `brief-v1.md` should exist for from-scratch runs
|
|
525
|
-
- `brief.md` must exist
|
|
526
|
-
- `lead-review.md` must exist
|
|
527
|
-
- `lead-sample.json` must exist
|
|
528
|
-
- `lead-filter.md` must exist
|
|
529
|
-
- `message-validation.md` must exist
|
|
530
|
-
- `message-review.md` must exist
|
|
531
|
-
- `message-review-decision.md` must exist and contain `approve-message`
|
|
532
|
-
- `approval-packet.md` must be written before asking for a commit decision
|
|
533
|
-
- `rubric.json` is optional but strongly preferred
|
|
534
|
-
|
|
535
|
-
Anchor validation (block `approve` when empty):
|
|
536
|
-
|
|
537
|
-
- `brief.md` has a non-empty `## Message Thesis`
|
|
538
|
-
- `lead-review.md` status is `confirmed`
|
|
539
|
-
- `lead-sample.json` parses and contains at least one row
|
|
540
|
-
- `lead-filter.md` has `Decision`, `Who We'll Keep`, `Who We'll Exclude`,
|
|
541
|
-
`Pass Rate`, `Recommendation`, and `Implementation Details`
|
|
542
|
-
- `message-validation.md` has `Status: confirmed`, `Mode: DRY MODE (no DB
|
|
543
|
-
mutation)`, a `Selected Winner`, and a non-empty `Token Adherence Table`
|
|
544
|
-
- `message-review.md` has `Status: message-review`, `Subject:`,
|
|
545
|
-
`Tokenized template:` with at least one literal `{{...}}` token,
|
|
546
|
-
`Sample prospect fill:` with a complete rendered subject + body and
|
|
547
|
-
`Token fill basis:`, `Rendered examples:`, `Good token fill:` with a complete
|
|
548
|
-
rendered subject + body, `Good omit:` with a complete rendered subject + body,
|
|
549
|
-
`Bad token fill:`, `Why bad:`, `Fallback if missing:`, `Token notes:`,
|
|
550
|
-
`My take:`, `Suggested adjustment:`, `Question:
|
|
551
|
-
approve-message or revise-messaging?`, and
|
|
552
|
-
`Recommendation: approve-message`
|
|
553
|
-
- `message-review-decision.md` is exactly `approve-message`
|
|
554
|
-
|
|
555
|
-
Present the approval packet in user-facing order: brief, lead sample, lead
|
|
556
|
-
filter, message validation. Do not dump raw JSON or opaque validation
|
|
557
|
-
anchors at the user.
|
|
558
|
-
|
|
559
|
-
When rendering the approval packet inline, start it with exactly
|
|
560
|
-
`Status: approval-packet`. Write the same content to `approval-packet.md`
|
|
561
|
-
before showing the gate. The same assistant turn must then use the structured
|
|
562
|
-
question gate with the six commit choices below. In Claude Code, call
|
|
563
|
-
`AskUserQuestion`; in Codex, call `request_user_input` when available. A
|
|
564
|
-
commit-gate question without the inline `Status: approval-packet` packet is
|
|
565
|
-
invalid UX. If a
|
|
566
|
-
customer roleplay is performed, write `customer-roleplay.md` before the final
|
|
567
|
-
choice. A roleplay result that says "looks good", "approve", or equivalent is
|
|
568
|
-
only critique; it does not satisfy the gate. Write `commit-gate-decision.md`
|
|
569
|
-
only after the user/operator makes one explicit choice.
|
|
570
|
-
|
|
571
|
-
Use the structured question gate with exactly these 6 choices, in this order:
|
|
572
|
-
|
|
573
|
-
```text
|
|
574
|
-
approve
|
|
575
|
-
revise-brief
|
|
576
|
-
revise-leads
|
|
577
|
-
revise-rubric
|
|
578
|
-
revise-messaging
|
|
579
|
-
abort
|
|
580
|
-
```
|
|
581
|
-
|
|
582
|
-
Do not collapse, reorder, or add choices. Do not auto-select.
|
|
583
|
-
|
|
584
|
-
If the host UI caps options per question, preserve the same six routes with a
|
|
585
|
-
two-question gate: Q1 = `approve`, `revise`, `abort`; Q2 =
|
|
586
|
-
`revise-brief`, `revise-leads`, `revise-rubric`, `revise-messaging`. Do not
|
|
587
|
-
tell the customer this is because of schema/tool limits. In the split form,
|
|
588
|
-
`revise` is only the parent choice for the second question, not a new route.
|
|
589
|
-
|
|
590
|
-
</commit_gate>
|
|
591
|
-
|
|
592
|
-
<sender_resolution>
|
|
593
|
-
|
|
594
|
-
Before atomic mint, the skill needs a concrete sender identity. Resolve
|
|
595
|
-
it in this order — stop at the first step that yields a confirmed
|
|
596
|
-
sender, and NEVER invent / guess / brute-force LinkedIn URLs:
|
|
597
|
-
|
|
598
|
-
1. **If the fixture/caller provides `senderIdentity.senderId`**, use it
|
|
599
|
-
directly. Call `get_sender({ senderId })` to confirm it exists in
|
|
600
|
-
the active workspace, then call `enrich_sender({ senderId })` ONCE
|
|
601
|
-
to populate `companySnapshot` + `proofDigest`. Do not call
|
|
602
|
-
`list_senders`, do not guess alternate URLs.
|
|
603
|
-
2. **Else if the fixture/caller provides a confirmed
|
|
604
|
-
`senderIdentity.linkedinUrl`**, call `enrich_sender({ linkedinUrl
|
|
605
|
-
})` ONCE. If it errors, DO NOT retry with a variation of the URL.
|
|
606
|
-
Fall through to step 3.
|
|
607
|
-
3. **Else if the caller provides `senderIdentity.name` +
|
|
608
|
-
`companyDomain` (or workspace context), do ONE `WebSearch` pass**
|
|
609
|
-
for the authoritative LinkedIn profile URL ("`{name}` `{company}`
|
|
610
|
-
linkedin"). Use ONLY a URL that appears verbatim in a web search
|
|
611
|
-
result. Never synthesize a URL from the name. If WebSearch returns
|
|
612
|
-
no clean match, escalate to step 4.
|
|
613
|
-
4. **Else escalate.** Print the available sender context and ask the
|
|
614
|
-
user to resolve (senderId or confirmed linkedinUrl). Do not proceed
|
|
615
|
-
with atomic mint.
|
|
616
|
-
|
|
617
|
-
Hard rules:
|
|
618
|
-
|
|
619
|
-
- At most ONE `enrich_sender` retry per run, only when the first
|
|
620
|
-
response was a transient (5xx) error. Do not retry on 400/404 by
|
|
621
|
-
changing the URL.
|
|
622
|
-
- At most ONE `WebSearch` pass per run for sender resolution.
|
|
623
|
-
- NEVER call `enrich_sender` on a lead's LinkedIn URL. `enrich_sender`
|
|
624
|
-
is for the single sender entity only. Lead enrichment happens via
|
|
625
|
-
the Enrich Prospect column in the workflow table.
|
|
626
|
-
- If the fixture's `mustMatchWorkspaceSender` flag is true and the
|
|
627
|
-
resolved sender isn't in the active workspace, ABORT the run with an
|
|
628
|
-
explicit mismatch error.
|
|
629
|
-
|
|
630
|
-
</sender_resolution>
|
|
631
|
-
|
|
632
|
-
<atomic_mint>
|
|
633
|
-
|
|
634
|
-
Only run this sequence when the user picks `approve`. Load
|
|
635
|
-
`references/watch-link-handoff.md` before starting; it owns the watch-link
|
|
636
|
-
surfacing protocol and the partial-mint recovery rules.
|
|
637
|
-
|
|
638
|
-
Exact sequence:
|
|
639
|
-
|
|
640
|
-
1. Read `brief.md` and `rubric.json` from the draft directory.
|
|
641
|
-
2. Validate anchors one more time. An empty anchor blocks the approve path.
|
|
642
|
-
The brief must include an `## Approved Message Template` section with
|
|
643
|
-
the exact live message template and at least one supported `{{token}}`
|
|
644
|
-
such as `{{first_name}}`. Rendered examples are not enough. The
|
|
645
|
-
Generate Message column switches to template mode only when
|
|
646
|
-
`campaignBrief.content` contains `{{...}}`; without this marker it will
|
|
647
|
-
use full-generation mode and may rewrite the approved message.
|
|
648
|
-
The live template body must contain only sender-ready copy plus supported
|
|
649
|
-
tokens. Two token shapes are allowed:
|
|
650
|
-
- `{{snake_case}}` field-substitution tokens for atomic values that drop
|
|
651
|
-
directly into a fixed slot (greeting, subject, company name).
|
|
652
|
-
- `[ALL_CAPS_NAME — Intent. DO: ... DON'T: ... FALLBACK: omit the line.]`
|
|
653
|
-
AI-native tokens for personalization sentences. The bracket carries
|
|
654
|
-
the contract inline; the model writes a sentence (or omits the line)
|
|
655
|
-
per the inline rules. Full spec:
|
|
656
|
-
`mcp/sellable/skills/create-campaign/references/ai-native-tokens.md`.
|
|
657
|
-
No other bracketed placeholders are allowed — bracketed prose without an
|
|
658
|
-
explicit Intent / DO / DON'T / FALLBACK contract (e.g., `[ROW BRIDGE]`,
|
|
659
|
-
`[insert ...]`, `[generated ...]`) is BLOCKED because it leaves the
|
|
660
|
-
per-row generation under-specified.
|
|
661
|
-
Include `## Token Fill Rules` for every token in the template. Field
|
|
662
|
-
tokens may be row fields such as `{{first_name}}`, `{{company}}`,
|
|
663
|
-
`{{role}}`, `{{headline}}`, `{{profile_summary}}`, `{{post_context}}`,
|
|
664
|
-
`{{comment_summary}}`, `{{source_post_topic}}`, or `{{row_proof_note}}`
|
|
665
|
-
when those exact fields are supported by the row/enrichment data. AI-native
|
|
666
|
-
tokens are listed in the rules table by name and type with a pointer back
|
|
667
|
-
to the inline contract (the rules themselves live inside the bracket — do
|
|
668
|
-
not duplicate them in the table). Do not document static sender identity,
|
|
669
|
-
product name, proof points, casing style, `{{recent_signal_quote}}`, or
|
|
670
|
-
abstract slot tokens such as `{{hookLine}}`, `{{painLine}}`,
|
|
671
|
-
`{{productLine}}`, `{{closeLine}}`, or `{{psLine}}` — those are abstract
|
|
672
|
-
slots without a contract; convert them to AI-native bracket tokens with
|
|
673
|
-
real Intent / DO / DON'T / FALLBACK clauses, or cut them.
|
|
674
|
-
Include `## Token Fill Examples` copied from the approved message review and
|
|
675
|
-
validation artifacts. It must preserve `Good token fill:`, `Good omit:`,
|
|
676
|
-
`Bad token fill:`, `Why bad:`, `Fallback if missing:`, and
|
|
677
|
-
`Token fill basis:` so the minted campaign brief teaches future row
|
|
678
|
-
generation how to fill tokens, what fills are blocked, and what to do when
|
|
679
|
-
row data is missing. If the brief does not contain `## Token Fill Rules` and
|
|
680
|
-
`## Token Fill Examples`, do not call `create_campaign`; route back to
|
|
681
|
-
message review or approval packet generation.
|
|
682
|
-
3. Call `bootstrap_create_campaign({ flowVersion: "v2" })`. Respect
|
|
683
|
-
`safeToProceed`; on blocking errors, surface and stop.
|
|
684
|
-
4. Call
|
|
685
|
-
`create_campaign({ campaignBrief, currentStep: "auto-execute-leads" })`.
|
|
686
|
-
The `campaignBrief` argument must preserve the approved message
|
|
687
|
-
template section verbatim, including the `{{tokens}}`.
|
|
688
|
-
If the brief has no supported `{{token}}`, do not call
|
|
689
|
-
`create_campaign`. Fail the approve path and ask for a corrected
|
|
690
|
-
template, unless the user explicitly approved freeform AI-generated
|
|
691
|
-
messages and the tool call sets `messageGenerationMode:
|
|
692
|
-
"ai-generated"`.
|
|
693
|
-
Include `senderIds` when a workspace sender is available. If the
|
|
694
|
-
customer-named sender is not configured in the current UAT workspace,
|
|
695
|
-
use an available workspace sender for the rehearsal and state that
|
|
696
|
-
mismatch in the final runtime notes. Do not silently mint without a
|
|
697
|
-
sender because sequence selection depends on sender tier/settings.
|
|
698
|
-
Capture `{ campaignId, watchUrl }` from the response.
|
|
699
|
-
- If the call errors, surface the error and stop. No rollback needed; no
|
|
700
|
-
campaign exists.
|
|
701
|
-
- If the response is missing `watchUrl`, treat it as a recoverable
|
|
702
|
-
failure. Stop before `save_rubrics`. Do not print a watch link.
|
|
703
|
-
Surface the contract violation explicitly — a missing `watchUrl` is a
|
|
704
|
-
plumbing bug, not a silent skip.
|
|
705
|
-
5. Call `save_rubrics({ campaignOfferId: campaignId, rubric })` using the
|
|
706
|
-
payload derived from `rubric.json` (or from `lead-filter.md` if
|
|
707
|
-
`rubric.json` is absent).
|
|
708
|
-
- If this call fails after `create_campaign` succeeded, either rollback
|
|
709
|
-
the `CampaignOffer` row, or mark an explicit recoverable failure state
|
|
710
|
-
so the user can retry. Never leave a silent partial campaign. Do NOT
|
|
711
|
-
print the watch link during this recovery window.
|
|
712
|
-
6. Flip the brief status in the draft to `committed`. Move the draft
|
|
713
|
-
directory from
|
|
714
|
-
`.sellable/create-campaign-v2/drafts/{workspace-slug}/{campaign-slug}/`
|
|
715
|
-
to
|
|
716
|
-
`.sellable/create-campaign-v2/drafts/{workspace-slug}/.committed/{campaign-slug}/`.
|
|
717
|
-
7. Surface the `watchUrl` using the exact block defined in
|
|
718
|
-
`references/watch-link-handoff.md`. Only print after both
|
|
719
|
-
`create_campaign` AND `save_rubrics` have succeeded.
|
|
720
|
-
8. Hand off to the autonomous tail (Plan 85-02). Do not run Step 13 lead
|
|
721
|
-
import here.
|
|
722
|
-
|
|
723
|
-
If the approved lead source exists only as a provider lane in
|
|
724
|
-
`lead-review.md` (for example a Sales Navigator filter/search URL) and no
|
|
725
|
-
live `searchId` or `sourceLeadListId` is present, recreate the approved
|
|
726
|
-
source by calling the matching provider search tool first, then import from
|
|
727
|
-
the returned id. This is replaying the approved lane, not inventing a new
|
|
728
|
-
source. Do not skip import/messages on that basis.
|
|
729
|
-
|
|
730
|
-
Do NOT call `import_leads` during atomic mint. Full lead sourcing and import
|
|
731
|
-
happen in Plan 85-02 Step 13 after mint.
|
|
732
|
-
|
|
733
|
-
</atomic_mint>
|
|
734
|
-
|
|
735
|
-
<revision_routing>
|
|
736
|
-
|
|
737
|
-
When the user picks a revision choice, do not mint a campaign. Do not print
|
|
738
|
-
a watch link. Route back to the relevant upstream step and reset the draft
|
|
739
|
-
directory per `references/approval-gate-framing.md`:
|
|
740
|
-
|
|
741
|
-
- `revise-brief` -> back to Phase 83 or Phase 84 depending on what changed.
|
|
742
|
-
Preserve `brief.md`; delete all downstream artifacts.
|
|
743
|
-
- `revise-leads` -> back to Phase 84 `find leads`. Preserve `brief.md`;
|
|
744
|
-
delete lead and downstream artifacts.
|
|
745
|
-
- `revise-rubric` -> back to Phase 84 `filter leads`. Preserve `brief.md`,
|
|
746
|
-
`lead-review.md`, `lead-sample.json`; delete `lead-filter.md`,
|
|
747
|
-
`message-validation.md`, and `rubric.json`.
|
|
748
|
-
- `revise-messaging` -> back to Phase 84 `generate message`. Preserve
|
|
749
|
-
upstream artifacts; delete only `message-validation.md`,
|
|
750
|
-
`message-review.md`, `message-review-decision.md`,
|
|
751
|
-
`customer-roleplay.md`, and `approval-packet.md`.
|
|
752
|
-
- `abort` -> move the draft directory to
|
|
753
|
-
`.sellable/create-campaign-v2/drafts/{workspace-slug}/.aborted/{campaign-slug}-{timestamp}/`.
|
|
754
|
-
No DB rows touched.
|
|
755
|
-
|
|
756
|
-
Revisions must produce zero DB rows.
|
|
757
|
-
|
|
758
|
-
</revision_routing>
|
|
759
|
-
|
|
760
|
-
<boundaries>
|
|
761
|
-
|
|
762
|
-
Phase 84 (pre-commit-gate) boundary:
|
|
763
|
-
|
|
764
|
-
Do not create live campaign state during validation. Specifically, do not:
|
|
765
|
-
|
|
766
|
-
- mint a campaign
|
|
767
|
-
- import leads
|
|
768
|
-
- persist selected lead lists
|
|
769
|
-
- attach downstream assets
|
|
770
|
-
- save remote rubric state
|
|
771
|
-
- move the user into a live execution step
|
|
772
|
-
|
|
773
|
-
Phase 85 (commit-gate and beyond) boundary:
|
|
774
|
-
|
|
775
|
-
- The commit gate is the only place in this skill where DB mutation is
|
|
776
|
-
authorized. Do not call `create_campaign`, `save_rubrics`,
|
|
777
|
-
`update_campaign`, or any other mutating tool before the user picks
|
|
778
|
-
`approve`.
|
|
779
|
-
- Revision choices and `abort` must never call a mutating tool.
|
|
780
|
-
- `import_leads` is not part of atomic mint. Defer it to Plan 85-02 Step 13.
|
|
781
|
-
- Never print the `watchUrl` until both `create_campaign` and `save_rubrics`
|
|
782
|
-
have succeeded.
|
|
783
|
-
- Never fabricate or reconstruct the `watchUrl` — capture it from the
|
|
784
|
-
`create_campaign` response.
|
|
785
|
-
- Once a campaign has been minted, every provider search and every
|
|
786
|
-
provider-import call from this skill MUST include the minted
|
|
787
|
-
`campaignOfferId`. This is universal — Prospeo, Sales Nav, Apollo,
|
|
788
|
-
Signal Discovery, and any future provider. The only place a
|
|
789
|
-
campaignless search is correct is the pre-mint Phase 84 `find leads`
|
|
790
|
-
discovery sample. Post-mint expansion, scale-up imports, alternate
|
|
791
|
-
lane explorations, account-based reruns, signal-monitor lanes, and
|
|
792
|
-
operator-driven follow-up searches all persist to the campaign and
|
|
793
|
-
must carry `campaignOfferId`. A search without `campaignOfferId`
|
|
794
|
-
after mint orphans the search from the campaign UI's Contact
|
|
795
|
-
Search panel and the campaign's `Searches` history, even when the
|
|
796
|
-
search produced rows that later get imported. If the search
|
|
797
|
-
helper enforces a `confirmed: true` gate, pass that with
|
|
798
|
-
`campaignOfferId` rather than dropping it.
|
|
799
|
-
|
|
800
|
-
</boundaries>
|
|
801
|
-
|
|
802
|
-
<autonomous_tail>
|
|
803
|
-
|
|
804
|
-
The review-batch tail runs AFTER the atomic mint succeeds. It is driven by
|
|
805
|
-
`core/auto-execute.yaml` and progresses through four steps — Step 13
|
|
806
|
-
(import review batch), Step 14 (validate-sample), Step 15
|
|
807
|
-
(auto-execute-messaging for the review batch), Step 16
|
|
808
|
-
(awaiting-user-greenlight) — before stopping and waiting for a user
|
|
809
|
-
greenlight.
|
|
810
|
-
|
|
811
|
-
Cost boundary: the commit gate approves creating a campaign and producing a
|
|
812
|
-
real review batch. It does NOT approve spending enrichment/message credits on
|
|
813
|
-
hundreds of leads. Two separate scales apply post-mint and they MUST be kept
|
|
814
|
-
distinct in your head:
|
|
815
|
-
|
|
816
|
-
- **Import scale** (how many rows land in the campaign table). Defaults to
|
|
817
|
-
`import.importLimit` (25). The user MAY explicitly raise this at or before
|
|
818
|
-
mint with phrasing like "import all of them", "pull the full search", "all
|
|
819
|
-
~2,000 from this lane", or a numeric target. When the user has stated the
|
|
820
|
-
full-scale import intent, pass that count as `targetLeadCount` so the table
|
|
821
|
-
carries the full pool. Importing rows is cheap (no enrichment runs yet);
|
|
822
|
-
storing them up front lets the user kick remaining enrichment manually
|
|
823
|
-
without re-importing later.
|
|
824
|
-
- **Cascade scale** (how many rows get enriched/scored/messaged before user
|
|
825
|
-
approval). ALWAYS stays at the configured review batch (`sample.sampleSize`
|
|
826
|
-
or `import.importLimit` — default 25) until the user explicitly approves the
|
|
827
|
-
sample. This is the cost gate. Even when the user said "import all", you
|
|
828
|
-
queue Enrich Prospect cells for only the first review-batch rows. The
|
|
829
|
-
remaining rows sit `pending` in the table for the user to kick manually
|
|
830
|
-
after they've eyeballed the sample.
|
|
831
|
-
|
|
832
|
-
A pre-stated full-import intent is NOT a pre-approval to cascade everything.
|
|
833
|
-
Re-confirm before queueing enrichment beyond the review batch.
|
|
834
|
-
|
|
835
|
-
If the source lane contains hundreds of leads and the user has not stated a
|
|
836
|
-
full-import intent, leave the rest untouched in the source — do not import
|
|
837
|
-
beyond `importLimit` until the user asks to expand.
|
|
838
|
-
|