@sellable/mcp 0.1.362 → 0.1.364
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/dist/api.d.ts +7 -0
- package/dist/api.js +32 -0
- package/dist/index-dev.js +0 -0
- package/dist/index.js +0 -0
- package/dist/server.js +8 -2
- package/dist/tools/cells.js +2 -2
- package/dist/tools/prompts.js +1 -1
- package/dist/tools/registry.js +2 -0
- package/dist/tools/rows.js +1 -1
- package/dist/tools/setup-evergreen-campaigns.d.ts +1 -1
- package/dist/tools/workspace-export.d.ts +82 -0
- package/dist/tools/workspace-export.js +328 -0
- package/package.json +1 -1
- package/skills/create-campaign/SKILL.md +0 -8
- package/skills/create-campaign-v2/references/final-handoff-contract.md +1 -14
- package/skills/create-campaign-v2-tail/SKILL.md +1 -16
- package/skills/create-evergreen-campaigns/SKILL.md +11 -427
- package/skills/refill-sends/SKILL.md +0 -8
- package/skills/refill-sends-workflow/SKILL.md +0 -61
|
@@ -68,7 +68,6 @@ Step 16 — awaiting-user-greenlight
|
|
|
68
68
|
ask the user which connected sender to attach; in explicit UAT safe mode only, use the safe mock sender
|
|
69
69
|
update_campaign(senderIds=[selectedSenderId], currentStep=sequence)
|
|
70
70
|
attach_recommended_sequence({ campaignId, currentStep: "send" }) # tier-aware: premium/SN -> If Open Profile->INMAIL_OPEN, else INVITE->accepted->DM
|
|
71
|
-
reread get_campaign + list_tables; verify hasSequence:true on the current workflowTableId, not an older shell table
|
|
72
71
|
if the attach response did not move the UI: update_campaign(currentStep=send)
|
|
73
72
|
surface campaign setup orientation + final launch choices without repeating the watch URL
|
|
74
73
|
STOP. DO NOT call start_campaign. DO NOT move to running without explicit launch greenlight.
|
|
@@ -82,13 +81,7 @@ hand-author a `version: 2` template with nodes, branches, and
|
|
|
82
81
|
entryNodeId — that's error-prone mid-long-context (galley-off UAT
|
|
83
82
|
`20260420T195732Z` failed because Claude hit "Invalid node type" and
|
|
84
83
|
tried to debug via forbidden Bash/Glob calls). Use `attach_sequence`
|
|
85
|
-
only when the caller explicitly needs a custom non-recommended cadence
|
|
86
|
-
you have just reread the campaign after `confirm_lead_list` and proved the
|
|
87
|
-
current campaign table has no sequence while an older stale shell table is
|
|
88
|
-
causing campaign-level `SEQUENCE_EXISTS`. In that stale-shell repair case, use
|
|
89
|
-
`attach_sequence` on the current workflowTableId with the same non-paid product
|
|
90
|
-
template the backend would select; never choose a paid-InMail template and never
|
|
91
|
-
repair a table other than the current workflowTableId.
|
|
84
|
+
only when the caller explicitly needs a custom non-recommended cadence.
|
|
92
85
|
|
|
93
86
|
Hard gates — if you find yourself about to violate any of these, stop
|
|
94
87
|
first:
|
|
@@ -539,14 +532,6 @@ Shape:
|
|
|
539
532
|
opt-in because it can spend InMail credits. If the tool response
|
|
540
533
|
does not persist `currentStep: "send"`, call
|
|
541
534
|
`update_campaign({ campaignId, currentStep: "send" })`.
|
|
542
|
-
Then reread `get_campaign({ campaignId })` and `list_tables()` and verify the
|
|
543
|
-
returned current workflowTableId has `hasSequence:true`. If
|
|
544
|
-
`attach_recommended_sequence` returned `SEQUENCE_EXISTS` but the current table
|
|
545
|
-
still has `hasSequence:false`, do not continue to launch handoff from that
|
|
546
|
-
stale shell proof. Repair only the current workflowTableId with
|
|
547
|
-
`attach_sequence` using the same non-paid product template the recommended
|
|
548
|
-
selector would have chosen, or stop and report the stale-shell sequence
|
|
549
|
-
blocker if replacement was not explicitly approved.
|
|
550
535
|
9. Surface the `handoff.orientation` string from `auto-execute.yaml` without
|
|
551
536
|
repeating the watch URL.
|
|
552
537
|
10. Ask the final launch greenlight with the structured question function:
|
|
@@ -51,353 +51,11 @@ 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 use the create-campaign workflow and
|
|
142
|
-
create-campaign workflow/subskills for campaign creation/setup, source/import,
|
|
143
|
-
filter choice, Message Drafting, packaged `generate-messages`, sequence
|
|
144
|
-
attachment, and send-review readiness. Do not rebuild those steps ad hoc in the
|
|
145
|
-
parent thread. The parent thread is only the planner, dispatcher, verifier, and
|
|
146
|
-
receipt collector: it must not call `create_campaign`, `import_leads`,
|
|
147
|
-
`start_campaign_message_preparation`, `update_cell`, or sequence-attach tools
|
|
148
|
-
for a lane that was assigned to a worker. When the host supports branch
|
|
149
|
-
execution, Task/subagent, or separate Codex threads, assign one lane packet per
|
|
150
|
-
worker and run the lane workers in parallel. If the host cannot run parallel
|
|
151
|
-
workers, report `blocked: missing_parallel_lane_worker_runtime` instead of
|
|
152
|
-
silently doing all lane creation sequentially in the parent thread.
|
|
153
|
-
Short form: the parent thread is the planner, dispatcher, verifier, and receipt collector.
|
|
154
|
-
Before the first mutating lane action, complete an execution-runtime preflight:
|
|
155
|
-
name the callable worker surface/tool that will run lane packets (for example
|
|
156
|
-
Task/subagent, branch worker, separate Codex thread, or an equivalent explicit
|
|
157
|
-
worker runtime). If you cannot name a real callable worker runtime, stop with
|
|
158
|
-
`blocked: missing_parallel_lane_worker_runtime` before calling
|
|
159
|
-
`create_campaign`, `update_campaign`, `import_leads`, `confirm_lead_list`, or
|
|
160
|
-
any source/message/sequence mutation. Creating campaign shells first and then
|
|
161
|
-
discovering the worker runtime is missing is a failed run.
|
|
162
|
-
|
|
163
|
-
For any lane with `intent:"create"`, the worker must satisfy the normal
|
|
164
|
-
net-new create-campaign preflight before shell creation: load
|
|
165
|
-
`get_subskill_prompt({ subskillName:"research-sender" })`, run the sender/team
|
|
166
|
-
identity research needed for the brief, call `complete_sender_research(...)`,
|
|
167
|
-
then call `create_campaign`. Do not bypass this with a parent-thread brief or
|
|
168
|
-
by directly setting campaign state.
|
|
169
|
-
|
|
170
|
-
Workers return worker receipts. Each customer-visible lane receipt must include
|
|
171
|
-
one durable `createCampaignStepReceipt` built from actual tool calls or worker receipts,
|
|
172
|
-
not a parent-thread summary. The receipt must include:
|
|
173
|
-
|
|
174
|
-
- `setupPlanCall`: the exact `setup_evergreen_campaigns({ mode:"plan", ... })`
|
|
175
|
-
planRevision, selected actionId, lane key, workspace id, sender ids, and
|
|
176
|
-
campaign/table binding or create intent used before work started. For a
|
|
177
|
-
create lane, this receipt must explicitly include `createIntent:true`; for a
|
|
178
|
-
reuse lane, do not report `createIntent:true`.
|
|
179
|
-
- `researchSenderReceipt`: `research-sender` prompt load proof,
|
|
180
|
-
`complete_sender_research` result, sender LinkedIn identity basis, and the
|
|
181
|
-
notes/proof counts used before the net-new `create_campaign` call.
|
|
182
|
-
- `campaignBriefReceipt`: the CampaignOffer/campaign id, current
|
|
183
|
-
workflowTableId, brief hash or updated-at marker, delivery format, token
|
|
184
|
-
rules, hard avoids, source-use rules, and first-message template state.
|
|
185
|
-
- `sourceDecisionReceipt`: selected source/list id, provider, cursor/inventory
|
|
186
|
-
state, import/copy caps, explicit `dedupeDncProviderExclusions:true` or an
|
|
187
|
-
equivalent exclusions object, row ids or row-count evidence (`rowCount`,
|
|
188
|
-
`readyRowCount`, or `rowEvidence`), and the reason no colder source was used
|
|
189
|
-
before the current source was proven exhausted or insufficient.
|
|
190
|
-
- `filterDecisionReceipt`: saved/applied filter ids or explicit skip-filter
|
|
191
|
-
decision with current campaign/table basis.
|
|
192
|
-
- `messageDraftingReceipt`: the Message Drafting proof described below,
|
|
193
|
-
including packaged `generate-messages` prompt/assets and validation.
|
|
194
|
-
- `reviewBatchReceipt`: review-batch row ids/hash, generated row count,
|
|
195
|
-
quality-valid route-proof row id when approved, and proof that no broad
|
|
196
|
-
approve-all occurred.
|
|
197
|
-
- `sequenceReceipt`: exact current workflowTableId, recommended non-paid
|
|
198
|
-
sequence attach/precheck result, and readback showing `hasSequence:true` when
|
|
199
|
-
completion is claimed.
|
|
200
|
-
- final paused-send proof: if the current campaign table is `DRAFT` after the
|
|
201
|
-
sequence is attached, call the product `pause_campaign({ campaignId })`
|
|
202
|
-
endpoint/tool to put the unlaunched campaign into `PAUSED` review state, then
|
|
203
|
-
reread the campaign/table. Do not raw-write `campaignStatus`, do not start or
|
|
204
|
-
launch, and do not schedule/send. Completion requires reread proof of
|
|
205
|
-
`currentStep:"send"` and `campaignStatus:"PAUSED"`.
|
|
206
|
-
- `verifyCall`: the exact `setup_evergreen_campaigns({ mode:"verify",
|
|
207
|
-
planRevision, selectedActionIds, receipts })` call/result used before the
|
|
208
|
-
parent reports completion.
|
|
209
|
-
|
|
210
|
-
Use these canonical receipt leaf names exactly. Do not substitute looser aliases
|
|
211
|
-
like `generateMessagesPromptLoad`, `requiredAssetLoads`,
|
|
212
|
-
`validationPromptLoad`, `reviewBatchBasisHash`, `reviewRowIds`,
|
|
213
|
-
`sequenceActionTypes`, or `pass:true` unless you also include the canonical
|
|
214
|
-
fields below:
|
|
215
|
-
|
|
216
|
-
```json
|
|
217
|
-
{
|
|
218
|
-
"status": "succeeded",
|
|
219
|
-
"planRevision": "<original planRevision>",
|
|
220
|
-
"laneKey": "<lane key>",
|
|
221
|
-
"laneType": "<lane type>",
|
|
222
|
-
"workspaceId": "<workspace id>",
|
|
223
|
-
"senderIds": ["<sender id>"],
|
|
224
|
-
"campaignId": "<campaign id>",
|
|
225
|
-
"tableId": "<workflow table id>",
|
|
226
|
-
"createCampaignStepReceipt": {
|
|
227
|
-
"campaignBriefReceipt": {
|
|
228
|
-
"campaignId": "<campaign id>",
|
|
229
|
-
"workflowTableId": "<workflow table id>",
|
|
230
|
-
"briefHash": "<brief/currentApprovedBriefHash>",
|
|
231
|
-
"deliveryFormat": "single message",
|
|
232
|
-
"tokenRules": true,
|
|
233
|
-
"hardAvoids": true,
|
|
234
|
-
"sourceUseRules": true,
|
|
235
|
-
"firstMessageTemplate": "<template or template state>"
|
|
236
|
-
},
|
|
237
|
-
"sourceDecisionReceipt": {
|
|
238
|
-
"sourceId": "<source id>",
|
|
239
|
-
"sourceListId": "<source/list id>",
|
|
240
|
-
"cursorOrInventoryState": "<cursor/inventory/import state>",
|
|
241
|
-
"dedupeDncProviderExclusions": true,
|
|
242
|
-
"sourceLadderReason": "<why the current source is valid before colder sources>"
|
|
243
|
-
},
|
|
244
|
-
"filterDecisionReceipt": {
|
|
245
|
-
"status": "skipped",
|
|
246
|
-
"workflowTableId": "<workflow table id>"
|
|
247
|
-
},
|
|
248
|
-
"messageDraftingReceipt": {
|
|
249
|
-
"promptLoadedToHasMoreFalse": true,
|
|
250
|
-
"requiredAssetsLoaded": true,
|
|
251
|
-
"validationLoaded": true,
|
|
252
|
-
"campaignId": "<campaign id>",
|
|
253
|
-
"workflowTableId": "<workflow table id>",
|
|
254
|
-
"selectedLeadListId": "<source/list id>",
|
|
255
|
-
"reviewBatchRowHash": "<review row ids hash>",
|
|
256
|
-
"messageDraftRecommendation": {"basis": "generate-messages"},
|
|
257
|
-
"sampleMessages[].verdict": "pass",
|
|
258
|
-
"sampleMessages": [
|
|
259
|
-
{"rowId": "<row id>", "generatedMessageText": "<message>", "verdict": "pass"}
|
|
260
|
-
]
|
|
261
|
-
},
|
|
262
|
-
"reviewBatchReceipt": {
|
|
263
|
-
"rowIdsHash": "<review row ids hash>",
|
|
264
|
-
"generatedCount": 3
|
|
265
|
-
},
|
|
266
|
-
"sequenceReceipt": {
|
|
267
|
-
"workflowTableId": "<workflow table id>",
|
|
268
|
-
"hasSequence": true,
|
|
269
|
-
"sequenceReceipt.actionTypes": ["send_invite"],
|
|
270
|
-
"sequenceReceipt.nonPaid": true,
|
|
271
|
-
"actionTypes": ["send_invite"],
|
|
272
|
-
"nonPaid": true
|
|
273
|
-
},
|
|
274
|
-
"finalPausedSendProof": {
|
|
275
|
-
"currentStep": "send",
|
|
276
|
-
"campaignStatus": "PAUSED"
|
|
277
|
-
},
|
|
278
|
-
"verifyCall": {
|
|
279
|
-
"mode": "verify",
|
|
280
|
-
"planRevision": "<original planRevision>",
|
|
281
|
-
"selectedActionIds": ["<all original selected action ids>"]
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
```
|
|
286
|
-
|
|
287
|
-
Verification always uses the original execution planRevision and original
|
|
288
|
-
selectedActionIds from the plan packets that were dispatched before mutation.
|
|
289
|
-
The parent must preserve the original selectedActionIds exactly.
|
|
290
|
-
Do not replace them with a later reuse planRevision/actionIds after the
|
|
291
|
-
campaigns exist. After workers complete, attach or patch `verifyCall` on each
|
|
292
|
-
lane receipt with that original execution planRevision, original
|
|
293
|
-
selectedActionIds, same sender scopes, and the verify result. Only after verify
|
|
294
|
-
succeeds should the parent perform the idempotency rerun.
|
|
295
|
-
|
|
296
|
-
If any required field is missing, report
|
|
297
|
-
`blocked: missing_create_campaign_step_receipt` for that lane and do not call it
|
|
298
|
-
complete.
|
|
299
|
-
|
|
300
|
-
Message Drafting runtime rule: the preferred proof source is the normal
|
|
301
|
-
create-campaign branch handoff (`watchNarration.workerDetails.messageDraftBuilder`
|
|
302
|
-
or equivalent) with `statusSource:"branch"`. If that internal branch handoff is
|
|
303
|
-
not callable or not exposed in the packaged MCP runtime, do **not** stop after
|
|
304
|
-
creating/importing rows. The lane worker must run the packaged message-drafting
|
|
305
|
-
path itself in the worker thread:
|
|
306
|
-
|
|
307
|
-
1. Load `get_subskill_prompt({ subskillName:"generate-messages" })` until
|
|
308
|
-
`hasMore:false`.
|
|
309
|
-
2. Load the required message assets and `create-campaign-v2-validation`.
|
|
310
|
-
3. Call `start_campaign_message_preparation({ campaignId, tableId,
|
|
311
|
-
targetPreparedMessages, maxRowsToCheck, approvalMode:"mark_ready",
|
|
312
|
-
autoContinue:true })` inside the same lane worker, with bounded caps from the
|
|
313
|
-
lane packet.
|
|
314
|
-
4. Poll `get_campaign_message_preparation_status` until no queued/active cells
|
|
315
|
-
remain or a bounded stop reason is returned.
|
|
316
|
-
5. Use `get_campaign_messages_preview` or `select_campaign_cells` to inspect
|
|
317
|
-
generated rows, quality-review at least 3 samples, approve exactly one
|
|
318
|
-
quality-valid route-proof row when none is already approved, and prove no
|
|
319
|
-
broad approve-all happened.
|
|
320
|
-
6. Continue to `attach_recommended_sequence({ campaignId, currentStep:"send" })`
|
|
321
|
-
and, if the campaign is still `DRAFT`, `pause_campaign({ campaignId })`.
|
|
322
|
-
Reread the campaign/table before claiming completion.
|
|
323
|
-
|
|
324
|
-
That packaged worker path must return `messageDraftingReceipt.statusSource:
|
|
325
|
-
"packaged-generate-messages-worker"`. It is accepted only when the receipt
|
|
326
|
-
includes the same prompt/assets/validation proof, current campaign/table/source
|
|
327
|
-
basis, generated review rows, sample messages, quality review, route-proof
|
|
328
|
-
approval evidence, sequence proof, and final paused-send proof required of the
|
|
329
|
-
branch handoff. `statusSource:"parent-thread-fallback"` is still invalid.
|
|
330
|
-
|
|
331
|
-
One lane may produce exactly one CampaignOffer/campaign id and one current
|
|
332
|
-
workflow table id. For `intent:"create"`, the first successful
|
|
333
|
-
`create_campaign` result establishes the lane's only allowed campaign/table
|
|
334
|
-
target. Every later source/import/confirm/message/filter/sequence/readback call
|
|
335
|
-
must use that same campaign id and current workflow table id. If any tool
|
|
336
|
-
returns or creates a different CampaignOffer, workflow table, "Campaign from
|
|
337
|
-
Source" campaign, or shell for the same lane, stop immediately with
|
|
338
|
-
`blocked: duplicate_campaign_offer_created`, include both ids in the receipt,
|
|
339
|
-
and do not report completion. Do not hide the earlier shell by reporting only
|
|
340
|
-
the later imported campaign.
|
|
341
|
-
|
|
342
|
-
Each customer-visible lane receipt must also include message proof from
|
|
343
|
-
`watchNarration.workerDetails.messageDraftBuilder`, the worker's equivalent
|
|
344
|
-
branch handoff record, or the packaged message-prep worker path above. Valid
|
|
345
|
-
evergreen message proof names the current campaign id, workflowTableId,
|
|
346
|
-
selected lead-list or source id, filter choice, and review-batch row ids/hash,
|
|
347
|
-
and it must use `statusSource:"branch"` or
|
|
348
|
-
`statusSource:"packaged-generate-messages-worker"`.
|
|
349
|
-
`statusSource:"parent-thread-fallback"` is not accepted for evergreen
|
|
350
|
-
customer-visible completion because it can be a parent summary rather than proof
|
|
351
|
-
that the create-campaign Message Drafting branch actually ran. The proof must
|
|
352
|
-
show the worker loaded the full packaged prompt with
|
|
353
|
-
`get_subskill_prompt({ subskillName:"generate-messages"` until `hasMore:false`,
|
|
354
|
-
loaded the required message assets, and loaded `create-campaign-v2-validation`
|
|
355
|
-
before returning a `messageDraftRecommendation`.
|
|
356
|
-
The receipt must also include `validationResult:"passed"`, a passed
|
|
357
|
-
quality-review object, and at least 3 concrete reviewed sample messages with
|
|
358
|
-
row ids, generated message text, pass verdicts, and no issues. Shared Cold
|
|
359
|
-
Fallback samples must not open with a standalone name line followed by
|
|
360
|
-
"Hey there" or another generic greeting; that pattern means the lane did not
|
|
361
|
-
finish the create-campaign message workflow to customer quality.
|
|
362
|
-
Shared Signal Discovery samples must not use sender-owned post-engager language
|
|
363
|
-
such as "my post", "thanks for showing support", "saw you pop up", or any copy
|
|
364
|
-
that implies the sending sender authored the source post or personally received
|
|
365
|
-
the prospect's engagement unless the selected source evidence proves that exact
|
|
366
|
-
sender authored the post.
|
|
367
|
-
The generated-message cells are not prompt proof: `currentRevisionGeneratedMessages`,
|
|
368
|
-
Generate Message column counts, `messagesCount`, or rendered row previews prove
|
|
369
|
-
only that table cells exist from the current brief/template, not that the
|
|
370
|
-
create-campaign Message Drafting branch ran. If this receipt is missing, report
|
|
371
|
-
`blocked: missing_message_drafting_receipt` and do not call the lane complete.
|
|
372
|
-
|
|
373
|
-
The parent then calls
|
|
374
|
-
`setup_evergreen_campaigns({ mode:"verify", postEngagerSenderIds,
|
|
375
|
-
sharedSenderIds, planRevision, selectedActionIds, receipts })` using the same
|
|
376
|
-
sender scopes as the plan call and the original execution planRevision/actionIds,
|
|
377
|
-
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
|
|
378
|
-
`setup_evergreen_campaigns({ mode:"plan", postEngagerSenderIds,
|
|
379
|
-
sharedSenderIds, yolo:true })` without explicit campaign/table bindings. That
|
|
380
|
-
idempotency rerun must return `intent:"reuse"` for every completed lane and no
|
|
381
|
-
duplicate-lane blockers before you call the command finished. If verify blocks,
|
|
382
|
-
report the blocker and repair only the missing postconditions from the same
|
|
383
|
-
current plan.
|
|
384
|
-
|
|
385
|
-
Message proof for every customer-visible lane must come from the current
|
|
386
|
-
campaign/table basis and include at least 3 generated review rows from the
|
|
387
|
-
packaged `generate-messages` path before completion is reported.
|
|
388
|
-
Parent-thread handwritten copy or setting `currentStep` is not proof.
|
|
389
|
-
</command_backed_workflow>
|
|
390
|
-
|
|
391
54
|
<objective>
|
|
392
55
|
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.
|
|
393
|
-
2. **Create only the missing slots**
|
|
394
|
-
workflow path for each lane, using the lane packet as the setup basis:
|
|
56
|
+
2. **Create only the missing slots** with `create_on_demand_campaign({ name, senderIds, campaignBrief })`:
|
|
395
57
|
- Post Engagers lanes: that sender's ID only. Shared lanes: all the senders' IDs.
|
|
396
|
-
- The brief
|
|
397
|
-
Engagers opening for Shared Cold Fallback.
|
|
398
|
-
- **Post Engagers** first-message style: short, casual, references the
|
|
399
|
-
sender-authored post they engaged with, closed question, **no internal
|
|
400
|
-
vocabulary, no pitch, no meeting ask** in message one:
|
|
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:
|
|
401
59
|
|
|
402
60
|
```
|
|
403
61
|
{{first_name}}
|
|
@@ -407,33 +65,6 @@ Parent-thread handwritten copy or setting `currentStep` is not proof.
|
|
|
407
65
|
Saw you pop up on my post about {{post_topic_line}}
|
|
408
66
|
```
|
|
409
67
|
|
|
410
|
-
- **Shared Signal Discovery** first-message style: short, casual, references
|
|
411
|
-
the real observed signal/theme or public conversation. It may mention the
|
|
412
|
-
engagement source only when the source evidence supports it. Do not say
|
|
413
|
-
"my post" unless the selected source post was actually authored by the
|
|
414
|
-
sending sender. Do not thank the prospect for showing support, reacting,
|
|
415
|
-
commenting, or popping up unless this is a sender-owned Post Engagers lane
|
|
416
|
-
with sender-authored source evidence.
|
|
417
|
-
- **Shared Cold Fallback** first-message style: cold, direct, and cohesive.
|
|
418
|
-
It must not pretend there was engagement, must not say "saw you pop up",
|
|
419
|
-
and must not use a standalone name line followed by "Hey there" or other
|
|
420
|
-
generic greeting. It should open with a light role/company/problem
|
|
421
|
-
observation, avoid awkward phrases like "looks like a team where", and ask
|
|
422
|
-
one clear problem question. This is a single-message
|
|
423
|
-
cold path unless the approved create-campaign message workflow proves a
|
|
424
|
-
better delivery format.
|
|
425
|
-
- Every customer-visible lane must run Message Drafting from the current
|
|
426
|
-
campaign/table basis after source rows exist. The worker must load the full
|
|
427
|
-
packaged `generate-messages` prompt, every required asset, and
|
|
428
|
-
`create-campaign-v2-validation`; if the internal branch handoff is not
|
|
429
|
-
callable in the packaged runtime, the worker must call
|
|
430
|
-
`start_campaign_message_preparation` and poll
|
|
431
|
-
`get_campaign_message_preparation_status` from the lane worker. The
|
|
432
|
-
returned receipt must include `statusSource:"branch"` or
|
|
433
|
-
`statusSource:"packaged-generate-messages-worker"`, at least 3 concrete
|
|
434
|
-
reviewed sample messages, a passed validation result, and a passed quality
|
|
435
|
-
review. Row/message counts alone are not enough.
|
|
436
|
-
|
|
437
68
|
- **The brief must declare the delivery format** so Generate Message writes copy suited to how it actually sends:
|
|
438
69
|
- 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.
|
|
439
70
|
- **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.
|
|
@@ -459,73 +90,26 @@ Parent-thread handwritten copy or setting `currentStep` is not proof.
|
|
|
459
90
|
- Message Drafting has run from the current campaign/table basis, using the
|
|
460
91
|
create-campaign message prompt/assets and validation gate. Updating
|
|
461
92
|
`currentStep:"messages"` is not proof. Parent-thread handwritten copy is
|
|
462
|
-
not a substitute.
|
|
463
|
-
`get_subskill_prompt({ subskillName:"generate-messages"` was loaded until
|
|
464
|
-
`hasMore:false`, required message assets were loaded, and
|
|
465
|
-
`create-campaign-v2-validation` ran through
|
|
466
|
-
`workerDetails.messageDraftBuilder` with `statusSource:"branch"` or
|
|
467
|
-
through the lane worker's packaged message-prep path with
|
|
468
|
-
`statusSource:"packaged-generate-messages-worker"`,
|
|
469
|
-
`start_campaign_message_preparation`, and
|
|
470
|
-
`get_campaign_message_preparation_status`. The generated-message cells are
|
|
471
|
-
not prompt proof; `currentRevisionGeneratedMessages` and row previews are
|
|
472
|
-
only downstream cell evidence. The proof must include
|
|
473
|
-
`validationResult:"passed"`, a passed quality review, and at least 3
|
|
474
|
-
reviewed sample messages. For Shared Cold Fallback, reject samples that
|
|
475
|
-
open with a standalone name followed by "Hey there" or a similarly generic
|
|
476
|
-
greeting.
|
|
93
|
+
not a substitute.
|
|
477
94
|
- The first review batch exists and at least 3 review rows have generated
|
|
478
95
|
messages from the approved brief. If fewer than 3 usable rows exist, report
|
|
479
96
|
the actual count and why.
|
|
480
|
-
If a bounded preparation job requested more than the minimum but already
|
|
481
|
-
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
|
|
482
|
-
available, and continue with route-proof review rather than expanding source
|
|
483
|
-
or waiting forever.
|
|
484
97
|
- At least one generated row is approved as a route-proof gate. If zero rows
|
|
485
98
|
are approved and the prompt explicitly asked for full/customer-ready
|
|
486
99
|
completion, approve exactly one quality-valid generated row. If one or more
|
|
487
100
|
rows are already approved, do not add more approvals during evergreen
|
|
488
101
|
completion. Never broad approve all rows.
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
current workflowTableId with `list_tables` and require `hasSequence:true`
|
|
496
|
-
before reporting completion. If a stale shell table has sequence columns but
|
|
497
|
-
the current campaign table does not, `SEQUENCE_EXISTS` is not enough:
|
|
498
|
-
record the stale shell table id, then repair the current workflowTableId
|
|
499
|
-
with `attach_sequence` using the same non-paid product template
|
|
500
|
-
`attach_recommended_sequence` would have selected. Never use a paid-InMail
|
|
501
|
-
template and never attach/replace sequence outside the current table unless
|
|
502
|
-
the current lane packet explicitly allowed sequence repair.
|
|
503
|
-
- If the current campaign table is still `DRAFT` after sequence/readiness
|
|
504
|
-
proof, call `pause_campaign({ campaignId })` and reread. `pause_campaign`
|
|
505
|
-
is the product-native review-state transition; it is not a launch and does
|
|
506
|
-
not schedule or send. Never raw-write campaign status.
|
|
507
|
-
- No scheduled, queued outbound, sent outbound, campaign launch, or paid
|
|
508
|
-
InMail spend is created by this skill.
|
|
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.
|
|
509
108
|
4. **Verify each slot** after create/reuse/repair:
|
|
510
109
|
- `get_campaign` shows the campaign exists, remains unlaunched, and has the expected workflow table.
|
|
511
110
|
- 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.
|
|
512
|
-
- Send/action lanes such as Post Engagers have a sequence attached
|
|
513
|
-
|
|
514
|
-
quick cross-check, but do not treat that filter as the only proof. If the
|
|
515
|
-
canonical current campaign table is missing from the sequence-filtered
|
|
516
|
-
table list and `attach_recommended_sequence({ campaignId })` returns
|
|
517
|
-
`SEQUENCE_EXISTS`, inspect whether a stale shell table owns the sequence.
|
|
518
|
-
For a customer-visible completion run, stale shell proof is not completion:
|
|
519
|
-
rerun the current-table repair above or report `blocked: stale_shell_sequence`.
|
|
520
|
-
- Only use `attach_recommended_sequence({ campaignId })` for a send-lane
|
|
521
|
-
sequence repair/precheck when the target is a canonical prod slot and the
|
|
522
|
-
invoking prompt allows sequence repair. Call it at most once without
|
|
523
|
-
`confirmed`; a successful response is `repaired`. Treat `SEQUENCE_EXISTS` as
|
|
524
|
-
non-mutating proof only when the current table also has `hasSequence:true`.
|
|
525
|
-
Never call `attach_recommended_sequence` or `attach_sequence` with
|
|
526
|
-
`confirmed:true` in evergreen automation unless the user explicitly asks to
|
|
527
|
-
replace an existing sequence or the approved lane packet allows current
|
|
528
|
-
table stale-shell repair.
|
|
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.
|
|
529
113
|
- Do not report `source-only/no sequence expected` for any named evergreen
|
|
530
114
|
campaign that appears in Campaigns, has a CampaignOffer ID, or is backed by
|
|
531
115
|
a campaign dashboard table. Shared Signal Discovery and Shared Cold
|
|
@@ -9,9 +9,6 @@ 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
|
|
15
12
|
- mcp__sellable__get_campaign_message_preparation_status
|
|
16
13
|
- mcp__sellable__start_campaign_message_preparation
|
|
17
14
|
- mcp__sellable__cancel_campaign_message_preparation
|
|
@@ -19,7 +16,6 @@ allowed-tools:
|
|
|
19
16
|
- mcp__sellable__wait_for_lead_list_ready
|
|
20
17
|
- mcp__sellable__confirm_lead_list
|
|
21
18
|
- mcp__sellable__search_signals
|
|
22
|
-
- mcp__sellable__select_promising_posts
|
|
23
19
|
- mcp__sellable__fetch_post_engagers
|
|
24
20
|
- mcp__sellable__search_sales_nav
|
|
25
21
|
- mcp__sellable__lookup_sales_nav_filter
|
|
@@ -33,10 +29,6 @@ allowed-tools:
|
|
|
33
29
|
- mcp__sellable__get_rows
|
|
34
30
|
- mcp__sellable__get_rows_minimal
|
|
35
31
|
- 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
|
|
40
32
|
---
|
|
41
33
|
|
|
42
34
|
# Refill Sends
|
|
@@ -7,9 +7,6 @@ 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
|
|
13
10
|
- mcp__sellable__get_campaign_message_preparation_status
|
|
14
11
|
- mcp__sellable__start_campaign_message_preparation
|
|
15
12
|
- mcp__sellable__cancel_campaign_message_preparation
|
|
@@ -17,7 +14,6 @@ allowed-tools:
|
|
|
17
14
|
- mcp__sellable__wait_for_lead_list_ready
|
|
18
15
|
- mcp__sellable__confirm_lead_list
|
|
19
16
|
- mcp__sellable__search_signals
|
|
20
|
-
- mcp__sellable__select_promising_posts
|
|
21
17
|
- mcp__sellable__fetch_post_engagers
|
|
22
18
|
- mcp__sellable__search_sales_nav
|
|
23
19
|
- mcp__sellable__lookup_sales_nav_filter
|
|
@@ -31,10 +27,6 @@ allowed-tools:
|
|
|
31
27
|
- mcp__sellable__get_rows
|
|
32
28
|
- mcp__sellable__get_rows_minimal
|
|
33
29
|
- 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
|
|
38
30
|
---
|
|
39
31
|
|
|
40
32
|
# Refill Sends Workflow
|
|
@@ -109,42 +101,6 @@ Source replenishment choices:
|
|
|
109
101
|
source prospects into the same campaign/source-list path, then confirm
|
|
110
102
|
readiness.
|
|
111
103
|
|
|
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
|
-
|
|
148
104
|
Before any source mutation, show exact approval evidence: workspace id,
|
|
149
105
|
campaign id, workflow table id, source/provider id, source-list id when known,
|
|
150
106
|
import caps, target dates/caps, dedupe/DNC/provider exclusion expectations, and
|
|
@@ -179,28 +135,11 @@ Before prep or approval mutation, reread `get_campaign_refill_state` and compare
|
|
|
179
135
|
If they changed, stop. A second concurrent refill run must see the first run's
|
|
180
136
|
import/prep state on reread and avoid duplicate work.
|
|
181
137
|
|
|
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
|
-
|
|
194
138
|
Scheduled success requires a post-action reread proving scheduler-owned cells
|
|
195
139
|
with non-null `scheduledFor`. Prepared, approved, and ready-to-schedule rows are
|
|
196
140
|
intermediate states; report them as awaiting scheduler unless scheduled cells
|
|
197
141
|
are present.
|
|
198
142
|
|
|
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
|
-
|
|
204
143
|
## Evergreen Campaign Discipline
|
|
205
144
|
|
|
206
145
|
`fill_campaign_horizon` is a legacy evergreen-only lower-level primitive. Call it
|