@sellable/mcp 0.1.557 → 0.1.559
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -13
- package/agents/registry.json +2 -2
- package/dist/api.js +6 -3
- package/dist/auth.d.ts +6 -0
- package/dist/auth.js +44 -2
- package/dist/refill-contract.d.ts +157 -0
- package/dist/refill-contract.js +487 -0
- package/dist/refill-run-client.d.ts +10 -0
- package/dist/refill-run-client.js +22 -0
- package/dist/refill-run-loop.d.ts +14 -2
- package/dist/refill-run-loop.js +169 -18
- package/dist/server.js +0 -23
- package/dist/tools/auth.d.ts +5 -0
- package/dist/tools/auth.js +49 -12
- package/dist/tools/campaign-message-preparation.d.ts +62 -0
- package/dist/tools/campaign-message-preparation.js +41 -0
- package/dist/tools/campaigns.js +2 -2
- package/dist/tools/csv-dnc.js +2 -2
- package/dist/tools/evergreen-refill-plan.d.ts +3 -0
- package/dist/tools/evergreen-refill-plan.js +29 -7
- package/dist/tools/leads.d.ts +32 -317
- package/dist/tools/leads.js +10 -171
- package/dist/tools/model-quality.js +6 -4
- package/dist/tools/prompts.d.ts +3 -3
- package/dist/tools/prompts.js +15 -7
- package/dist/tools/provider-preflight.d.ts +2 -65
- package/dist/tools/provider-preflight.js +10 -97
- package/dist/tools/readiness.d.ts +5 -89
- package/dist/tools/readiness.js +0 -66
- package/dist/tools/refill-executors.d.ts +38 -0
- package/dist/tools/refill-executors.js +222 -3
- package/dist/tools/refill-sends-v2.d.ts +118 -1
- package/dist/tools/refill-sends-v2.js +312 -3
- package/dist/tools/refill-sends.d.ts +678 -32
- package/dist/tools/refill-sends.js +274 -13
- package/dist/tools/refill-target-plan.js +486 -14
- package/dist/tools/registry.d.ts +115 -330
- package/dist/tools/registry.js +1 -7
- package/dist/tools/scheduler-fill-capacity.js +1 -1
- package/dist/tools/scheduler-run.d.ts +71 -0
- package/dist/tools/scheduler-run.js +203 -1
- package/dist/tools/setup-evergreen-campaigns.js +1 -1
- package/dist/tools/workspace-context.d.ts +1 -1
- package/dist/tools/workspace-context.js +8 -3
- package/dist/tools/workspace-export.js +2 -2
- package/dist/tools/workspaces.d.ts +48 -2
- package/dist/tools/workspaces.js +48 -5
- package/package.json +1 -1
- package/skills/create-campaign/SKILL.md +3 -3
- package/skills/create-campaign-v2/SKILL.md +1 -1
- package/skills/create-evergreen-campaigns/SKILL.md +16 -16
- package/skills/find-leads/SKILL.md +630 -48
- package/skills/refill-sends/SKILL.md +94 -353
- package/skills/refill-sends-v2/SKILL.md +6 -6
- package/skills/refill-sends-v2-workflow/SKILL.md +5 -5
- package/skills/refill-sends-v2-workflow/core/flow.v1.json +8 -8
- package/skills/refill-sends-workflow/SKILL.md +100 -743
- package/skills/refill-sends-workflow/core/contract.v2.json +543 -0
- package/skills/refill-sends-workflow/core/flow.v1.json +185 -1
- package/dist/tools/find-leads-runs.d.ts +0 -151
- package/dist/tools/find-leads-runs.js +0 -98
- package/skills/find-leads-v2/SKILL.md +0 -70
- package/skills/find-leads-v2/core/flow.v1.json +0 -31
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: refill-sends-workflow
|
|
3
|
-
description: Internal
|
|
3
|
+
description: Internal typed workflow contract for regular and evergreen campaign refills.
|
|
4
4
|
visibility: internal
|
|
5
5
|
allowed-tools:
|
|
6
6
|
- mcp__sellable__get_subskill_asset
|
|
@@ -49,748 +49,105 @@ allowed-tools:
|
|
|
49
49
|
|
|
50
50
|
# Refill Sends Workflow
|
|
51
51
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
`
|
|
71
|
-
`
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
source-more. A new source or provider switch changes the reply-rate baseline
|
|
145
|
-
and is a manual alternate, not a `--yolo` side effect.
|
|
146
|
-
Source/copy/fallback requires receipt-proven exhaustion of earlier rungs:
|
|
147
|
-
`existingRowFrontier.hasMoreFrontierRows:false`, zero
|
|
148
|
-
`approvalCandidates`, no fresh active prep, no `stuckActiveCells`, and no
|
|
149
|
-
non-terminal `approvedNotDispatched` rows. Treat anomalies,
|
|
150
|
-
`stuckActiveCells`, and non-terminal `approvedNotDispatched` as
|
|
151
|
-
diagnose-and-report gates, not exhaustion. Terminal
|
|
152
|
-
`approvedNotDispatched` blockers may be reported, then the ladder can
|
|
153
|
-
proceed.
|
|
154
|
-
Run-local paid-credit guard: in `--yolo`, the `refill_sends` MCP command
|
|
155
|
-
automatically maintains a `refreshedPaidInmailSenderIds` set for the current
|
|
156
|
-
command call. If its first target plan has stale/missing paid-InMail credit
|
|
157
|
-
facts, it refreshes each selected sender at most once, reruns
|
|
158
|
-
`get_refill_target_plan`, and returns the post-refresh `targetPlan` before
|
|
159
|
-
choosing the next prep/source-copy/bounded-approval/read-only wait action. If fresh facts are still below
|
|
160
|
-
threshold, below-threshold paid-InMail facts fall back to an existing
|
|
161
|
-
connection lane, the same Sales Nav cascade campaign's connection branch, or
|
|
162
|
-
a manual continuation.
|
|
163
|
-
Freshness gate precedes scheduler wait: if any selected
|
|
164
|
-
`target.senderRefillPlans[].paidInmail.status` is `missing_credit_facts` or
|
|
165
|
-
`stale_credit_facts`, or the target plan contains a
|
|
166
|
-
`refresh_paid_inmail_credits` candidate, do not enter
|
|
167
|
-
`wait_for_scheduler` even when `remainingReadyOrProjectedGap:0`. Refresh the
|
|
168
|
-
exact selected sender credit facts once, rerun `get_refill_target_plan`, and
|
|
169
|
-
only then decide whether scheduler wait is the next safe action. If facts
|
|
170
|
-
remain missing/stale after the single refresh attempt, stop with a
|
|
171
|
-
paid-InMail freshness blocker instead of waiting on scheduler pickup.
|
|
172
|
-
The standalone MCP refresh surface is the scoped route
|
|
173
|
-
`/api/v3/mcp/senders/:senderId/refresh-inmail-credits` with explicit
|
|
174
|
-
`workspaceId`; do not use active-workspace mutation as the automation
|
|
175
|
-
control path.
|
|
176
|
-
Compact refill lessons: sender-level target plan is final truth; trust
|
|
177
|
-
`schedulerGate.sendable` and scheduler gate blockers, not raw
|
|
178
|
-
`unipileAccountStatus` labels alone; use compact prep status checks for
|
|
179
|
-
long-running jobs; reread target plans after prep or cancel; treat ready rows
|
|
180
|
-
as intermediate; avoid huge parallel target-plan reads when output is large.
|
|
181
|
-
If a campaign produces prepared/ready rows but sender-level projected
|
|
182
|
-
coverage does not move after one bounded settle loop, pivot to compact prep
|
|
183
|
-
status or a scheduler-proven lane. Do not keep waiting on campaign-level
|
|
184
|
-
ready counts.
|
|
185
|
-
For exact-date requests, pass `targetDate` to `get_refill_target_plan`.
|
|
186
|
-
When you need raw diagnostic proof, call `get_scheduler_fill_capacity` for
|
|
187
|
-
the same exact sender/action/date. That query is read-only and tells the MCP
|
|
188
|
-
how many cells the product scheduler will try to place for that sender; it
|
|
189
|
-
does not create rows, import, approve, schedule, refresh paid-InMail credits,
|
|
190
|
-
or mutate thresholds.
|
|
191
|
-
When ready rows exist and the wait is for scheduler pickup, call
|
|
192
|
-
`run_scheduler_sweep` with the same explicit `workspaceId` to request the
|
|
193
|
-
product scheduler placement pass now and read its receipt. This may place
|
|
194
|
-
cells within existing gates, never sends messages, and never bypasses limits.
|
|
195
|
-
Scheduler-run receipt interpretation: `cellsConsidered is
|
|
196
|
-
allocation-attempt count`, not total ready supply, while `readyCellsFound`
|
|
197
|
-
is ready inventory found before prefilters. Inspect `campaignScopeSummary`
|
|
198
|
-
before assuming the selected refill campaign/table was included; if absent,
|
|
199
|
-
do not infer that target was ready-but-blocked. Interpret `prefiltered` as
|
|
200
|
-
ready cells removed before allocation, `skipped` as considered cells blocked
|
|
201
|
-
by scheduler gates, and `deferred` as considered cells waiting on
|
|
202
|
-
windows/capacity/cooldown. For ready closed-InMail cells with stale
|
|
203
|
-
paid-credit prefilter/defer reasons, refresh paid-InMail credits once through
|
|
204
|
-
existing tools, then rerun `run_scheduler_sweep` or read `action:"status"`;
|
|
205
|
-
`refresh_paid_inmail_credits_then_rerun` is that path.
|
|
206
|
-
`wait_for_capacity_or_window` means report loaded/capped/waiting and do not
|
|
207
|
-
source or prep more rows; `no_ready_cells_continue_refill_prep` means return
|
|
208
|
-
to the refill/prep ladder. Do not treat `cellsScheduled:0` alone as failure.
|
|
209
|
-
If `status:"complete"`, report the target, selected dates, sent count,
|
|
210
|
-
scheduled count, projected count, campaign ids, and no-op proof without
|
|
211
|
-
asking for approval or mutating.
|
|
212
|
-
If `remainingReadyOrProjectedGap:0` but `remainingProjectedGap>0`, and paid
|
|
213
|
-
InMail credit freshness is clean for every selected paid-InMail lane, run
|
|
214
|
-
only a persistent read-only scheduler wait/reread loop; do not ask for
|
|
215
|
-
prep/import/approval. Poll `get_refill_target_plan` every 60-120 seconds, or
|
|
216
|
-
on the host's next continuation interval, until projected coverage fills,
|
|
217
|
-
a concrete non-scheduler blocker appears, or Christian explicitly asks to
|
|
218
|
-
stop or only receive a status report. Treat
|
|
219
|
-
`awaiting_scheduler_after_ready_buffer` as an in-progress wait state, not a
|
|
220
|
-
close-out condition.
|
|
221
|
-
Wait actions are gates, not competing goals. When `wait_for_active_work` or
|
|
222
|
-
`wait_for_scheduler` includes receipt `wait.deadlineAt`, honor that absolute
|
|
223
|
-
deadline; if it is expired on this call, escalate to diagnostics with the
|
|
224
|
-
receipt evidence instead of issuing another blind wait.
|
|
225
|
-
If paid InMail credit facts are stale or missing and the first target plan
|
|
226
|
-
contains `refresh_paid_inmail_credits`, do not present that as the operator's
|
|
227
|
-
next action in `--yolo`. Do not present paid-credit refresh as the next
|
|
228
|
-
operator action after `refill_sends` returns `autoPaidInmailRefresh` and the
|
|
229
|
-
post-refresh `targetPlan`. Trust `refill_sends.autoPaidInmailRefresh`: it
|
|
230
|
-
should show the exact sender ids refreshed once, sender-credit-cache write
|
|
231
|
-
receipts, and a returned post-refresh `targetPlan`. Continue from that
|
|
232
|
-
post-refresh packet. If paid InMail is below threshold after the fresh credit read, report
|
|
233
|
-
the exact campaign/table/column threshold action or same-campaign
|
|
234
|
-
connection fallback; `--yolo` does not lower paid-InMail thresholds or create campaigns.
|
|
235
|
-
2. Call `resolve_campaign_fill_route`. Use `intent:"plain"` for generic
|
|
236
|
-
fill/load language, `intent:"active"` only when the user explicitly narrowed
|
|
237
|
-
to active regular campaigns or when the plain route has stale managed
|
|
238
|
-
waterfall evidence, and `intent:"evergreen"` only when the user explicitly
|
|
239
|
-
asked for evergreen or horizon work. The resolver must refetch current
|
|
240
|
-
dashboard-active campaigns as well as managed waterfall state. If the plain
|
|
241
|
-
route returns managed waterfall targets but skipped targets include
|
|
242
|
-
archived/completed waterfall slots, or if the returned targets do not cover a
|
|
243
|
-
named sender, immediately call `resolve_campaign_fill_route({ intent:"active" })`
|
|
244
|
-
and inspect current dashboard-active `ACTIVE` and `PAUSED` campaign-backed
|
|
245
|
-
sequence campaigns before declaring a sender blocked.
|
|
246
|
-
Short form: stale waterfall bindings do not overrule current dashboard-active campaigns.
|
|
247
|
-
3. Resolve sender context before choosing a target. Call `list_senders`; when a
|
|
248
|
-
managed program is involved, also call `get_campaign_waterfall`, and call
|
|
249
|
-
`get_sender_routing` when territory rules may constrain sender eligibility.
|
|
250
|
-
If the user named senders, map those names to exact sender ids. If the user
|
|
251
|
-
did not name senders and the request is sender-scoped:
|
|
252
|
-
- in `--yolo`, target all eligible healthy senders enrolled in active
|
|
253
|
-
campaign-backed sequence campaigns returned by the route/state evidence;
|
|
254
|
-
- outside `--yolo`, ask which eligible enrolled senders to refill before
|
|
255
|
-
choosing campaigns or mutating.
|
|
256
|
-
If the request is campaign-scoped or ambiguous "fill sends" language, infer
|
|
257
|
-
the relevant senders from the most recent scheduler-owned sends in
|
|
258
|
-
refill-state readbacks and report that inference.
|
|
259
|
-
Short form: `--yolo refill senders` means all eligible enrolled senders; non-yolo asks which enrolled senders.
|
|
260
|
-
4. Read `get_campaign_refill_state` for candidate campaigns from the resolver
|
|
261
|
-
result and from the required active-route refetch when managed waterfall
|
|
262
|
-
evidence looks stale. For a broad `active_campaigns` route, inspect enough
|
|
263
|
-
candidates to identify the campaign or small campaign set that most recently
|
|
264
|
-
had scheduler-owned sends for the relevant sender set. Do not stop after the
|
|
265
|
-
first resolver target unless it is also the most recent sender-relevant send
|
|
266
|
-
lane. A current dashboard-active `PAUSED` campaign-backed sequence campaign is
|
|
267
|
-
a start-eligible refill candidate, not an archived blocker; read its refill
|
|
268
|
-
state first, then include a bounded `start_campaign` packet only when the
|
|
269
|
-
state proves healthy senders, sequence, source/list, ready/preparable rows,
|
|
270
|
-
and starting is needed for the product scheduler to schedule/send.
|
|
271
|
-
Short form: Do not stop after the first resolver target unless recent-send evidence supports it.
|
|
272
|
-
Short form: `PAUSED` dashboard-active campaigns are start-eligible candidates, not skipped archived inventory.
|
|
273
|
-
In `--yolo` sender-scoped mode, selecting every active campaign is still a
|
|
274
|
-
scope error, but selecting one best refill campaign per eligible sender is
|
|
275
|
-
expected. Pick the single highest-ranked recent-send or best-result campaign
|
|
276
|
-
for each target sender unless the refill-state evidence proves that a sender
|
|
277
|
-
already has enough ready/scheduled rows across enrolled campaigns or that a
|
|
278
|
-
different same-sender campaign is needed because the best lane is blocked.
|
|
279
|
-
Short form: `--yolo refill senders` selects all eligible enrolled senders, then the best campaign per sender.
|
|
280
|
-
5. Pick exact ids from the selected refill-state response. Do not use campaign
|
|
281
|
-
names or table names as target identifiers for tool calls.
|
|
282
|
-
6. Classify the target from refill state:
|
|
283
|
-
- regular campaign: campaign-backed workflow table with normal source,
|
|
284
|
-
sequence, senders, and send/refill stages.
|
|
285
|
-
- evergreen campaign: managed always-on lane with current source evidence.
|
|
286
|
-
- `unsupported_campaign_type`: internal direct campaign table; report the
|
|
287
|
-
blocker and stop without mutation.
|
|
288
|
-
|
|
289
|
-
## Research Checklist
|
|
290
|
-
|
|
291
|
-
Use the refill-state response as the current facts receipt:
|
|
292
|
-
|
|
293
|
-
- campaign status/current step, workflow table id, table config type, and public
|
|
294
|
-
type, including whether the route target is dashboard-active `PAUSED` and
|
|
295
|
-
start-eligible;
|
|
296
|
-
- selected source list/provider identity, source id, target prospect gap,
|
|
297
|
-
cursor/inventory state, dedupe/DNC/provider exclusions, good-prospect yield,
|
|
298
|
-
scan/import caps, and proof;
|
|
299
|
-
- sender ids, healthy senders, disconnected senders, and daily capacity;
|
|
300
|
-
- target-window saturation ledger per target sender: sender-local send days in
|
|
301
|
-
scope, gross target capacity, actual sent coverage, future scheduler-owned
|
|
302
|
-
scheduled counts in that window, projected coverage (`sent + scheduled`),
|
|
303
|
-
ready-to-schedule buffer, remaining projected gap, paid-InMail
|
|
304
|
-
credit/threshold feasibility, `targetShapeRevision`, `stateRevision`, and the
|
|
305
|
-
next smallest MCP primitive that can reduce the gap;
|
|
306
|
-
- row/stage counts: source available, rows, enriched, passed, generated,
|
|
307
|
-
approved, ready to schedule, scheduled;
|
|
308
|
-
- active message prep job;
|
|
309
|
-
- scheduler-owned scheduled counts by date/action from cells with non-null
|
|
310
|
-
`scheduledFor`; treat connection-invite and paid-InMail cells as the refill
|
|
311
|
-
lane continuation signal and the last sends/continuation signal, and ignore
|
|
312
|
-
DM/open-InMail cells for refill target capacity;
|
|
313
|
-
- latest scheduled send date, latest scheduled-cell update when present,
|
|
314
|
-
sender overlap with the requested or inferred sender set, and whether future
|
|
315
|
-
scheduled sends already exist;
|
|
316
|
-
- `freshness.stateHash` and exact campaign/table/workspace ids.
|
|
317
|
-
|
|
318
|
-
Empty or unsafe states stop with the blocker: no fillable target, missing or deleted campaign,
|
|
319
|
-
missing table, archived/completed campaign, paused campaign without an explicit
|
|
320
|
-
selected-start packet, zero sender capacity, zero source candidates, zero viable
|
|
321
|
-
rows, no approved/preparable rows, active prep job, awaiting scheduler, or
|
|
322
|
-
unsupported direct campaign type.
|
|
323
|
-
|
|
324
|
-
## Regular Campaign Decision
|
|
325
|
-
|
|
326
|
-
For regular campaign workspaces, rank regular campaigns from route and
|
|
327
|
-
`get_campaign_refill_state` evidence. Choose the best existing refill target.
|
|
328
|
-
The default campaign-scoped refill interpretation is "continue the campaign that
|
|
329
|
-
was actually sending most recently for the relevant available senders", not
|
|
330
|
-
"touch every active campaign". The default sender-scoped refill interpretation is
|
|
331
|
-
"fill every eligible target sender from the best enrolled active campaign for
|
|
332
|
-
that sender", not "fill only the first/best campaign globally".
|
|
333
|
-
|
|
334
|
-
When managed waterfall bindings point at archived/completed campaigns but the
|
|
335
|
-
dashboard has current unarchived `ACTIVE` or `PAUSED` campaigns for the same
|
|
336
|
-
sender/shared lane, prefer the current dashboard-active campaign for target
|
|
337
|
-
discovery. The stale waterfall slot remains diagnostic proof, not a reason to
|
|
338
|
-
skip a visible paused shared campaign that can be started through a bounded
|
|
339
|
-
packet.
|
|
340
|
-
|
|
341
|
-
Default `--yolo` target is the scheduler-forward 48-hour window when the user
|
|
342
|
-
asks to fill, refill, load, or max sends without naming a different horizon or
|
|
343
|
-
exact `targetDate`.
|
|
344
|
-
The planner selects sender-local days whose configured sending windows overlap
|
|
345
|
-
that rolling target window; do not treat weekends or other no-send-hour days as
|
|
346
|
-
fillable capacity. If the user explicitly names a no-send calendar date, report
|
|
347
|
-
that date as unschedulable and continue only for the next valid send day when
|
|
348
|
-
the packet says "send days" or the user asked to max/refill senders broadly.
|
|
349
|
-
|
|
350
|
-
For campaign-scoped requests, compute the target from scheduler-fillable
|
|
351
|
-
capacity in the selected target window/date, not the full daily limit. For
|
|
352
|
-
sender-scoped requests, compute that scheduler-fillable gap per target sender
|
|
353
|
-
across all active enrolled campaigns for that sender; count ready-to-schedule
|
|
354
|
-
cells in any active enrolled campaign toward that sender's fillable gap before
|
|
355
|
-
preparing more rows. If ready rows already cover a sender's scheduler-fillable
|
|
356
|
-
gap, do not add rows or prep for that sender; report that the sender is loaded,
|
|
357
|
-
awaiting scheduler. If the gap is positive, prepare only the gap in the best
|
|
358
|
-
same-sender campaign and keep all source-copy/prep caps tied to that target
|
|
359
|
-
window/date packet.
|
|
360
|
-
Short form: default `--yolo refill senders` target is each eligible sender's
|
|
361
|
-
scheduler-forward 48-hour gap.
|
|
362
|
-
|
|
363
|
-
Lane selection must choose one refill lane per sender from current
|
|
364
|
-
dashboard-active campaign-backed sequence evidence:
|
|
365
|
-
|
|
366
|
-
1. Prefer the sender's most recent future scheduled refill action, then the most
|
|
367
|
-
recent recent scheduled refill action, then ready refill rows.
|
|
368
|
-
2. Refill actions are `send_invite` or `send_inmail_closed`; unified Sales Nav
|
|
369
|
-
cascades also appear as `send_inmail_closed` with
|
|
370
|
-
`campaignClassification:"sales_nav_cascade"`.
|
|
371
|
-
3. If the most recent active campaign evidence is DM, ignore it for refill lane
|
|
372
|
-
selection. If open InMail evidence belongs to a unified Sales Nav cascade,
|
|
373
|
-
keep the cascade campaign as the selected `send_inmail_closed` target; do not
|
|
374
|
-
create side open/paid/connection campaigns.
|
|
375
|
-
4. If no connection/paid-InMail evidence exists, fall back to the selected
|
|
376
|
-
active campaign sequence, still choosing only one of `send_invite` or
|
|
377
|
-
`send_inmail_closed` for that sender.
|
|
378
|
-
|
|
379
|
-
For `--yolo` fill/schedule requests, do not treat "ready" as completion. Build
|
|
380
|
-
and maintain a target-window saturation ledger for every selected sender from
|
|
381
|
-
`get_refill_target_plan`. A sender is complete only when a final target-plan or
|
|
382
|
-
refill-state reread proves projected coverage (`sent + scheduled`) meets or
|
|
383
|
-
exceeds the sender's capacity for the selected target window. Put another way:
|
|
384
|
-
complete when the same daily-limit/readback model would show no remaining
|
|
385
|
-
send-day capacity for that selected lane. Ready-to-schedule rows are only buffer
|
|
386
|
-
for the product scheduler. If ready plus projected coverage covers the target
|
|
387
|
-
window but scheduled cells do not yet, run a persistent read-only scheduler wait
|
|
388
|
-
loop: wait, reread, recompute the ledger, and continue until projected coverage
|
|
389
|
-
is proved, a concrete non-scheduler blocker appears, or Christian explicitly
|
|
390
|
-
stops or asks for status only. `awaiting_scheduler_after_ready_buffer` is not
|
|
391
|
-
success and is not a terminal blocker for an active refill goal; it is the
|
|
392
|
-
loaded, awaiting scheduler poll state that keeps the thread waiting. It does not
|
|
393
|
-
need a prep/import/approval packet.
|
|
394
|
-
Short form: ready buffer is not success.
|
|
395
|
-
Short form: keep polling the scheduler instead of closing out while ready buffer
|
|
396
|
-
covers the gap.
|
|
397
|
-
Short form: `--yolo refill senders` completes only on projected target-window
|
|
398
|
-
saturation or a concrete blocker.
|
|
399
|
-
|
|
400
|
-
Rank candidates in this order:
|
|
401
|
-
|
|
402
|
-
1. **Sender match**: campaigns whose healthy sender ids match the user-named
|
|
403
|
-
senders, or the inferred sender set when the user said only "fill sends" or
|
|
404
|
-
"load senders". Do not include campaigns merely because they are active when
|
|
405
|
-
their sender set is unrelated.
|
|
406
|
-
2. **Recent sends**: campaigns with the newest scheduler-owned scheduled counts
|
|
407
|
-
and non-null `scheduledFor` cells for those senders. Future scheduled sends
|
|
408
|
-
are stronger continuation evidence than old historical sends; otherwise use
|
|
409
|
-
the latest scheduled send date / newest scheduled-cell update from refill
|
|
410
|
-
state.
|
|
411
|
-
3. **Continuation capacity**: campaigns with healthy senders, remaining daily
|
|
412
|
-
capacity, and a gap between current ready-to-schedule rows and the desired
|
|
413
|
-
sender capacity.
|
|
414
|
-
4. **Same-campaign source health**: prefer campaigns whose current source has
|
|
415
|
-
enough `sourceAvailable` inventory to prepare the gap. If source inventory
|
|
416
|
-
is low or exhausted, choose same-campaign row replenishment before switching
|
|
417
|
-
campaigns.
|
|
418
|
-
5. **Campaign scope**: keep the selected set small. For campaign-scoped
|
|
419
|
-
requests, normally choose one campaign. For sender-scoped requests, choose at
|
|
420
|
-
most one best campaign per target sender unless evidence shows one sender
|
|
421
|
-
needs multiple same-sender campaigns because the first is blocked or already
|
|
422
|
-
loaded.
|
|
423
|
-
|
|
424
|
-
Do not create side campaigns, warm-post-engager side campaigns, on-demand campaigns,
|
|
425
|
-
or unrelated campaigns as the default answer to a fill request.
|
|
426
|
-
|
|
427
|
-
For each selected campaign, decide the next action by the smallest sufficient
|
|
428
|
-
step:
|
|
429
|
-
|
|
430
|
-
- **Enrich/prep more rows in the same campaign** when sender health is good,
|
|
431
|
-
recent sends point to this campaign, and `sourceAvailable` can cover the
|
|
432
|
-
ready-to-schedule gap.
|
|
433
|
-
- **Add/import more rows to the same campaign/source path** when the campaign is
|
|
434
|
-
the right recent-send lane but source inventory or viable rows cannot cover
|
|
435
|
-
the gap.
|
|
436
|
-
- **Use a different existing campaign** only when the recent-send/best-result
|
|
437
|
-
campaign for that sender is blocked, archived, missing sequence/source/senders,
|
|
438
|
-
disconnected, exhausted with no viable same-campaign replenishment path,
|
|
439
|
-
already loaded while the sender still has a gap, or the user explicitly asks
|
|
440
|
-
to move to another campaign.
|
|
441
|
-
- **Ask what to create** only after resolver/state evidence shows there is no
|
|
442
|
-
suitable existing campaign for the requested sender set.
|
|
443
|
-
|
|
444
|
-
When the best regular campaign is source-exhausted, recommend same-campaign source-ladder replenishment.
|
|
445
|
-
Keep `selectedLeadListId` source identity distinct
|
|
446
|
-
from the campaign `workflowTableId`.
|
|
447
|
-
|
|
448
|
-
Source replenishment choices:
|
|
449
|
-
|
|
450
|
-
- existing source list reuse: append/confirm the existing source list when it
|
|
451
|
-
has enough eligible rows;
|
|
452
|
-
- CSV path: load CSV profiles/domains, then import into the same campaign/source
|
|
453
|
-
path;
|
|
454
|
-
- Sales Nav path: use Sales Nav lookup/search, then import selected prospects;
|
|
455
|
-
- Prospeo path: use account/person search as appropriate, then import selected
|
|
456
|
-
prospects;
|
|
457
|
-
- Signal Discovery path: search/select posts or engagers, import selected
|
|
458
|
-
source prospects into the same campaign/source-list path, then confirm
|
|
459
|
-
readiness.
|
|
460
|
-
|
|
461
|
-
When reusing an existing source list on an already-large campaign, handle the
|
|
462
|
-
campaign-table row cap automatically in `--yolo` mode. If `confirm_lead_list`
|
|
463
|
-
fails with `USER_ADDED_ROWS_LIMIT_EXCEEDED`, parse `remainingRows`,
|
|
464
|
-
`currentRows`, `requestedRows`, and `maxRows`; do not stop just because the full
|
|
465
|
-
source list is too large. Instead, create a bounded same-source split:
|
|
466
|
-
|
|
467
|
-
1. choose a split size no larger than `remainingRows`, the two-day refill row
|
|
468
|
-
budget, and the first-pass processing cap unless the packet named a higher
|
|
469
|
-
cap;
|
|
470
|
-
2. read compact rows from the current `selectedLeadListId` with
|
|
471
|
-
`get_rows_minimal`;
|
|
472
|
-
3. create a temporary bounded LinkedIn profile source list with
|
|
473
|
-
`load_csv_linkedin_leads`, preserving useful source columns such as post URL,
|
|
474
|
-
post author, engagement type/text, keyword, confidence, and why;
|
|
475
|
-
4. call `confirm_lead_list` with that bounded source-list id, the same campaign
|
|
476
|
-
id, and `currentStep:"running"` for ACTIVE campaigns;
|
|
477
|
-
5. verify the campaign table row count and review batch with
|
|
478
|
-
`get_campaign_table_schema` before any prep/approval.
|
|
479
|
-
|
|
480
|
-
Do not use `add_on_demand_leads` or create an on-demand table as the automatic
|
|
481
|
-
regular-campaign fallback. The fallback must stay same-campaign and
|
|
482
|
-
source-list-backed.
|
|
483
|
-
Short form: row-cap fallback means bounded same-source split, not stop or on-demand.
|
|
484
|
-
|
|
485
|
-
### Running Campaign Signal Discovery Recovery
|
|
486
|
-
|
|
487
|
-
For an already-running regular campaign that needs same-campaign Signal
|
|
488
|
-
Discovery replenishment, treat the watch/current-step transition as a guarded
|
|
489
|
-
repair, not as normal create-campaign navigation.
|
|
490
|
-
|
|
491
|
-
Do not call `search_signals` with `currentStep:"signal-discovery"` directly from `currentStep:"running"`. The campaign step guard intentionally rejects that
|
|
492
|
-
backwards transition. After exact approval and a fresh refill-state reread, clear `currentStep` only if it still exactly matches `running`:
|
|
493
|
-
|
|
494
|
-
```text
|
|
495
|
-
update_campaign({ campaignId, currentStep:null, clearCurrentStepIfMatches:"running" })
|
|
496
|
-
```
|
|
497
|
-
|
|
498
|
-
Then register provider preflight and run a campaign-scoped `search_signals` so
|
|
499
|
-
post rows exist in the campaign's Signal Discovery state:
|
|
52
|
+
This prompt is operator choreography around `core/contract.v2.json` and
|
|
53
|
+
`core/flow.v1.json`. The typed planner and durable run are the executable
|
|
54
|
+
authority. Do not reconstruct allocation, preparation calibration, circuit
|
|
55
|
+
backoff, scheduler selection, or completion mathematics in prose.
|
|
56
|
+
|
|
57
|
+
Default mode is read-only research for both a regular campaign and an evergreen
|
|
58
|
+
campaign. Mutation begins only through the typed approval or bounded yolo gates
|
|
59
|
+
below.
|
|
60
|
+
|
|
61
|
+
Call `get_refill_target_plan` first. Use projected coverage (`sent + scheduled`)
|
|
62
|
+
as the completion measure, and preserve `targetShapeRevision` across every
|
|
63
|
+
approved action. The workflow does not lower paid-InMail thresholds. Any
|
|
64
|
+
threshold change remains a separate operator gate.
|
|
65
|
+
|
|
66
|
+
## Read-only routing compatibility
|
|
67
|
+
|
|
68
|
+
Preserve the established discovery order without reimplementing its decisions:
|
|
69
|
+
|
|
70
|
+
1. Call `resolve_campaign_fill_route`; stop on `unsupported_campaign_type`.
|
|
71
|
+
2. Call `get_campaign_refill_state` and bind `freshness.stateHash`. After any
|
|
72
|
+
primitive, reread `get_campaign_refill_state` before deciding again.
|
|
73
|
+
3. Treat `fill_campaign_horizon` as a legacy evergreen-only lower-level primitive.
|
|
74
|
+
Reach it only through the durable contract when the current source is exhausted,
|
|
75
|
+
and preserve all scan/import caps.
|
|
76
|
+
|
|
77
|
+
## Contract load
|
|
78
|
+
|
|
79
|
+
Load both assets completely to `hasMore:false`. Require
|
|
80
|
+
`workflow:"refill-sends-workflow"`, a supported v1 flow, contract major 2, and
|
|
81
|
+
matching `contractVersion`, `cacheVersion`, `contractHash`, semantic ID sets,
|
|
82
|
+
and backend projection hash. Any mismatch is a terminal pre-execution blocker.
|
|
83
|
+
|
|
84
|
+
The public command is `refill_sends`. `refill_sends_v2` remains a
|
|
85
|
+
server-internal implementation detail and is never advertised or invoked by a
|
|
86
|
+
customer prompt.
|
|
87
|
+
|
|
88
|
+
## Durable choreography
|
|
89
|
+
|
|
90
|
+
1. Resolve and echo the explicit workspace and sender-local date scope.
|
|
91
|
+
2. Read the canonical target packet. Render its normal bounded approval packet
|
|
92
|
+
even when yolo auto-accepts it.
|
|
93
|
+
3. Preserve immutable workspace, selectors, date, timezone, contract/cache,
|
|
94
|
+
expected-target hash, placement cap, approval fingerprint, and installed
|
|
95
|
+
identities. Display labels never define scope.
|
|
96
|
+
4. Execute exactly one current packet action through the public durable entry.
|
|
97
|
+
The run records planned -> did -> outcome, reconciles uncertain outcomes,
|
|
98
|
+
rereads authoritative target truth, and replans before another primitive.
|
|
99
|
+
5. Treat expected scheduler targets as diagnostic receipts only. Placement
|
|
100
|
+
remains workspace-wide and every expected target must be `included` or
|
|
101
|
+
`omitted` with a typed reason.
|
|
102
|
+
6. Stop exact preparation only after a fresh canonical deficit read proves the
|
|
103
|
+
target satisfied. Repeated zero-yield work follows the contract's closed,
|
|
104
|
+
open, and single-fenced half-open circuit receipts.
|
|
105
|
+
|
|
106
|
+
Use the packet's campaign and sender names first. Preserve the six coverage
|
|
107
|
+
labels: `Need to prepare`, `Goal`, `Already sent`, `Scheduled`,
|
|
108
|
+
`Ready and waiting to be scheduled`, and `Still need`.
|
|
109
|
+
|
|
110
|
+
## Approval and safety
|
|
111
|
+
|
|
112
|
+
Interactive mode posts the complete packet in normal chat, then asks one compact
|
|
113
|
+
host-native question with exactly `Accept` and `Decline`. Codex uses
|
|
114
|
+
`request_user_input`; Claude Code uses `AskUserQuestion`. Decline means no
|
|
115
|
+
mutation. A plain chat yes is not approval. Yolo is explicit bounded
|
|
116
|
+
auto-accept of the rendered packet only.
|
|
117
|
+
|
|
118
|
+
Preserve workspace access, approval, source-family, threshold, scheduler,
|
|
119
|
+
campaign-lifecycle, send-time, and no-direct-send gates. Never direct-send,
|
|
120
|
+
raw-write `scheduledFor`/`scheduledAt`, create/archive/delete campaigns, lower
|
|
121
|
+
paid InMail thresholds, mutate sender configuration, or broaden source family.
|
|
122
|
+
Only a packet-named paused campaign start or scheduler sweep may use the
|
|
123
|
+
existing product-gated path. A scheduler sweep is customer-visible and may
|
|
124
|
+
place unrelated eligible workspace cells within the approved global cap.
|
|
125
|
+
|
|
126
|
+
Proof is Sellable MCP evidence only: target plan, refill state, scheduler
|
|
127
|
+
capacity, complete target receipts, sender-dimensional changed counts, and
|
|
128
|
+
durable run events. Never use cell IDs, Prisma, SQL, direct database access, or
|
|
129
|
+
production scripts as completion proof.
|
|
130
|
+
|
|
131
|
+
## Terminal report
|
|
132
|
+
|
|
133
|
+
Render `projected_full` only from canonical projected coverage. Otherwise name
|
|
134
|
+
the target-specific `concrete_blocker`, `deadline_reached`, `iteration_limit`,
|
|
135
|
+
or `operator_stopped` receipt. `ready_buffer_exists_is_not_complete`; ready rows
|
|
136
|
+
and `awaiting_scheduler_after_ready_buffer` are progress, not terminal success.
|
|
137
|
+
|
|
138
|
+
<!-- REFILL_CONTRACT_GENERATED:START -->
|
|
139
|
+
## Generated refill contract metadata
|
|
140
|
+
|
|
141
|
+
contractVersion: 2.0.0
|
|
142
|
+
cacheVersion: refill-contract-v1
|
|
143
|
+
contractHash: e54ba0f80bc534fd70fd8c10c1caab109fe9a91c205cfc423f5f6735cd53fdc1
|
|
500
144
|
|
|
501
145
|
```text
|
|
502
|
-
|
|
503
|
-
|
|
146
|
+
scope: workspace_id, target_date, sender_selectors, campaign_selectors, approval_mode, contract_identity, installed_identity
|
|
147
|
+
gates: approval_packet, one_primitive, fresh_reread, normalized_accounting, prep_circuit
|
|
148
|
+
release identities (independent of semantic hash): mcpPackageVersion, installPackageVersion, codexPluginVersion
|
|
149
|
+
forbidden: direct_send, raw_scheduler_write, campaign_create, campaign_archive_delete, threshold_lowering, unselected_source_mutation, unapproved_campaign_start, sender_config_write
|
|
150
|
+
terminal: projected_full, concrete_blocker, deadline_reached, iteration_limit, operator_stopped, capped_by_scheduler, loaded_awaiting_scheduler, lanes_exhausted, not_an_evergreen_workspace, no_refillable_campaigns
|
|
151
|
+
ready_buffer_exists_is_not_complete
|
|
504
152
|
```
|
|
505
|
-
|
|
506
|
-
Use `select_promising_posts` before importing. For a fresh source refill, prefer
|
|
507
|
-
`selectionMode:"replace"` and `scrapePlanMode:"all-selected"`, then call
|
|
508
|
-
`import_leads` without `targetEngagerCount` or `maxPostsToScrape` so the scrape
|
|
509
|
-
uses the selected posts' max available engagers within backend provider caps.
|
|
510
|
-
Do not invent, lower, or pass a people-to-check target for refill imports unless
|
|
511
|
-
Christian explicitly supplied that target or post cap in the current request or
|
|
512
|
-
approval packet. If Christian did supply an explicit `targetEngagerCount` or
|
|
513
|
-
`maxPostsToScrape`, use `scrapePlanMode:"capacity-target"` and respect the
|
|
514
|
-
under-capacity refusal: ask for approval to expand the selected post set or
|
|
515
|
-
switch source instead of scraping a target-mismatched set.
|
|
516
|
-
|
|
517
|
-
If `import_leads` returns `reusedExistingSourceList` but the user explicitly
|
|
518
|
-
approved a different selected-post scrape, retry `import_leads` with the existing `sourceLeadListId` from that response or from refill state. For Signal
|
|
519
|
-
Discovery this bypasses the stale reuse guard and materializes the newly
|
|
520
|
-
selected campaign-scoped posts into a fresh source list.
|
|
521
|
-
|
|
522
|
-
After `wait_for_lead_list_ready`, call `confirm_lead_list` with the new
|
|
523
|
-
source-list id and same campaign id. Pass `currentStep:"running"` to `confirm_lead_list` for an ACTIVE already-running campaign so the watched state
|
|
524
|
-
returns to Running after same-campaign source rows are copied. Do not call
|
|
525
|
-
`update_campaign` to fake this after the copy; `confirm_lead_list` owns the
|
|
526
|
-
post-copy state transition.
|
|
527
|
-
|
|
528
|
-
Before any source mutation, show exact approval evidence: workspace id,
|
|
529
|
-
campaign name, campaign id, workflow table id, source/provider id, source-list
|
|
530
|
-
id when known, import caps, target dates/caps, dedupe/DNC/provider exclusion expectations, and expected row side effects. Present the campaign name and plain-English reason first; include ids only as proof and execution targets.
|
|
531
|
-
Then reread `get_campaign_refill_state`; if `freshness.stateHash` or exact ids
|
|
532
|
-
changed, stop and ask for a fresh approval.
|
|
533
|
-
|
|
534
|
-
After source import, call `wait_for_lead_list_ready` and `confirm_lead_list`
|
|
535
|
-
with the source-list id and campaign id. Imports append to the existing
|
|
536
|
-
campaign/source path; they must not reset campaign state, sequence, sender
|
|
537
|
-
assignment, current step, or watch state. If provider auth, rate limit, timeout,
|
|
538
|
-
partial import, duplicate-only import, DNC-only result, CSV parse failure,
|
|
539
|
-
readiness timeout, or `confirm_lead_list` failure occurs, stop before prep and
|
|
540
|
-
report the blocker/retry path.
|
|
541
|
-
|
|
542
|
-
## Prep, Approval, And Scheduler Proof
|
|
543
|
-
|
|
544
|
-
Existing rows normally move through enrichment/prep/approval using
|
|
545
|
-
`start_campaign_message_preparation` and
|
|
546
|
-
`get_campaign_message_preparation_status`. Standalone Generate Message is not the normal refill action;
|
|
547
|
-
it is only for a manual/user-authored message path
|
|
548
|
-
that requires it.
|
|
549
|
-
|
|
550
|
-
Approval mode:
|
|
551
|
-
|
|
552
|
-
- default is `approvalMode:"mark_ready"`, not approve;
|
|
553
|
-
- use `approvalMode:"approve"` only when the user explicitly asks to approve
|
|
554
|
-
messages or schedule sends and has approved the exact bounded cohort;
|
|
555
|
-
- `start_campaign` is allowed only as a bounded start/resume step for a
|
|
556
|
-
selected `PAUSED`, dashboard-active, campaign-backed sequence refill target;
|
|
557
|
-
- never launch/start unrelated, archived, completed, draft, direct, or
|
|
558
|
-
non-selected campaigns from refill.
|
|
559
|
-
|
|
560
|
-
Paused campaign start discipline:
|
|
561
|
-
|
|
562
|
-
- `PAUSED`, dashboard-active campaign-backed sequence campaigns are existing
|
|
563
|
-
refill targets, not archived inventory.
|
|
564
|
-
- If the route or active-campaign refetch marks a target as start-eligible, read
|
|
565
|
-
`get_campaign_refill_state` for that campaign/table before deciding whether to
|
|
566
|
-
start, prep, or skip it.
|
|
567
|
-
- If the selected refill target is `PAUSED` and has healthy senders, sequence,
|
|
568
|
-
source/list, and ready/preparable rows or a bounded source/prep packet, the
|
|
569
|
-
final packet may include `start_campaign` when starting is needed for the
|
|
570
|
-
scheduler to schedule/send approved eligible rows.
|
|
571
|
-
- In non-yolo mode, the `Accept` choice must say that starting the paused
|
|
572
|
-
campaign can let the product scheduler schedule/send approved eligible
|
|
573
|
-
sequence actions.
|
|
574
|
-
- In `--yolo`, `start_campaign` is auto-accepted only for the exact selected
|
|
575
|
-
paused campaign ids, sender scope, caps, approval mode, and start side effect
|
|
576
|
-
named in the packet after the final reread.
|
|
577
|
-
- Do not start if the campaign is archived, completed, draft, direct, lacks a
|
|
578
|
-
sequence/senders/source, has no viable row path, or state drifts.
|
|
579
|
-
|
|
580
|
-
Before prep or approval mutation, reread `get_campaign_refill_state` and compare
|
|
581
|
-
`freshness.stateHash`, campaign id, table id, workspace id, source id, and caps.
|
|
582
|
-
If they changed, stop. A second concurrent refill run must see the first run's
|
|
583
|
-
import/prep state on reread and avoid duplicate work.
|
|
584
|
-
|
|
585
|
-
After the final reread and before the first mutating refill primitive, render
|
|
586
|
-
one final approval packet in normal chat, in campaign-name-first language, so
|
|
587
|
-
Codex/Claude can display the packet as Markdown. Then ask a host-native
|
|
588
|
-
structured question with exactly two choices: `Accept` and `Decline`. The
|
|
589
|
-
question is the execution boundary, but the full packet belongs in chat, not in
|
|
590
|
-
the structured question body.
|
|
591
|
-
Short form: exactly two choices: `Accept` and `Decline`.
|
|
592
|
-
|
|
593
|
-
- In Codex interactive sessions, use `request_user_input`.
|
|
594
|
-
- In Claude Code interactive sessions, use `AskUserQuestion`.
|
|
595
|
-
- The normal chat message immediately before the structured question must
|
|
596
|
-
include this exact operator output packet shape:
|
|
597
|
-
- workspace name/id and mode (`--yolo` or approval-required);
|
|
598
|
-
- sender scope, including selected sender names and skipped sender names with
|
|
599
|
-
health/enrollment reasons;
|
|
600
|
-
- a campaign-by-campaign plan table with campaign name, sender names, action,
|
|
601
|
-
target count/cap, source/list, and blocker/skip reason;
|
|
602
|
-
- exact workspace id, campaign id, workflow table id, source/action ids when
|
|
603
|
-
relevant, freshness/state hash, caps/dates, approval mode, and the next
|
|
604
|
-
MCP primitive to call;
|
|
605
|
-
- expected side effects, including any exact-sender
|
|
606
|
-
`refresh_paid_inmail_credits` credit-cache writes, forbidden actions, and the
|
|
607
|
-
stop/rollback condition.
|
|
608
|
-
- The structured question body must be compact and refer back to the posted
|
|
609
|
-
chat packet. Do not duplicate the campaign table or full operator packet
|
|
610
|
-
inside `request_user_input` / `AskUserQuestion`; refer back to the posted
|
|
611
|
-
packet instead of duplicating it.
|
|
612
|
-
- The `Accept` choice description must name that it accepts only the posted
|
|
613
|
-
packet, with its exact workspace, campaign/table/source/action ids,
|
|
614
|
-
caps/dates, approval mode, side effects, and stop condition.
|
|
615
|
-
- The `Decline` choice must stop with no mutation.
|
|
616
|
-
- Do not proceed from a plain chat "ok", "yes", "go", or "do it" when the
|
|
617
|
-
structured question tool is available. If the structured question tool is not
|
|
618
|
-
available in an interactive session, stop and tell the user the quick question
|
|
619
|
-
panel is required for refill approvals. In non-interactive exec/automation,
|
|
620
|
-
print the approval packet and stop before mutation.
|
|
621
|
-
|
|
622
|
-
`--yolo` mode is explicit auto-accept. If Christian includes `--yolo` in the
|
|
623
|
-
same refill request, do not ask the `Accept` / `Decline` question. Instead:
|
|
624
|
-
|
|
625
|
-
1. render the same final approval packet;
|
|
626
|
-
2. reread `get_campaign_refill_state` and, when the packet includes paid-InMail
|
|
627
|
-
credit refreshes, verify the current `get_refill_target_plan` action
|
|
628
|
-
candidates still name the same sender ids, campaign ids, table ids, and
|
|
629
|
-
column ids;
|
|
630
|
-
3. verify the route, workspace id, campaign id, table id, source/action ids,
|
|
631
|
-
caps/dates, approval mode, blockers, and side-effect class still match the
|
|
632
|
-
packet;
|
|
633
|
-
4. execute only the exact packet;
|
|
634
|
-
5. after each terminal credit-refresh/apply/prep/source-copy/bounded-approval/read-only wait result, rerun
|
|
635
|
-
`get_refill_target_plan`, reread state, settle processing when needed,
|
|
636
|
-
recompute the target-window saturation ledger, and continue with the next smallest
|
|
637
|
-
safe action inside the same bounded packet until every selected sender is
|
|
638
|
-
saturated by projected coverage or blocked by non-scheduler evidence. Do not
|
|
639
|
-
stop at "loaded" or "awaiting scheduler"; when no in-packet MCP mutation
|
|
640
|
-
remains and ready rows cover the gap, keep the run open in read-only
|
|
641
|
-
scheduler polling until the sender is projected-full or Christian stops it.
|
|
642
|
-
|
|
643
|
-
In `--yolo`, do not stop after the first selected campaign if the request was
|
|
644
|
-
sender-scoped and more selected senders or same-packet start-eligible campaigns
|
|
645
|
-
remain. If the first candidate is duplicate-only, already loaded, awaiting
|
|
646
|
-
scheduler, source-exhausted, or otherwise blocked, continue to the next
|
|
647
|
-
same-sender candidate that was already in the rendered packet and still matches
|
|
648
|
-
the fresh route/refill-state evidence. If no in-packet safe action remains, stop
|
|
649
|
-
before mutation and return continuation options instead of guessing.
|
|
650
|
-
|
|
651
|
-
Continuation options must be campaign-name-first and concrete. For each blocked
|
|
652
|
-
sender/campaign, list the next viable options with exact ids and the approval
|
|
653
|
-
boundary, for example: start an already selected start-eligible paused campaign,
|
|
654
|
-
repair stale waterfall bindings, replenish the current source, expand Signal
|
|
655
|
-
Discovery post selection, use the next same-sender campaign, or run evergreen
|
|
656
|
-
setup/repair. Mark which options are covered by the current `--yolo` packet and
|
|
657
|
-
which require a new approval packet.
|
|
658
|
-
Short form: in `--yolo`, go as far as the packet safely allows, then give exact continuation options.
|
|
659
|
-
|
|
660
|
-
`--yolo` authorizes `start_campaign` only for selected `PAUSED` refill targets
|
|
661
|
-
named in the exact packet. It never authorizes starting unrelated, archived,
|
|
662
|
-
completed, draft, or direct campaigns, separate launch/send actions,
|
|
663
|
-
archive/delete cleanup, raw scheduler writes, sender reassignment, paid-InMail
|
|
664
|
-
credit refreshes for senders not named in the packet, extra senders beyond the
|
|
665
|
-
eligible sender set, or higher caps than the packet named. In
|
|
666
|
-
sender-scoped `--yolo`, a bounded packet containing all eligible enrolled
|
|
667
|
-
senders and at most one best campaign per sender is covered; arbitrary expanded
|
|
668
|
-
campaign sets are not. If any state or scope drifts, stop and re-plan instead of
|
|
669
|
-
auto-accepting the changed action.
|
|
670
|
-
Short form: all eligible enrolled senders are covered by sender-scoped `--yolo`; arbitrary expanded campaign sets are not.
|
|
671
|
-
Short form: direct scheduler writes are not covered by `--yolo`.
|
|
672
|
-
Short form: stop and re-plan instead of auto-accepting the changed action.
|
|
673
|
-
|
|
674
|
-
Before choosing a refill prep primitive, inspect the table frontier from
|
|
675
|
-
`get_campaign_refill_state.preparationFrontier`. If
|
|
676
|
-
`hasLaterPreparedIsland:true` or `earliestUnpreparedRow` exists before later
|
|
677
|
-
successful enrichment, treat that as a top-down gap: use
|
|
678
|
-
`select_campaign_cells`/`queue_campaign_cells` with
|
|
679
|
-
`rowSelector:{ type:"needsEnrichment" }` and `columnRole:"enrich"` in bounded
|
|
680
|
-
row-position order, or use `start_campaign_message_preparation` with the
|
|
681
|
-
adaptive defaults for the full enrich -> rubric -> Generate Message loop. Do not
|
|
682
|
-
use the UI Jump anchor as the automation cursor; Jump is a navigation affordance
|
|
683
|
-
and can be fooled by a later enriched island.
|
|
684
|
-
|
|
685
|
-
After same-campaign source copy, inspect the actual campaign-table position of
|
|
686
|
-
the copied rows before starting prep. Newly copied rows often land after the
|
|
687
|
-
first 100 table rows. Use `get_campaign_table_schema` to read the `reviewBatch`
|
|
688
|
-
and `select_campaign_cells` diagnostics against
|
|
689
|
-
`rowSelector:{ type:"reviewBatch" }` only to understand the copied row set. Do
|
|
690
|
-
not operate on `reviewBatch` before earlier `needsEnrichment` rows unless the
|
|
691
|
-
approved packet explicitly says to prioritize the just-copied bounded split and
|
|
692
|
-
the earlier rows are exhausted, dependency-blocked, or intentionally excluded.
|
|
693
|
-
|
|
694
|
-
For bounded split refills, prefer top-down `needsEnrichment` or adaptive
|
|
695
|
-
`start_campaign_message_preparation` first. If the bounded packet explicitly
|
|
696
|
-
uses `approvalMode:"approve"` because the user asked to fill/schedule sends or
|
|
697
|
-
provided `--yolo` for a two-day send fill, inspect approval candidates with
|
|
698
|
-
`rowSelector:{ type:"needsApproval" }` after current generated messages exist,
|
|
699
|
-
then approve only the bounded cohort covered by the packet. Do not broad approve
|
|
700
|
-
existing table rows.
|
|
701
|
-
|
|
702
|
-
If enrich cells are pending or Generate Message cells are dependency-blocked,
|
|
703
|
-
omit `maxRowsToCheck` so the adaptive prep job can cover the bounded table scan,
|
|
704
|
-
or set a bounded cap high enough to include the necessary row-position frontier
|
|
705
|
-
when row count evidence requires it. Do not run a fixed `maxRowsToCheck:100`
|
|
706
|
-
prep pass after appending rows to a larger existing campaign table; it can spend
|
|
707
|
-
the whole budget on older rows and miss the refill batch.
|
|
708
|
-
|
|
709
|
-
Bound `targetPreparedMessages` to the actual ready-to-schedule gap for the
|
|
710
|
-
selected campaign and sender set. In sender-scoped mode, compute and report the
|
|
711
|
-
gap per sender. If ready/scheduled rows across active enrolled campaigns already
|
|
712
|
-
cover a sender's gap, do not start prep for that sender; report that the sender
|
|
713
|
-
is already loaded and awaiting the scheduler. If source inventory is available
|
|
714
|
-
and the gap is small, prepare only the sender gap instead of filling every active
|
|
715
|
-
campaign.
|
|
716
|
-
Short form: prepare only the per-sender gap instead of filling every active campaign.
|
|
717
|
-
|
|
718
|
-
Scheduled success requires a post-action reread proving scheduler-owned cells
|
|
719
|
-
with non-null `scheduledFor`. Prepared, approved, and ready-to-schedule rows are
|
|
720
|
-
intermediate states; report them as awaiting scheduler unless scheduled cells
|
|
721
|
-
are present.
|
|
722
|
-
|
|
723
|
-
preparedMessages can remain 0 while scheduler-ready state changes downstream.
|
|
724
|
-
After a prep job reaches a terminal state, run `wait_for_campaign_processing`
|
|
725
|
-
when generated/pass counts are still settling, then poll `get_campaign_refill_state` until `readyToSchedule` drops, scheduled counts increase, or the state clearly
|
|
726
|
-
remains awaiting scheduler. Do not force scheduler fields directly.
|
|
727
|
-
|
|
728
|
-
For requested fill/refill completion, continue the saturation loop after each
|
|
729
|
-
poll: recompute projected target-window coverage by sender and date, subtract it
|
|
730
|
-
from the requested target capacity, and decide whether another safe prep/import/
|
|
731
|
-
approval pass is required. If projected coverage is still short and ready buffer
|
|
732
|
-
is also short, run the next bounded MCP primitive that can create more eligible
|
|
733
|
-
rows: `fill_campaign_horizon` for the selected evergreen target, or
|
|
734
|
-
`start_campaign_message_preparation`, source import/replenishment, and bounded
|
|
735
|
-
approval for the selected regular target. If projected coverage is short but
|
|
736
|
-
ready buffer covers the gap, keep polling the scheduler until it fills, a
|
|
737
|
-
concrete non-scheduler blocker appears, or Christian explicitly stops/statuses
|
|
738
|
-
the run. Never mark the refill complete or blocked while a daily limit/readback
|
|
739
|
-
equivalent would still show the selected send day below capacity and the only
|
|
740
|
-
remaining state is scheduler pickup.
|
|
741
|
-
|
|
742
|
-
## Evergreen Campaign Discipline
|
|
743
|
-
|
|
744
|
-
`fill_campaign_horizon` is a legacy evergreen-only lower-level primitive. Call it
|
|
745
|
-
only after `resolve_campaign_fill_route` and `get_campaign_refill_state` prove an
|
|
746
|
-
evergreen campaign target.
|
|
747
|
-
|
|
748
|
-
For evergreen campaigns, do not advance down the source ladder while the current source can produce enough good prospects.
|
|
749
|
-
The current source is exhausted, or
|
|
750
|
-
unable to produce enough good prospects, only when evidence includes source id,
|
|
751
|
-
target prospect gap, cursor/inventory state, dedupe/DNC/provider exclusions,
|
|
752
|
-
good-prospect yield, and scan/import caps. If that proof is missing, continue
|
|
753
|
-
the current source or ask for more research instead of walking the ladder.
|
|
754
|
-
|
|
755
|
-
## Hard Safety Rules
|
|
756
|
-
|
|
757
|
-
- Never call launch/start tools except `start_campaign` for an exact selected
|
|
758
|
-
`PAUSED`, dashboard-active, campaign-backed sequence refill target in the
|
|
759
|
-
rendered packet.
|
|
760
|
-
- Never raw-write `scheduledFor`, `scheduledAt`, scheduled status, or sender
|
|
761
|
-
assignment fields.
|
|
762
|
-
- Never expose internal direct campaign types as an operator refill path.
|
|
763
|
-
- Never claim scheduled success without rereading scheduler-owned cells with
|
|
764
|
-
non-null `scheduledFor`.
|
|
765
|
-
- Never call `refresh_paid_inmail_credits` except for exact sender ids named in
|
|
766
|
-
the rendered refill packet; after refresh, rerun `get_refill_target_plan` and
|
|
767
|
-
stop if the fresh status is still missing, stale, below threshold, or drifts to
|
|
768
|
-
a different campaign/table/column.
|
|
769
|
-
- No mutation in production unless Christian explicitly approves exact
|
|
770
|
-
workspace, campaign/table ids, source/action ids, caps/dates, approval mode,
|
|
771
|
-
expected side effects, and rollback/stop condition.
|
|
772
|
-
- In interactive Codex or Claude Code, that explicit approval must be collected
|
|
773
|
-
through the host-native `Accept` / `Decline` question gate.
|
|
774
|
-
- `--yolo` in the current refill request is explicit auto-accept for the
|
|
775
|
-
rendered bounded refill packet only; it does not loosen the unrelated-campaign
|
|
776
|
-
start/send, scheduler-write, archive/delete, or drift rules above.
|
|
777
|
-
|
|
778
|
-
## Report
|
|
779
|
-
|
|
780
|
-
Report in campaign-name-first language:
|
|
781
|
-
|
|
782
|
-
- selected campaign name(s), target type, and sender names;
|
|
783
|
-
- why they were selected, including last scheduled send date/future scheduled
|
|
784
|
-
send evidence and sender overlap;
|
|
785
|
-
- whether the smallest next step is enrich/prep more in that campaign, add rows
|
|
786
|
-
to that campaign/source, use a different existing campaign, or ask what to
|
|
787
|
-
create;
|
|
788
|
-
- source state, blockers, exact next primitive, approval required, and the
|
|
789
|
-
freshness hash used.
|
|
790
|
-
- for `--yolo`, how far the run continued automatically, which selected senders
|
|
791
|
-
are loaded/scheduled/awaiting scheduler, and the exact continuation options for
|
|
792
|
-
any sender/campaign that remains blocked.
|
|
793
|
-
|
|
794
|
-
Keep ids visible but secondary: show them under "Exact target" or "Proof", not
|
|
795
|
-
as the main decision text. Say explicitly whether a selected paused campaign was
|
|
796
|
-
started; otherwise say nothing was started and nothing was sent.
|
|
153
|
+
<!-- REFILL_CONTRACT_GENERATED:END -->
|