@sellable/mcp 0.1.451 → 0.1.453

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/server.js CHANGED
@@ -10,6 +10,7 @@ import { resolveCampaignFillRoute } from "./tools/campaign-fill-routing.js";
10
10
  import { fillCampaignHorizon } from "./tools/campaign-horizon-fill.js";
11
11
  import { cancelPrepareCampaignMessages, getPrepareCampaignMessagesStatus, startPrepareCampaignMessages, } from "./tools/campaign-message-preparation.js";
12
12
  import { getCampaignRefillState } from "./tools/campaign-refill-state.js";
13
+ import { getRefillTargetPlan } from "./tools/refill-target-plan.js";
13
14
  import { getCampaignTableSchema, queueCampaignCells, recordCampaignReviewBatch, reviseMessageTemplateAndRerun, selectCampaignCells, waitForCampaignProcessing, } from "./tools/campaign-processing.js";
14
15
  import { archiveCampaign, createCampaign, duplicateCampaign, getCampaign, getCampaignMessagesPreview, getCampaigns, pauseCampaign, startCampaign, updateCampaign, updateCampaignBrief, } from "./tools/campaigns.js";
15
16
  import { queueCells, updateCell } from "./tools/cells.js";
@@ -221,6 +222,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
221
222
  case "get_campaign_refill_state":
222
223
  result = await getCampaignRefillState(args);
223
224
  break;
225
+ case "get_refill_target_plan":
226
+ result = await getRefillTargetPlan(args);
227
+ break;
224
228
  case "refill_sends":
225
229
  result = refillSendsCommand(args);
226
230
  break;
@@ -142,6 +142,15 @@ export function refillSendsCommand(input = {}) {
142
142
  },
