@sellable/mcp 0.1.366 → 0.1.367

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.
@@ -51,11 +51,392 @@ Default evergreen plan per workspace (override only if the prompt specifies diff
51
51
  3. **`<Workspace/Team> - Shared Cold Fallback`** — one shared cold/fallback campaign lane across senders
52
52
  </inputs>
53
53
 
54
+ <command_backed_workflow>
55
+ For full/customer-visible setup, the first operational command is always
56
+ `setup_evergreen_campaigns({ mode:"plan", yolo })`. Inventory can happen first
57
+ for operator understanding, but do not create/reuse/repair campaign shells,
58
+ source rows, messages, sequence, approvals, or cleanup before the command-backed
59
+ plan returns lane packets.
60
+ If `setup_evergreen_campaigns` is unavailable, returns 404, throws an API
61
+ error, or does not return a current `planRevision` with lane packets, stop
62
+ immediately with `blocked: setup_evergreen_campaigns_unavailable`. Do not fall
63
+ back to ad hoc create-campaign calls, inventory-only guesses, or direct shell
64
+ creation when the command-backed plan is missing.
65
+
66
+ Plan shape:
67
+
68
+ - one Post Engagers lane per sender in the **Post Engagers sender scope**;
69
+ - one shared Signal Discovery lane for the **shared lane sender scope**;
70
+ - one shared Cold Fallback lane for the **shared lane sender scope**;
71
+ - explicit existing campaign/table bindings when the user points at a canonical
72
+ lane. Pass those exact ids to the plan and reuse/verify them instead of
73
+ creating duplicates.
74
+ - If a user supplies canonical ids, reuse/verify them instead of creating duplicates.
75
+
76
+ The command-backed unbound plan is authoritative. If
77
+ `setup_evergreen_campaigns({ mode:"plan", ... })` returns a lane packet with
78
+ `intent:"create"`, execute that create packet exactly. Do not convert it into an
79
+ explicit binding because `list_tables`, `get_campaigns`, a waterfall, or old
80
+ inventory shows an `ARCHIVED` campaign/table with a matching name. Archived
81
+ campaigns are not reusable evergreen targets. Only pass an archived
82
+ campaign/table as an explicit binding when the user supplied those exact ids and
83
+ explicitly allowed dashboard visibility repair; otherwise ignore archived
84
+ matches for execution and let the create packet proceed.
85
+
86
+ Use `selectedSenderIds` only as a legacy shorthand when the same senders should
87
+ get sender-owned Post Engagers lanes and attach to shared lanes. When the scopes
88
+ differ, pass both explicit arrays:
89
+
90
+ - `postEngagerSenderIds`: only senders that should have sender-owned Post
91
+ Engagers campaigns.
92
+ - `sharedSenderIds`: senders attached to the shared Signal Discovery and Shared
93
+ Cold Fallback campaigns.
94
+
95
+ If the operator says a sender is shared-only, do not create a Post Engagers lane
96
+ for that shared-only sender. Example: if Justin already has a Post Engagers
97
+ campaign and Jell/Hulan should only participate in the two shared campaigns,
98
+ call `setup_evergreen_campaigns` with `postEngagerSenderIds:["<justin id>"]`
99
+ and `sharedSenderIds:["<justin id>","<jell or hulan id>"]`.
100
+
101
+ When senders are ambiguous, ask which sender ids belong in the Post Engagers
102
+ sender scope and which belong in the shared lane sender scope before planning.
103
+ If the operator says all connected senders for both scopes, pass
104
+ `allConnectedSenders` and let the command return exact selected sender ids or
105
+ blockers.
106
+
107
+ `--yolo` is supported, but it is a safety-scoped execution mode, not a cleanup
108
+ or launch permission. With `--yolo`, pass `yolo:true` to
109
+ `setup_evergreen_campaigns({ mode:"plan", yolo })` and proceed without
110
+ intermediate approval only when every selected lane packet returns
111
+ `autoExecutable:true`. `--yolo` does not authorize archive/delete cleanup,
112
+ launch/start, schedule/send, broad approve-all, wrong workspace work, stale
113
+ plan execution, paid InMail spend, or ambiguous target choice. Stop on wrong
114
+ workspace, cleanup/archive/delete need, stale plan, disconnected sender,
115
+ duplicate candidate, unsafe side effect, missing source proof, or missing
116
+ message proof.
117
+
118
+ When safe-yolo is blocked only because the plan needs normal setup work, ask
119
+ for a **bounded delegated approval** over the rendered current plan instead of
120
+ asking for each substep. The approval packet must name the workspace id, sender
121
+ ids, lane keys, campaign/table ids or create intents, source/import caps,
122
+ generate-message caps, route-proof approval policy, selectedActionIds,
123
+ planRevision, allowed side-effect classes, and stop conditions. After that one
124
+ approval covers the current planRevision, the parent may act on behalf of the
125
+ operator and execute the selectedActionIds end to end. Do not ask again for
126
+ substep approvals for source import, create-campaign choices, Message Drafting,
127
+ sequence attach/precheck, or exactly one route-proof approval when those actions
128
+ are within the lane packet, approved caps, approved side-effect classes, and
129
+ route-proof approval policy. Stop and re-plan if any fresh reread changes the
130
+ workspace id, sender ids, source/list id, campaign/table id, new campaign/table
131
+ id, planRevision, actionId, selectedActionIds, allowed side effects, caps,
132
+ status, or blocker set outside the approved packet.
133
+ The short rule: one approval covers the current planRevision and selectedActionIds.
134
+ The execution rule: act on behalf of the operator; do not ask again for substep approvals while work stays within the lane packet, approved caps, approved side-effect classes, and route-proof approval policy.
135
+ The drift rule: stop and re-plan when ids, caps, blockers, side-effect classes, or any new campaign/table id leave the approved packet.
136
+
137
+ After bounded delegated approval or safe `--yolo`, fan out lane worker tasks
138
+ only from the current plan. Each worker receives the complete lane packet:
139
+ workspace id, sender ids, lane key/type, existing campaign/table binding or
140
+ create intent, source hints, caps, planRevision, actionId, allowed side effects,
141
+ and postconditions. Workers must explicitly load and use the installed visible
142
+ `$sellable:create-campaign` wrapper as the nested workflow entrypoint, then load
143
+ `create-campaign-v2` and `create-campaign-v2/core/flow.v2.json` through the
144
+ packaged MCP prompt/asset loaders before campaign creation or repair. The
145
+ worker then uses that create-campaign workflow/subskills for campaign
146
+ creation/setup, source/import, filter choice, Message Drafting, packaged
147
+ `generate-messages`, sequence attachment, and send-review readiness. Do not
148
+ rebuild those steps ad hoc in the parent thread or from a copied receipt
149
+ checklist. The parent thread is only the planner, dispatcher, verifier, and
150
+ receipt collector: it must not call `create_campaign`, `import_leads`,
151
+ `start_campaign_message_preparation`, `update_cell`, or sequence-attach tools
152
+ for a lane that was assigned to a worker. When the host supports branch
153
+ execution, Task/subagent, or separate Codex threads, assign one lane packet per
154
+ worker and run the lane workers in parallel. If the host cannot run parallel
155
+ workers, report `blocked: missing_parallel_lane_worker_runtime` instead of
156
+ silently doing all lane creation sequentially in the parent thread.
157
+ Short form: the parent thread is the planner, dispatcher, verifier, and receipt collector.
158
+ Before the first mutating lane action, complete an execution-runtime preflight:
159
+ name the callable worker surface/tool that will run lane packets (for example
160
+ Task/subagent, branch worker, separate Codex thread, or an equivalent explicit
161
+ worker runtime). If you cannot name a real callable worker runtime, stop with
162
+ `blocked: missing_parallel_lane_worker_runtime` before calling
163
+ `create_campaign`, `update_campaign`, `import_leads`, `confirm_lead_list`, or
164
+ any source/message/sequence mutation. Creating campaign shells first and then
165
+ discovering the worker runtime is missing is a failed run.
166
+
167
+ For any lane with `intent:"create"`, the worker must satisfy the normal
168
+ net-new create-campaign preflight before shell creation: load
169
+ `get_subskill_prompt({ subskillName:"research-sender" })`, run the sender/team
170
+ identity research needed for the brief, call `complete_sender_research(...)`,
171
+ then call `create_campaign`. Do not bypass this with a parent-thread brief or
172
+ by directly setting campaign state.
173
+
174
+ Workers return worker receipts. Each customer-visible lane receipt must include
175
+ one durable `createCampaignStepReceipt` built from actual tool calls or worker receipts,
176
+ not a parent-thread summary. The receipt must include:
177
+
178
+ - `setupPlanCall`: the exact `setup_evergreen_campaigns({ mode:"plan", ... })`
179
+ planRevision, selected actionId, lane key, workspace id, sender ids, and
180
+ campaign/table binding or create intent used before work started. For a
181
+ create lane, this receipt must explicitly include `createIntent:true`; for a
182
+ reuse lane, do not report `createIntent:true`.
183
+ - `createCampaignWorkflowReceipt`: proof the worker loaded the actual installed
184
+ `$sellable:create-campaign` wrapper instead of doing ad hoc MCP calls. It must
185
+ include `skillCommand:"$sellable:create-campaign"`,
186
+ `skillName:"create-campaign"`, `wrapperSkillLoaded:true`,
187
+ `workflowPromptName:"create-campaign-v2"`,
188
+ `workflowPromptLoadedToHasMoreFalse:true`,
189
+ `workflowAssetPath:"create-campaign-v2/core/flow.v2.json"`,
190
+ `workflowAssetLoaded:true`, the real `workerRuntime`, and `notAdHoc:true`.
191
+ - `researchSenderReceipt`: `research-sender` prompt load proof,
192
+ `complete_sender_research` result, sender LinkedIn identity basis, and the
193
+ notes/proof counts used before the net-new `create_campaign` call.
194
+ - `campaignBriefReceipt`: the CampaignOffer/campaign id, current
195
+ workflowTableId, brief hash or updated-at marker, delivery format, token
196
+ rules, hard avoids, source-use rules, and first-message template state.
197
+ - `sourceDecisionReceipt`: selected source/list id, provider, cursor/inventory
198
+ state, import/copy caps, explicit `dedupeDncProviderExclusions:true` or an
199
+ equivalent exclusions object, row ids or row-count evidence (`rowCount`,
200
+ `readyRowCount`, or `rowEvidence`), and the reason no colder source was used
201
+ before the current source was proven exhausted or insufficient.
202
+ - `filterDecisionReceipt`: saved/applied filter ids or explicit skip-filter
203
+ decision with current campaign/table basis.
204
+ - `messageDraftingReceipt`: the Message Drafting proof described below,
205
+ including packaged `generate-messages` prompt/assets and validation.
206
+ - `reviewBatchReceipt`: review-batch row ids/hash, generated row count,
207
+ quality-valid route-proof row id when approved, and proof that no broad
208
+ approve-all occurred.
209
+ - `sequenceReceipt`: exact current workflowTableId, recommended non-paid
210
+ sequence attach/precheck result, and readback showing `hasSequence:true` when
211
+ completion is claimed.
212
+ - final paused-send proof: if the current campaign table is `DRAFT` after the
213
+ sequence is attached, call the product `pause_campaign({ campaignId })`
214
+ endpoint/tool to put the unlaunched campaign into `PAUSED` review state, then
215
+ reread the campaign/table. Do not raw-write `campaignStatus`, do not start or
216
+ launch, and do not schedule/send. Completion requires reread proof of
217
+ `currentStep:"send"` and `campaignStatus:"PAUSED"`.
218
+ - `verifyCall`: the exact `setup_evergreen_campaigns({ mode:"verify",
219
+ planRevision, selectedActionIds, receipts })` call/result used before the
220
+ parent reports completion.
221
+
222
+ Use these canonical receipt leaf names exactly. Do not substitute looser aliases
223
+ like `generateMessagesPromptLoad`, `requiredAssetLoads`,
224
+ `validationPromptLoad`, `reviewBatchBasisHash`, `reviewRowIds`,
225
+ `sequenceActionTypes`, or `pass:true` unless you also include the canonical
226
+ fields below:
227
+
228
+ The receipt status must be `status:"succeeded"` or `status:"completed"` when
229
+ the lane is complete. Do not use `status:"passed"` or
230
+ `status:"passed_with_warnings"` as the primary success status. Warnings may go
231
+ in a separate `warnings` array, but the completion status stays canonical.
232
+
233
+ All step proof objects must live under `createCampaignStepReceipt`. Do not put
234
+ `createCampaignWorkflowReceipt`, `campaignBriefReceipt`,
235
+ `sourceDecisionReceipt`, `filterDecisionReceipt`, `messageDraftingReceipt`,
236
+ `reviewBatchReceipt`, or `sequenceReceipt` only as top-level siblings;
237
+ top-level copies are useful for humans but are not enough for command
238
+ verification unless the same object also appears inside
239
+ `createCampaignStepReceipt`.
240
+
241
+ ```json
242
+ {
243
+ "status": "succeeded",
244
+ "planRevision": "<original planRevision>",
245
+ "laneKey": "<lane key>",
246
+ "laneType": "<lane type>",
247
+ "workspaceId": "<workspace id>",
248
+ "senderIds": ["<sender id>"],
249
+ "campaignId": "<campaign id>",
250
+ "tableId": "<workflow table id>",
251
+ "createCampaignStepReceipt": {
252
+ "createCampaignWorkflowReceipt": {
253
+ "skillCommand": "$sellable:create-campaign",
254
+ "skillName": "create-campaign",
255
+ "wrapperSkillLoaded": true,
256
+ "workflowPromptName": "create-campaign-v2",
257
+ "workflowPromptLoadedToHasMoreFalse": true,
258
+ "workflowAssetPath": "create-campaign-v2/core/flow.v2.json",
259
+ "workflowAssetLoaded": true,
260
+ "workerRuntime": "separate-codex-thread",
261
+ "notAdHoc": true
262
+ },
263
+ "campaignBriefReceipt": {
264
+ "campaignId": "<campaign id>",
265
+ "workflowTableId": "<workflow table id>",
266
+ "briefHash": "<brief/currentApprovedBriefHash>",
267
+ "deliveryFormat": "single message",
268
+ "tokenRules": true,
269
+ "hardAvoids": true,
270
+ "sourceUseRules": true,
271
+ "firstMessageTemplate": "<template or template state>"
272
+ },
273
+ "sourceDecisionReceipt": {
274
+ "sourceId": "<source id>",
275
+ "sourceListId": "<source/list id>",
276
+ "cursorOrInventoryState": "<cursor/inventory/import state>",
277
+ "dedupeDncProviderExclusions": true,
278
+ "sourceLadderReason": "<why the current source is valid before colder sources>"
279
+ },
280
+ "filterDecisionReceipt": {
281
+ "status": "skipped",
282
+ "workflowTableId": "<workflow table id>"
283
+ },
284
+ "messageDraftingReceipt": {
285
+ "statusSource": "branch",
286
+ "promptLoadedToHasMoreFalse": true,
287
+ "requiredAssetsLoaded": true,
288
+ "validationLoaded": true,
289
+ "validationResult": "passed",
290
+ "qualityReview": {"passed": true, "checkedSampleCount": 3, "issues": []},
291
+ "campaignId": "<campaign id>",
292
+ "workflowTableId": "<workflow table id>",
293
+ "selectedLeadListId": "<source/list id>",
294
+ "reviewBatchRowHash": "<review row ids hash>",
295
+ "messageDraftRecommendation": {"basis": "generate-messages"},
296
+ "sampleMessages[].verdict": "pass",
297
+ "sampleMessages": [
298
+ {"rowId": "<row id>", "generatedMessageText": "<message>", "verdict": "pass"}
299
+ ]
300
+ },
301
+ "reviewBatchReceipt": {
302
+ "rowIdsHash": "<review row ids hash>",
303
+ "generatedCount": 3
304
+ },
305
+ "sequenceReceipt": {
306
+ "workflowTableId": "<workflow table id>",
307
+ "hasSequence": true,
308
+ "sequenceReceipt.actionTypes": ["send_invite"],
309
+ "sequenceReceipt.nonPaid": true,
310
+ "actionTypes": ["send_invite"],
311
+ "nonPaid": true
312
+ },
313
+ "finalPausedSendProof": {
314
+ "currentStep": "send",
315
+ "campaignStatus": "PAUSED"
316
+ },
317
+ "verifyCall": {
318
+ "mode": "verify",
319
+ "planRevision": "<original planRevision>",
320
+ "selectedActionIds": ["<all original selected action ids>"]
321
+ }
322
+ }
323
+ }
324
+ ```
325
+
326
+ Verification always uses the original execution planRevision and original
327
+ selectedActionIds from the plan packets that were dispatched before mutation.
328
+ The parent must preserve the original selectedActionIds exactly.
329
+ Do not replace them with a later reuse planRevision/actionIds after the
330
+ campaigns exist. After workers complete, attach or patch `verifyCall` on each
331
+ lane receipt with that original execution planRevision, original
332
+ selectedActionIds, same sender scopes, and the verify result. Only after verify
333
+ succeeds should the parent perform the idempotency rerun.
334
+
335
+ If any required field is missing, report
336
+ `blocked: missing_create_campaign_step_receipt` for that lane and do not call it
337
+ complete.
338
+
339
+ Message Drafting runtime rule: the preferred proof source is the normal
340
+ create-campaign branch handoff (`watchNarration.workerDetails.messageDraftBuilder`
341
+ or equivalent) with `statusSource:"branch"`. If that internal branch handoff is
342
+ not callable or not exposed in the packaged MCP runtime, do **not** stop after
343
+ creating/importing rows. The lane worker must run the packaged message-drafting
344
+ path itself in the worker thread:
345
+
346
+ 1. Load `get_subskill_prompt({ subskillName:"generate-messages" })` until
347
+ `hasMore:false`.
348
+ 2. Load the required message assets and `create-campaign-v2-validation`.
349
+ 3. Call `start_campaign_message_preparation({ campaignId, tableId,
350
+ targetPreparedMessages, maxRowsToCheck, approvalMode:"mark_ready",
351
+ autoContinue:true })` inside the same lane worker, with bounded caps from the
352
+ lane packet.
353
+ 4. Poll `get_campaign_message_preparation_status` until no queued/active cells
354
+ remain or a bounded stop reason is returned.
355
+ 5. Use `get_campaign_messages_preview` or `select_campaign_cells` to inspect
356
+ generated rows, quality-review at least 3 samples, approve exactly one
357
+ quality-valid route-proof row when none is already approved, and prove no
358
+ broad approve-all happened.
359
+ 6. Continue to `attach_recommended_sequence({ campaignId, currentStep:"send" })`
360
+ and, if the campaign is still `DRAFT`, `pause_campaign({ campaignId })`.
361
+ Reread the campaign/table before claiming completion.
362
+
363
+ That packaged worker path must return `messageDraftingReceipt.statusSource:
364
+ "packaged-generate-messages-worker"`. It is accepted only when the receipt
365
+ includes the same prompt/assets/validation proof, current campaign/table/source
366
+ basis, generated review rows, sample messages, quality review, route-proof
367
+ approval evidence, sequence proof, and final paused-send proof required of the
368
+ branch handoff. `statusSource:"parent-thread-fallback"` is still invalid.
369
+
370
+ One lane may produce exactly one CampaignOffer/campaign id and one current
371
+ workflow table id. For `intent:"create"`, the first successful
372
+ `create_campaign` result establishes the lane's only allowed campaign/table
373
+ target. Every later source/import/confirm/message/filter/sequence/readback call
374
+ must use that same campaign id and current workflow table id. If any tool
375
+ returns or creates a different CampaignOffer, workflow table, "Campaign from
376
+ Source" campaign, or shell for the same lane, stop immediately with
377
+ `blocked: duplicate_campaign_offer_created`, include both ids in the receipt,
378
+ and do not report completion. Do not hide the earlier shell by reporting only
379
+ the later imported campaign.
380
+
381
+ Each customer-visible lane receipt must also include message proof from
382
+ `watchNarration.workerDetails.messageDraftBuilder`, the worker's equivalent
383
+ branch handoff record, or the packaged message-prep worker path above. Valid
384
+ evergreen message proof names the current campaign id, workflowTableId,
385
+ selected lead-list or source id, filter choice, and review-batch row ids/hash,
386
+ and it must use `statusSource:"branch"` or
387
+ `statusSource:"packaged-generate-messages-worker"`.
388
+ `statusSource:"parent-thread-fallback"` is not accepted for evergreen
389
+ customer-visible completion because it can be a parent summary rather than proof
390
+ that the create-campaign Message Drafting branch actually ran. The proof must
391
+ show the worker loaded the full packaged prompt with
392
+ `get_subskill_prompt({ subskillName:"generate-messages"` until `hasMore:false`,
393
+ loaded the required message assets, and loaded `create-campaign-v2-validation`
394
+ before returning a `messageDraftRecommendation`.
395
+ The receipt must also include `validationResult:"passed"`, a passed
396
+ quality-review object, and at least 3 concrete reviewed sample messages with
397
+ row ids, generated message text, pass verdicts, and no issues. Shared Cold
398
+ Fallback samples must not open with a standalone name line followed by
399
+ "Hey there" or another generic greeting; that pattern means the lane did not
400
+ finish the create-campaign message workflow to customer quality.
401
+ Shared Signal Discovery samples must not use sender-owned post-engager language
402
+ such as "my post", "thanks for showing support", "saw you pop up", or any copy
403
+ that implies the sending sender authored the source post or personally received
404
+ the prospect's engagement unless the selected source evidence proves that exact
405
+ sender authored the post.
406
+ The generated-message cells are not prompt proof: `currentRevisionGeneratedMessages`,
407
+ Generate Message column counts, `messagesCount`, or rendered row previews prove
408
+ only that table cells exist from the current brief/template, not that the
409
+ create-campaign Message Drafting branch ran. If this receipt is missing, report
410
+ `blocked: missing_message_drafting_receipt` and do not call the lane complete.
411
+
412
+ The parent then calls
413
+ `setup_evergreen_campaigns({ mode:"verify", postEngagerSenderIds,
414
+ sharedSenderIds, planRevision, selectedActionIds, receipts })` using the same
415
+ sender scopes as the plan call and the original execution planRevision/actionIds,
416
+ and reports only verified completion. A verify call without the same sender scopes can recompute an empty or different plan and is invalid proof. A verify call that uses a later reuse planRevision instead of the original execution planRevision is also invalid proof. Only after verify succeeds, rerun
417
+ `setup_evergreen_campaigns({ mode:"plan", postEngagerSenderIds,
418
+ sharedSenderIds, yolo:true })` without explicit campaign/table bindings. That
419
+ idempotency rerun must return `intent:"reuse"` for every completed lane and no
420
+ duplicate-lane blockers before you call the command finished. If verify blocks,
421
+ report the blocker and repair only the missing postconditions from the same
422
+ current plan.
423
+
424
+ Message proof for every customer-visible lane must come from the current
425
+ campaign/table basis and include at least 3 generated review rows from the
426
+ packaged `generate-messages` path before completion is reported.
427
+ Parent-thread handwritten copy or setting `currentStep` is not proof.
428
+ </command_backed_workflow>
429
+
54
430
  <objective>
55
431
  1. **Inventory first**: `get_campaigns` + `list_tables` + `get_campaign_waterfall` in the active workspace. Treat `list_tables` and the managed waterfall as authoritative for older managed slots; `get_campaigns` is a recent campaign page and may miss canonical evergreen lanes. Match existing campaigns/tables/waterfall slots to the plan by name (case-insensitive, ignore suffixes like "(Copy)") and stored slot identity. `list_tables.campaignStatus` and `list_tables.dashboardBucket` are part of the identity check: a matching `ARCHIVED` table/campaign is not a plain `REUSE`. If it is the canonical prod slot and the invocation explicitly allows dashboard visibility repair, repair it to `PAUSED`; otherwise mark it `flagged`/`blocked` and do not create a duplicate. A matching non-archived campaign/table/waterfall slot = REUSE; record it and move on. Never create a second campaign for a slot that already has one in any of those inventories.
56
- 2. **Create only the missing slots** with `create_on_demand_campaign({ name, senderIds, campaignBrief })`:
432
+ 2. **Create only the missing slots** through the full `$sellable:create-campaign`
433
+ workflow path for each lane, using the lane packet as the setup basis:
57
434
  - Post Engagers lanes: that sender's ID only. Shared lanes: all the senders' IDs.
58
- - The brief must include the warm post-engager first-message template style — short, casual, references the post they engaged with, closed question, **no internal vocabulary, no pitch, no meeting ask** in message one:
435
+ - The brief and message direction are lane-specific. Never reuse the Post
436
+ Engagers opening for Shared Cold Fallback.
437
+ - **Post Engagers** first-message style: short, casual, references the
438
+ sender-authored post they engaged with, closed question, **no internal
439
+ vocabulary, no pitch, no meeting ask** in message one:
59
440
 
60
441
  ```
61
442
  {{first_name}}
@@ -65,6 +446,33 @@ Default evergreen plan per workspace (override only if the prompt specifies diff
65
446
  Saw you pop up on my post about {{post_topic_line}}
66
447
  ```
67
448
 
449
+ - **Shared Signal Discovery** first-message style: short, casual, references
450
+ the real observed signal/theme or public conversation. It may mention the
451
+ engagement source only when the source evidence supports it. Do not say
452
+ "my post" unless the selected source post was actually authored by the
453
+ sending sender. Do not thank the prospect for showing support, reacting,
454
+ commenting, or popping up unless this is a sender-owned Post Engagers lane
455
+ with sender-authored source evidence.
456
+ - **Shared Cold Fallback** first-message style: cold, direct, and cohesive.
457
+ It must not pretend there was engagement, must not say "saw you pop up",
458
+ and must not use a standalone name line followed by "Hey there" or other
459
+ generic greeting. It should open with a light role/company/problem
460
+ observation, avoid awkward phrases like "looks like a team where", and ask
461
+ one clear problem question. This is a single-message
462
+ cold path unless the approved create-campaign message workflow proves a
463
+ better delivery format.
464
+ - Every customer-visible lane must run Message Drafting from the current
465
+ campaign/table basis after source rows exist. The worker must load the full
466
+ packaged `generate-messages` prompt, every required asset, and
467
+ `create-campaign-v2-validation`; if the internal branch handoff is not
468
+ callable in the packaged runtime, the worker must call
469
+ `start_campaign_message_preparation` and poll
470
+ `get_campaign_message_preparation_status` from the lane worker. The
471
+ returned receipt must include `statusSource:"branch"` or
472
+ `statusSource:"packaged-generate-messages-worker"`, at least 3 concrete
473
+ reviewed sample messages, a passed validation result, and a passed quality
474
+ review. Row/message counts alone are not enough.
475
+
68
476
  - **The brief must declare the delivery format** so Generate Message writes copy suited to how it actually sends:
69
477
  - DM lanes: add a `Delivery format:` line — either `multiline (each paragraph sends as its own DM message)` or `single message`. When multiline, the template's blank-line paragraphs ARE the message boundaries — write each one as a standalone typed message.
70
478
  - **InMail lanes can never be multiline**: an InMail is one message and the recipient must reply before anything else can be sent. InMail-bound templates must read as one cohesive message — declare `Delivery format: single message (InMail — no follow-up until reply)` and never structure the copy to depend on multi-message pacing.
@@ -90,26 +498,73 @@ Default evergreen plan per workspace (override only if the prompt specifies diff
90
498
  - Message Drafting has run from the current campaign/table basis, using the
91
499
  create-campaign message prompt/assets and validation gate. Updating
92
500
  `currentStep:"messages"` is not proof. Parent-thread handwritten copy is
93
- not a substitute.
501
+ not a substitute. The proof receipt must show
502
+ `get_subskill_prompt({ subskillName:"generate-messages"` was loaded until
503
+ `hasMore:false`, required message assets were loaded, and
504
+ `create-campaign-v2-validation` ran through
505
+ `workerDetails.messageDraftBuilder` with `statusSource:"branch"` or
506
+ through the lane worker's packaged message-prep path with
507
+ `statusSource:"packaged-generate-messages-worker"`,
508
+ `start_campaign_message_preparation`, and
509
+ `get_campaign_message_preparation_status`. The generated-message cells are
510
+ not prompt proof; `currentRevisionGeneratedMessages` and row previews are
511
+ only downstream cell evidence. The proof must include
512
+ `validationResult:"passed"`, a passed quality review, and at least 3
513
+ reviewed sample messages. For Shared Cold Fallback, reject samples that
514
+ open with a standalone name followed by "Hey there" or a similarly generic
515
+ greeting.
94
516
  - The first review batch exists and at least 3 review rows have generated
95
517
  messages from the approved brief. If fewer than 3 usable rows exist, report
96
518
  the actual count and why.
519
+ If a bounded preparation job requested more than the minimum but already
520
+ met the minimum generated-message floor, do not stall on a single bad review row once a fresh status/readiness check shows no queued, processing, or active cells; record the shortfall, name the failed/empty-row reason when
521
+ available, and continue with route-proof review rather than expanding source
522
+ or waiting forever.
97
523
  - At least one generated row is approved as a route-proof gate. If zero rows
98
524
  are approved and the prompt explicitly asked for full/customer-ready
99
525
  completion, approve exactly one quality-valid generated row. If one or more
100
526
  rows are already approved, do not add more approvals during evergreen
101
527
  completion. Never broad approve all rows.
102
- - The recommended non-paid sequence is attached, and the watched campaign is
103
- on Send. Use `attach_recommended_sequence({ campaignId, currentStep:"send" })`
104
- when a safe attach is needed. Existing sequence proof may come from
105
- `SEQUENCE_EXISTS`; do not replace it without explicit user confirmation.
106
- - No scheduled, queued outbound, sent outbound, campaign launch, or paid
107
- InMail spend is created by this skill.
528
+ - The recommended non-paid sequence is attached to the current campaign
529
+ table, and the watched campaign is on Send. Use
530
+ `attach_recommended_sequence({ campaignId, currentStep:"send" })` when a
531
+ safe attach is needed. After `confirm_lead_list` or any source-list copy,
532
+ immediately reread `get_campaign({ campaignId })`; the returned
533
+ `workflowTableId` is the current campaign table. Cross-check that exact
534
+ current workflowTableId with `list_tables` and require `hasSequence:true`
535
+ before reporting completion. If a stale shell table has sequence columns but
536
+ the current campaign table does not, `SEQUENCE_EXISTS` is not enough:
537
+ record the stale shell table id, then repair the current workflowTableId
538
+ with `attach_sequence` using the same non-paid product template
539
+ `attach_recommended_sequence` would have selected. Never use a paid-InMail
540
+ template and never attach/replace sequence outside the current table unless
541
+ the current lane packet explicitly allowed sequence repair.
542
+ - If the current campaign table is still `DRAFT` after sequence/readiness
543
+ proof, call `pause_campaign({ campaignId })` and reread. `pause_campaign`
544
+ is the product-native review-state transition; it is not a launch and does
545
+ not schedule or send. Never raw-write campaign status.
546
+ - No scheduled, queued outbound, sent outbound, campaign launch, or paid
547
+ InMail spend is created by this skill.
108
548
  4. **Verify each slot** after create/reuse/repair:
109
549
  - `get_campaign` shows the campaign exists, remains unlaunched, and has the expected workflow table.
110
550
  - Builder truth must match dashboard truth. `currentStep:"running"` is valid only when the linked table has `campaignStatus:"ACTIVE"`. For a `PAUSED` or `ARCHIVED` campaign/table, repair or flag stale `currentStep:"running"` back to launch review (`send` / review-ready) before reporting the slot done. Never call `start_campaign` just to make a stale `running` step true.
111
- - Send/action lanes such as Post Engagers have a sequence attached. Use `list_tables({ hasSequence: true })` as a quick cross-check, but do not treat that filter as the only proof. If the canonical campaign/table is missing from the sequence-filtered table list but a safe `attach_recommended_sequence({ campaignId })` repair/precheck returns `SEQUENCE_EXISTS`, record `sequence attached (verified by SEQUENCE_EXISTS precheck; list_tables.hasSequence mismatch)` and do not retry.
112
- - Only use `attach_recommended_sequence({ campaignId })` for a send-lane sequence repair/precheck when the target is a canonical prod slot and the invoking prompt allows sequence repair. Call it at most once without `confirmed`; a successful response is `repaired`, and `SEQUENCE_EXISTS` is non-mutating proof that a sequence already exists. Never call `attach_recommended_sequence` or `attach_sequence` with `confirmed:true` in evergreen automation unless the user explicitly asks to replace an existing sequence.
551
+ - Send/action lanes such as Post Engagers have a sequence attached on the
552
+ exact current campaign table. Use `list_tables({ hasSequence: true })` as a
553
+ quick cross-check, but do not treat that filter as the only proof. If the
554
+ canonical current campaign table is missing from the sequence-filtered
555
+ table list and `attach_recommended_sequence({ campaignId })` returns
556
+ `SEQUENCE_EXISTS`, inspect whether a stale shell table owns the sequence.
557
+ For a customer-visible completion run, stale shell proof is not completion:
558
+ rerun the current-table repair above or report `blocked: stale_shell_sequence`.
559
+ - Only use `attach_recommended_sequence({ campaignId })` for a send-lane
560
+ sequence repair/precheck when the target is a canonical prod slot and the
561
+ invoking prompt allows sequence repair. Call it at most once without
562
+ `confirmed`; a successful response is `repaired`. Treat `SEQUENCE_EXISTS` as
563
+ non-mutating proof only when the current table also has `hasSequence:true`.
564
+ Never call `attach_recommended_sequence` or `attach_sequence` with
565
+ `confirmed:true` in evergreen automation unless the user explicitly asks to
566
+ replace an existing sequence or the approved lane packet allows current
567
+ table stale-shell repair.
113
568
  - Do not report `source-only/no sequence expected` for any named evergreen
114
569
  campaign that appears in Campaigns, has a CampaignOffer ID, or is backed by
115
570
  a campaign dashboard table. Shared Signal Discovery and Shared Cold
@@ -9,6 +9,9 @@ allowed-tools:
9
9
  - mcp__sellable__get_campaign_refill_state
10
10
  - mcp__sellable__fill_campaign_horizon
11
11
  - mcp__sellable__get_campaign
12
+ - mcp__sellable__get_campaign_context
13
+ - mcp__sellable__update_campaign
14
+ - mcp__sellable__get_provider_prompt
12
15
  - mcp__sellable__get_campaign_message_preparation_status
13
16
  - mcp__sellable__start_campaign_message_preparation
14
17
  - mcp__sellable__cancel_campaign_message_preparation
@@ -16,6 +19,7 @@ allowed-tools:
16
19
  - mcp__sellable__wait_for_lead_list_ready
17
20
  - mcp__sellable__confirm_lead_list
18
21
  - mcp__sellable__search_signals
22
+ - mcp__sellable__select_promising_posts
19
23
  - mcp__sellable__fetch_post_engagers
20
24
  - mcp__sellable__search_sales_nav
21
25
  - mcp__sellable__lookup_sales_nav_filter
@@ -29,6 +33,10 @@ allowed-tools:
29
33
  - mcp__sellable__get_rows
30
34
  - mcp__sellable__get_rows_minimal
31
35
  - mcp__sellable__get_table_rows
36
+ - mcp__sellable__get_campaign_table_schema
37
+ - mcp__sellable__select_campaign_cells
38
+ - mcp__sellable__wait_for_campaign_processing
39
+ - mcp__sellable__get_sender
32
40
  ---
33
41
 
34
42
  # Refill Sends
@@ -7,6 +7,9 @@ allowed-tools:
7
7
  - mcp__sellable__get_campaign_refill_state
8
8
  - mcp__sellable__fill_campaign_horizon
9
9
  - mcp__sellable__get_campaign
10
+ - mcp__sellable__get_campaign_context
11
+ - mcp__sellable__update_campaign
12
+ - mcp__sellable__get_provider_prompt
10
13
  - mcp__sellable__get_campaign_message_preparation_status
11
14
  - mcp__sellable__start_campaign_message_preparation
12
15
  - mcp__sellable__cancel_campaign_message_preparation
@@ -14,6 +17,7 @@ allowed-tools:
14
17
  - mcp__sellable__wait_for_lead_list_ready
15
18
  - mcp__sellable__confirm_lead_list
16
19
  - mcp__sellable__search_signals
20
+ - mcp__sellable__select_promising_posts
17
21
  - mcp__sellable__fetch_post_engagers
18
22
  - mcp__sellable__search_sales_nav
19
23
  - mcp__sellable__lookup_sales_nav_filter
@@ -27,6 +31,10 @@ allowed-tools:
27
31
  - mcp__sellable__get_rows
28
32
  - mcp__sellable__get_rows_minimal
29
33
  - mcp__sellable__get_table_rows
34
+ - mcp__sellable__get_campaign_table_schema
35
+ - mcp__sellable__select_campaign_cells
36
+ - mcp__sellable__wait_for_campaign_processing
37
+ - mcp__sellable__get_sender
30
38
  ---
31
39
 
32
40
  # Refill Sends Workflow
@@ -101,6 +109,42 @@ Source replenishment choices:
101
109
  source prospects into the same campaign/source-list path, then confirm
102
110
  readiness.
103
111
 
112
+ ### Running Campaign Signal Discovery Recovery
113
+
114
+ For an already-running regular campaign that needs same-campaign Signal
115
+ Discovery replenishment, treat the watch/current-step transition as a guarded
116
+ repair, not as normal create-campaign navigation.
117
+
118
+ Do not call `search_signals` with `currentStep:"signal-discovery"` directly from `currentStep:"running"`. The campaign step guard intentionally rejects that
119
+ backwards transition. After exact approval and a fresh refill-state reread, clear `currentStep` only if it still exactly matches `running`:
120
+
121
+ ```text
122
+ update_campaign({ campaignId, currentStep:null, clearCurrentStepIfMatches:"running" })
123
+ ```
124
+
125
+ Then register provider preflight and run a campaign-scoped `search_signals` so
126
+ post rows exist in the campaign's Signal Discovery state:
127
+
128
+ ```text
129
+ get_provider_prompt({ provider:"signal-discovery", campaignOfferId, confirmed:true })
130
+ search_signals({ campaignOfferId, currentStep:"signal-discovery", confirmed:true, ... })
131
+ ```
132
+
133
+ Use `select_promising_posts` before importing. For a fresh source refill, prefer
134
+ `selectionMode:"replace"` and `scrapePlanMode:"capacity-target"` with the
135
+ approved `targetEngagerCount` and `maxPostsToScrape`. If the approved post set cannot cover the target, do not knowingly run an under-capacity scrape; ask for approval to expand the selected post set or switch source.
136
+
137
+ If `import_leads` returns `reusedExistingSourceList` but the user explicitly
138
+ approved a different selected-post scrape, retry `import_leads` with the existing `sourceLeadListId` from that response or from refill state. For Signal
139
+ Discovery this bypasses the stale reuse guard and materializes the newly
140
+ selected campaign-scoped posts into a fresh source list.
141
+
142
+ After `wait_for_lead_list_ready`, call `confirm_lead_list` with the new
143
+ source-list id and same campaign id. Pass `currentStep:"running"` to `confirm_lead_list` for an ACTIVE already-running campaign so the watched state
144
+ returns to Running after same-campaign source rows are copied. Do not call
145
+ `update_campaign` to fake this after the copy; `confirm_lead_list` owns the
146
+ post-copy state transition.
147
+
104
148
  Before any source mutation, show exact approval evidence: workspace id,
105
149
  campaign id, workflow table id, source/provider id, source-list id when known,
106
150
  import caps, target dates/caps, dedupe/DNC/provider exclusion expectations, and
@@ -135,11 +179,28 @@ Before prep or approval mutation, reread `get_campaign_refill_state` and compare
135
179
  If they changed, stop. A second concurrent refill run must see the first run's
136
180
  import/prep state on reread and avoid duplicate work.
137
181
 
182
+ After same-campaign source copy, inspect the actual campaign-table position of
183
+ the copied rows before starting prep. Newly copied rows often land after the first 100 table rows. Use `get_campaign_table_schema` to read the `reviewBatch`,
184
+ then use `select_campaign_cells` diagnostics against
185
+ `rowSelector:{ type:"reviewBatch" }` for `enrich`, `generateMessage`, and
186
+ `approved` roles when needed.
187
+
188
+ If review-batch enrich cells are pending or Generate Message cells are
189
+ dependency-blocked, omit `maxRowsToCheck` so the adaptive prep job can cover the
190
+ bounded table scan, or set a bounded cap high enough to include appended rows
191
+ when row count evidence requires it. Do not run a fixed `maxRowsToCheck:100` prep pass after appending rows to a larger existing campaign table; it can spend
192
+ the whole budget on older rows and miss the refill batch.
193
+
138
194
  Scheduled success requires a post-action reread proving scheduler-owned cells
139
195
  with non-null `scheduledFor`. Prepared, approved, and ready-to-schedule rows are
140
196
  intermediate states; report them as awaiting scheduler unless scheduled cells
141
197
  are present.
142
198
 
199
+ preparedMessages can remain 0 while scheduler-ready state changes downstream.
200
+ After a prep job reaches a terminal state, run `wait_for_campaign_processing`
201
+ when generated/pass counts are still settling, then poll `get_campaign_refill_state` until `readyToSchedule` drops, scheduled counts increase, or the state clearly
202
+ remains awaiting scheduler. Do not force scheduler fields directly.
203
+
143
204
  ## Evergreen Campaign Discipline
144
205
 
145
206
  `fill_campaign_horizon` is a legacy evergreen-only lower-level primitive. Call it