143
143
  firstOperationalSteps: [
144
144
  'Load get_subskill_prompt({ subskillName: "refill-sends-workflow" }) before any product operation.',
145
+ `Call get_refill_target_plan({ intent: "${intent}"${input.campaignId ? `, campaignId: "${input.campaignId}"` : ""}${input.tableId ? `, tableId: "${input.tableId}"` : ""}${senderIds.length > 0
146
+ ? `, senderIds: ${JSON.stringify(senderIds)}`
147
+ : ""}${senderNames.length > 0
148
+ ? `, senderNames: ${JSON.stringify(senderNames)}`
149
+ : ""}${senders.length > 0 ? `, senders: ${JSON.stringify(senders)}` : ""}${untilDate
150
+ ? `, untilDate: "${untilDate}"`
151
+ : `, horizonSendDays: ${horizonSendDays}`}, approvalMode: "${approvalMode}" }) before any import, prep, approval, start, or schedule-affecting action.`,
152
+ "If get_refill_target_plan returns status complete, report gross target, selected days, scheduled count, campaign ids, targetShapeRevision, and no-op proof without asking for approval.",
153
+ "If remainingReadyOrScheduledGap is 0 but remainingScheduledGap is positive, run only a bounded read-only scheduler settle/reread loop; do not ask for prep/import/approval.",
145
154
  `Resolve route with resolve_campaign_fill_route({ intent: "${intent}"${input.campaignId ? `, campaignId: "${input.campaignId}"` : ""}${input.tableId ? `, tableId: "${input.tableId}"` : ""} }).`,
146
155
  'If the plain route shows stale managed waterfall evidence, archived/completed shared slots, or targets that do not cover the selected sender set, immediately call resolve_campaign_fill_route({ intent: "active" }) and inspect current dashboard-active ACTIVE/PAUSED campaign-backed sequence campaigns before declaring a sender blocked.',
147
156
  "Call list_senders and get_sender_routing, then resolve sender selectors against active enrolled campaign-backed sequence senders.",
@@ -151,15 +160,15 @@ export function refillSendsCommand(input = {}) {
151
160
  "Read get_campaign_refill_state for enough exact candidate campaigns to pick the best per-sender target by recent/future scheduler-owned sends, then recent result evidence, then source health.",
152
161
  "Treat current dashboard-active PAUSED campaign-backed sequence campaigns as start-eligible candidates: read refill state before deciding whether to prep, approve, start, or skip.",
153
162
  "Fresh reread get_campaign_refill_state immediately before any import, prep, approval, or horizon-fill mutation.",
154
- "Maintain a horizon saturation ledger per selected sender: selected send days, gross capacity, future scheduler-owned scheduled counts, ready-to-schedule buffer, remaining scheduled gap, remaining ready-or-scheduled gap, and next MCP primitive.",
155
- "In --yolo, continue through every safe selected sender/campaign action covered by the rendered packet; when no safe in-packet action remains, return concrete continuation options instead of silently stopping.",
163
+ "Maintain a target-led horizon saturation ledger per selected sender from get_refill_target_plan: selected days, gross capacity, future scheduler-owned scheduled counts, ready-to-schedule buffer, remaining scheduled gap, remaining ready-or-scheduled gap, targetShapeRevision, stateRevision, and next MCP primitive.",
164
+ "In --yolo or after one Accept, continue through every safe selected sender/campaign action covered by the rendered target packet; scheduled-count progress changes stateRevision and should continue while targetShapeRevision is stable.",
156
165
  "Do not complete a fill/schedule request until a final get_campaign_refill_state reread proves scheduler-owned scheduledFor cells fill the horizon, or report a concrete blocker such as awaiting_scheduler_after_ready_buffer.",
157
166
  ],
158
167
  approvalContract: yolo
159
- ? "Auto-accept only the rendered bounded refill packet after fresh reread. Continue through every safe apply/prep/start action inside that packet, including start_campaign only for exact selected PAUSED campaign-backed sequence refill targets named in the packet. After each action, recompute the per-sender horizon saturation ledger and keep going until scheduler-owned scheduledFor cells fill the requested horizon or a concrete blocker/timeout is proven. Ready-to-schedule rows are buffer, not completion. If no safe in-packet action remains, return concrete continuation options and stop. Stop immediately if sender set, route, ids, caps, dates, blockers, or side-effect class drifts."
168
+ ? "Auto-accept only the rendered bounded refill target packet after get_refill_target_plan and fresh reread. Continue through every safe apply/prep/start action inside that packet, including start_campaign only for exact selected PAUSED campaign-backed sequence refill targets named in the packet. After each action, rerun get_refill_target_plan; keep going while targetShapeRevision is stable and scheduled counts progress toward the target, even though stateRevision changes. Ready-to-schedule rows are buffer, not completion. If ready buffer covers the gap, use only read-only scheduler settle/reread. Stop immediately if targetShapeRevision changes because sender set, route, ids, caps, dates, blockers, action class, or side-effect class drifts."
160
169
  : hasSenderSelectors
161
- ? "Ask the final Accept/Decline structured approval question before mutation, including workspace, sender scope, campaign table, exact ids, caps/dates, side effects, forbidden actions, and stop condition."
162
- : "Ask which eligible enrolled senders to refill first, then ask the final Accept/Decline structured approval question before mutation.",
170
+ ? "Ask the final Accept/Decline structured approval question before mutation only when get_refill_target_plan reports a positive remaining ready-or-scheduled gap. Include workspace, sender scope, campaign table, exact ids, caps/dates, gross target, scheduled count, ready buffer, remaining scheduled gap, targetShapeRevision/stateRevision, side effects, forbidden actions, and stop condition. If target is complete, no-op without approval. If ready buffer covers the target, run only read-only scheduler settle/reread."
171
+ : "Ask which eligible enrolled senders to refill first, then ask the final Accept/Decline structured approval question before mutation only when get_refill_target_plan reports a positive remaining ready-or-scheduled gap.",
163
172
  forbiddenActions: [
164
173
  "start_campaign outside an exact selected PAUSED campaign-backed refill packet",
165
174
  "manual launch/send or direct sending outside product scheduler side effects",
@@ -0,0 +1,85 @@
1
+ type RefillTargetPlanIntent = "plain" | "active" | "evergreen";
2
+ type RefillTargetPlanApprovalMode = "approve" | "mark_ready";
3
+ type GetRefillTargetPlanInput = {
4
+ intent?: RefillTargetPlanIntent;
5
+ horizonSendDays?: number;
6
+ untilDate?: string;
7
+ campaignId?: string;
8
+ tableId?: string;
9
+ senderIds?: string[];
10
+ senderNames?: string[];
11
+ senders?: string[];
12
+ actionTypes?: string[];
13
+ approvalMode?: RefillTargetPlanApprovalMode;
14
+ };
15
+ export declare const refillTargetPlanToolDefinitions: {
16
+ name: string;
17
+ description: string;
18
+ inputSchema: {
19
+ type: string;
20
+ properties: {
21
+ intent: {
22
+ type: string;
23
+ enum: string[];
24
+ description: string;
25
+ };
26
+ horizonSendDays: {
27
+ type: string;
28
+ minimum: number;
29
+ maximum: number;
30
+ description: string;
31
+ };
32
+ untilDate: {
33
+ type: string;
34
+ pattern: string;
35
+ description: string;
36
+ };
37
+ campaignId: {
38
+ type: string;
39
+ description: string;
40
+ };
41
+ tableId: {
42
+ type: string;
43
+ description: string;
44
+ };
45
+ senderIds: {
46
+ type: string;
47
+ items: {
48
+ type: string;
49
+ };
50
+ description: string;
51
+ };
52
+ senderNames: {
53
+ type: string;
54
+ items: {
55
+ type: string;
56
+ };
57
+ description: string;
58
+ };
59
+ senders: {
60
+ type: string;
61
+ items: {
62
+ type: string;
63
+ };
64
+ description: string;
65
+ };
66
+ actionTypes: {
67
+ type: string;
68
+ items: {
69
+ type: string;
70
+ enum: string[];
71
+ };
72
+ description: string;
73
+ };
74
+ approvalMode: {
75
+ type: string;
76
+ enum: string[];
77
+ description: string;
78
+ };
79
+ };
80
+ required: never[];
81
+ additionalProperties: boolean;
82
+ };
83
+ }[];
84
+ export declare function getRefillTargetPlan(input?: GetRefillTargetPlanInput): Promise<unknown>;
85
+ export {};
@@ -0,0 +1,90 @@
1
+ import { getApi } from "../api.js";
2
+ async function postRefillTargetPlan(body) {
3
+ const api = getApi();
4
+ return api.post("/api/v3/mcp/refill-target-plan", body);
5
+ }
6
+ export const refillTargetPlanToolDefinitions = [
7
+ {
8
+ name: "get_refill_target_plan",
9
+ description: "read-only refill target planner to call before any refill mutation. It computes selected sender-local days, gross scheduled-send target, scheduler-owned scheduled coverage across active enrolled campaigns, ready buffer, remaining scheduled gap, bounded action candidates, and targetRevision drift proof. This tool does not create rows, import leads, prepare messages, approve messages, does not schedule sends, start campaigns, launch, spend InMail credits, or write scheduler fields. Complete targets no-op without approval; incomplete targets must be followed by a bounded approval packet.",
10
+ inputSchema: {
11
+ type: "object",
12
+ properties: {
13
+ intent: {
14
+ type: "string",
15
+ enum: ["plain", "active", "evergreen"],
16
+ description: 'Route intent. Use "plain" for normal refill requests unless the user explicitly narrows active or evergreen.',
17
+ },
18
+ horizonSendDays: {
19
+ type: "number",
20
+ minimum: 1,
21
+ maximum: 7,
22
+ description: "Calendar-day refill horizon to inspect from the sender-local current day. Defaults to 2 when untilDate is not provided; no-send days contribute zero target.",
23
+ },
24
+ untilDate: {
25
+ type: "string",
26
+ pattern: "^\\d{4}-\\d{2}-\\d{2}$",
27
+ description: "Optional sender-local YYYY-MM-DD date to inspect through, inclusive.",
28
+ },
29
+ campaignId: {
30
+ type: "string",
31
+ description: "Optional exact CampaignOffer.id to narrow routing. Do not pass campaign names.",
32
+ },
33
+ tableId: {
34
+ type: "string",
35
+ description: "Optional exact WorkflowTable.id to narrow routing. Do not pass table names.",
36
+ },
37
+ senderIds: {
38
+ type: "array",
39
+ items: { type: "string" },
40
+ description: "Optional exact Sender.id values to scope target planning.",
41
+ },
42
+ senderNames: {
43
+ type: "array",
44
+ items: { type: "string" },
45
+ description: "Optional sender display names to resolve before target planning.",
46
+ },
47
+ senders: {
48
+ type: "array",
49
+ items: { type: "string" },
50
+ description: "Optional sender selectors as names or ids.",
51
+ },
52
+ actionTypes: {
53
+ type: "array",
54
+ items: {
55
+ type: "string",
56
+ enum: [
57
+ "send_invite",
58
+ "send_dm",
59
+ "send_inmail_open",
60
+ "send_inmail_closed",
61
+ "react_and_comment",
62
+ ],
63
+ },
64
+ description: "Optional workflow send column/action types. Defaults from selected active campaign sequences.",
65
+ },
66
+ approvalMode: {
67
+ type: "string",
68
+ enum: ["approve", "mark_ready"],
69
+ description: 'Requested downstream approval mode. Use "approve" only for explicit fill/schedule requests.',
70
+ },
71
+ },
72
+ required: [],
73
+ additionalProperties: false,
74
+ },
75
+ },
76
+ ];
77
+ export function getRefillTargetPlan(input = {}) {
78
+ return postRefillTargetPlan({
79
+ intent: input.intent,
80
+ horizonSendDays: input.horizonSendDays,
81
+ untilDate: input.untilDate,
82
+ campaignId: input.campaignId,
83
+ tableId: input.tableId,
84
+ senderIds: input.senderIds,
85
+ senderNames: input.senderNames,
86
+ senders: input.senders,
87
+ actionTypes: input.actionTypes,
88
+ approvalMode: input.approvalMode,
89
+ });
90
+ }
@@ -33,6 +33,7 @@ import { onDemandToolDefinitions } from "./one-off.js";
33
33
  import { processingToolDefinitions } from "./processing.js";
34
34
  import { promptToolDefinitions } from "./prompts.js";
35
35
  import { readinessToolDefinitions } from "./readiness.js";
36
+ import { refillTargetPlanToolDefinitions } from "./refill-target-plan.js";
36
37
  import { refillSendsToolDefinitions } from "./refill-sends.js";
37
38
  import { rowToolDefinitions } from "./rows.js";
38
39
  import { rubricToolDefinitions } from "./rubrics.js";
@@ -50,6 +51,7 @@ export const allTools = [
50
51
  ...campaignAbTestToolDefinitions,
51
52
  ...campaignFillRoutingToolDefinitions,
52
53
  ...campaignRefillStateToolDefinitions,
54
+ ...refillTargetPlanToolDefinitions,
53
55
  ...refillSendsToolDefinitions,
54
56
  ...setupEvergreenCampaignsToolDefinitions,
55
57
  ...campaignHorizonFillToolDefinitions,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sellable/mcp",
3
- "version": "0.1.451",
3
+ "version": "0.1.453",
4
4
  "type": "module",
5
5
  "description": "Sellable MCP server for Claude Code and Codex campaign workflows",
6
6
  "main": "dist/index.js",
@@ -49,6 +49,7 @@ allowed-tools:
49
49
  - mcp__sellable__wait_for_campaign_processing
50
50
  - mcp__sellable__resolve_campaign_fill_route
51
51
  - mcp__sellable__get_campaign_refill_state
52
+ - mcp__sellable__get_refill_target_plan
52
53
  - mcp__sellable__fill_campaign_horizon
53
54
  - mcp__sellable__start_campaign_message_preparation
54
55
  - mcp__sellable__get_campaign_message_preparation_status
@@ -474,6 +474,19 @@ Transfer rules:
474
474
  context, and the current offer category. The question should be easy to answer,
475
475
  closed-ended, and specific to that prospect. Do not reuse the same generic
476
476
  question across every row.
477
+ - Good `{{custom_closed_question}}` examples:
478
+ - `Are you guys trying to make LinkedIn outbound a real founder-led channel, or is it still mostly ad hoc?`
479
+ - `Are you using LinkedIn to reach sales leaders directly, or is most pipeline still coming from intros and email?`
480
+ - `Are you actively testing outbound for growth teams right now, or is LinkedIn still more of a founder-network channel?`
481
+ - `Are you trying to turn customer feedback into account-risk signals, or is that still mostly manual check-ins?`
482
+ - `Are you looking at support conversations as a source of churn risk, or does that still mostly live in tickets and surveys?`
483
+ - `Are you building a repeatable sales motion yet, or is most of it still founder-led experimentation?`
484
+ - `Are you treating LinkedIn as a sales channel this year, or mostly as brand and founder content?`
485
+ - Bad `{{custom_closed_question}}` examples:
486
+ - `Are you trying to avoid manual research + follow-up process?`
487
+ - `Are you interested in seeing Sellable?`
488
+ - `Open to seeing how this would work?`
489
+ - `Do you need more leads, filters, messages, and sequence setup?`
477
490
  - The product/category wording in the question should be plain and immediately
478
491
  legible, for example `LinkedIn outbound`, `customer success`, `CX operations`,
479
492
  `support`, or `post-sales`. Avoid feature-heavy phrasing such as `manual
@@ -4,6 +4,7 @@ description: Plan regular campaign and evergreen campaign send refill work throu
4
4
  visibility: public
5
5
  allowed-tools:
6
6
  - mcp__sellable__refill_sends
7
+ - mcp__sellable__get_refill_target_plan
7
8
  - mcp__sellable__get_auth_status
8
9
  - mcp__sellable__start_cli_login
9
10
  - mcp__sellable__wait_for_cli_login
@@ -102,11 +103,23 @@ get_subskill_prompt({ subskillName: "refill-sends-workflow" })
102
103
  ```
103
104
 
104
105
  Then follow that workflow exactly. The default path is read-only research:
105
- resolve the route, identify the sender-relevant campaign that most recently had
106
- scheduler-owned sends, read refill state for that target, report the next safe
107
- step using campaign names first, and stop before mutation unless the user has
108
- explicitly approved the exact workspace, campaign/table/source ids, caps/dates,
109
- approval mode, expected side effects, and stop/rollback condition.
106
+ compute the refill target plan first, resolve the route, identify the
107
+ sender-relevant campaign that most recently had scheduler-owned sends, read
108
+ refill state for that target, report the next safe step using campaign names
109
+ first, and stop before mutation unless the user has explicitly approved the
110
+ exact workspace, campaign/table/source ids, caps/dates, approval mode, expected
111
+ side effects, and stop/rollback condition.
112
+
113
+ Immediately after loading the internal workflow, call
114
+ `get_refill_target_plan`. The target plan is the canonical first fact receipt:
115
+ selected sender-local days, gross scheduled-send target, scheduler-owned
116
+ scheduled coverage across active enrolled campaigns, ready buffer, remaining
117
+ scheduled gap, remaining ready-or-scheduled gap, `targetShapeRevision`, and
118
+ `stateRevision`. If the target plan is complete, report that the selected target
119
+ is already filled and no-op without asking for approval. If the ready buffer
120
+ covers the target but scheduled coverage is still short, run only a bounded
121
+ read-only scheduler settle/reread loop and report
122
+ `awaiting_scheduler_after_ready_buffer` if it does not settle.
110
123
 
111
124
  If the plain route's managed waterfall targets are stale, for example skipped
112
125
  targets show archived/completed shared slots or the returned targets do not cover
@@ -147,7 +160,10 @@ packet. It does not authorize starting unrelated, archived, completed, draft, or
147
160
  direct campaigns, separate launch/send actions, archive or delete cleanup,
148
161
  direct scheduler writes, sender reassignment, or campaigns outside those
149
162
  selected for the eligible sender set. Stop and re-plan if the route, sender set,
150
- ids, caps, blockers, or side-effect class drift before mutation.
163
+ ids, caps, blockers, action class, or side-effect class drift before mutation.
164
+ Scheduled counts increasing toward the approved target are expected progress:
165
+ they change `stateRevision`, not `targetShapeRevision`, and do not require a
166
+ second approval.
151
167
 
152
168
  In `--yolo`, continue as far as the rendered packet safely allows. After each
153
169
  apply/prep/start result, reread state, settle processing when needed, and move to
@@ -162,7 +178,8 @@ saturation, not merely prepared/approved/ready rows. Maintain a horizon
162
178
  saturation ledger per selected sender: selected send days, gross capacity,
163
179
  future scheduler-owned scheduled cells with non-null `scheduledFor`,
164
180
  ready-to-schedule buffer, remaining scheduled gap, remaining ready-or-scheduled
165
- gap, and the next MCP primitive that can reduce the gap. After every
181
+ gap, `targetShapeRevision`, `stateRevision`, and the next MCP primitive that can
182
+ reduce the gap. After every
166
183
  apply/prep/start result, wait for processing, reread refill state, recompute the
167
184
  ledger, then keep applying safe bounded actions until the scheduled count fills
168
185
  the horizon or a concrete blocker/timeout is proven. If ready rows cover the
@@ -3,6 +3,7 @@ name: refill-sends-workflow
3
3
  description: Internal skill-led refill sends workflow for regular campaigns and evergreen campaigns.
4
4
  visibility: internal
5
5
  allowed-tools:
6
+ - mcp__sellable__get_refill_target_plan
6
7
  - mcp__sellable__list_senders
7
8
  - mcp__sellable__get_sender_routing
8
9
  - mcp__sellable__get_campaign_waterfall
@@ -58,7 +59,18 @@ senders", and "load everyone up". A sender-scoped request targets senders
58
59
  enrolled in active campaign-backed sequence campaigns, not one arbitrary active
59
60
  campaign.
60
61
 
61
- 1. Call `resolve_campaign_fill_route` first. Use `intent:"plain"` for generic
62
+ 1. Call `get_refill_target_plan` first. This read-only target plan is the
63
+ canonical opening receipt: selected sender-local days, gross scheduled-send
64
+ target, scheduler-owned scheduled coverage across active enrolled campaigns,
65
+ ready buffer, remaining scheduled gap, remaining ready-or-scheduled gap,
66
+ bounded action candidates, `targetShapeRevision`, and `stateRevision`.
67
+ If `status:"complete"`, report the target, selected dates, scheduled count,
68
+ campaign ids, and no-op proof without asking for approval or mutating.
69
+ If `remainingReadyOrScheduledGap:0` but `remainingScheduledGap>0`, run only a
70
+ bounded read-only scheduler settle/reread loop; do not ask for
71
+ prep/import/approval. If the scheduler still does not fill, report
72
+ `awaiting_scheduler_after_ready_buffer`.
73
+ 2. Call `resolve_campaign_fill_route`. Use `intent:"plain"` for generic
62
74
  fill/load language, `intent:"active"` only when the user explicitly narrowed
63
75
  to active regular campaigns or when the plain route has stale managed
64
76
  waterfall evidence, and `intent:"evergreen"` only when the user explicitly
@@ -70,7 +82,7 @@ campaign.
70
82
  and inspect current dashboard-active `ACTIVE` and `PAUSED` campaign-backed
71
83
  sequence campaigns before declaring a sender blocked.
72
84
  Short form: stale waterfall bindings do not overrule current dashboard-active campaigns.
73
- 2. Resolve sender context before choosing a target. Call `list_senders`; when a
85
+ 3. Resolve sender context before choosing a target. Call `list_senders`; when a
74
86
  managed program is involved, also call `get_campaign_waterfall`, and call
75
87
  `get_sender_routing` when territory rules may constrain sender eligibility.
76
88
  If the user named senders, map those names to exact sender ids. If the user
@@ -83,7 +95,7 @@ campaign.
83
95
  the relevant senders from the most recent scheduler-owned sends in
84
96
  refill-state readbacks and report that inference.
85
97
  Short form: `--yolo refill senders` means all eligible enrolled senders; non-yolo asks which enrolled senders.
86
- 3. Read `get_campaign_refill_state` for candidate campaigns from the resolver
98
+ 4. Read `get_campaign_refill_state` for candidate campaigns from the resolver
87
99
  result and from the required active-route refetch when managed waterfall
88
100
  evidence looks stale. For a broad `active_campaigns` route, inspect enough
89
101
  candidates to identify the campaign or small campaign set that most recently
@@ -103,9 +115,9 @@ campaign.
103
115
  already has enough ready/scheduled rows across enrolled campaigns or that a
104
116
  different same-sender campaign is needed because the best lane is blocked.
105
117
  Short form: `--yolo refill senders` selects all eligible enrolled senders, then the best campaign per sender.
106
- 4. Pick exact ids from the selected refill-state response. Do not use campaign
118
+ 5. Pick exact ids from the selected refill-state response. Do not use campaign
107
119
  names or table names as target identifiers for tool calls.
108
- 5. Classify the target from refill state:
120
+ 6. Classify the target from refill state:
109
121
  - regular campaign: campaign-backed workflow table with normal source,
110
122
  sequence, senders, and send/refill stages.
111
123
  - evergreen campaign: managed always-on lane with current source evidence.
@@ -126,8 +138,8 @@ Use the refill-state response as the current facts receipt:
126
138
  - horizon saturation ledger per target sender: sender-local send days in scope,
127
139
  gross horizon capacity, future scheduler-owned scheduled counts in that
128
140
  window, ready-to-schedule buffer, remaining scheduled gap, remaining
129
- ready-or-scheduled gap, and the next smallest MCP primitive that can reduce
130
- the gap;
141
+ ready-or-scheduled gap, `targetShapeRevision`, `stateRevision`, and the next
142
+ smallest MCP primitive that can reduce the gap;
131
143
  - row/stage counts: source available, rows, enriched, passed, generated,
132
144
  approved, ready to schedule, scheduled;
133
145
  - active message prep job;
@@ -182,15 +194,20 @@ two-day packet.
182
194
  Short form: default `--yolo refill senders` target is each eligible sender's two-send-day gap.
183
195
 
184
196
  For `--yolo` fill/schedule requests, do not treat "ready" as completion. Build
185
- and maintain a horizon saturation ledger for every selected sender. A sender is
186
- complete only when a final `get_campaign_refill_state` reread proves
187
- scheduler-owned scheduled cells with non-null `scheduledFor` meet or exceed the
188
- sender's horizon capacity for the selected send days. Ready-to-schedule rows are
189
- only buffer for the product scheduler. If ready plus scheduled covers the
190
- horizon but scheduled cells do not yet, run a bounded scheduler settle loop:
191
- wait, reread, recompute the ledger, and continue until scheduled coverage is
192
- proved or the loop reaches an explicit timeout/blocker. Timeout is
193
- `awaiting_scheduler_after_ready_buffer`, not success.
197
+ and maintain a horizon saturation ledger for every selected sender from
198
+ `get_refill_target_plan`. A sender is complete only when a final target-plan or
199
+ refill-state reread proves scheduler-owned scheduled cells with non-null
200
+ `scheduledFor` meet or exceed the sender's horizon capacity for the selected
201
+ send days. Put another way: complete only when a final
202
+ `get_campaign_refill_state` reread proves the scheduler-owned non-null
203
+ `scheduledFor` coverage. Ready-to-schedule rows are only buffer for the product scheduler. If
204
+ ready plus scheduled covers the horizon but scheduled cells do not yet, run a
205
+ bounded read-only scheduler settle loop: wait, reread, recompute the ledger, and
206
+ continue until scheduled coverage is proved or the loop reaches an explicit
207
+ timeout/blocker. Timeout is `awaiting_scheduler_after_ready_buffer`, not
208
+ success, and it does not need a prep/import/approval packet. Short form: ready
209
+ buffer is not success.
210
+ Short form: use a bounded scheduler settle loop before calling it blocked.
194
211
  Short form: `--yolo refill senders` completes only on scheduled horizon saturation or a concrete blocker.
195
212
 
196
213
  Rank candidates in this order: