@sellable/install 0.1.388 → 0.1.390-uat.refill-zero-shot.20260721230009

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.
@@ -3,13 +3,13 @@ FROM ${HERMES_IMAGE}
3
3
 
4
4
  USER root
5
5
 
6
- ARG INSTALLER_PACKAGE=@sellable/install@0.1.388
7
- ARG MCP_PACKAGE=@sellable/mcp@0.1.643
6
+ ARG INSTALLER_PACKAGE=@sellable/install@0.1.389
7
+ ARG MCP_PACKAGE=@sellable/mcp@0.1.644
8
8
  ARG INSTALLER_INTEGRITY
9
9
  ARG MCP_INTEGRITY=sha512-b7N9CnZXBoO1JPc6YeM3S96FnsJQI8BjMtb1Vp0moKkSy3YQ/jd9VQDwFxUGgC7nXx0BNlvNJH9QocmarC52yA==
10
10
 
11
- RUN test "${INSTALLER_PACKAGE}" = "@sellable/install@0.1.388" \
12
- && test "${MCP_PACKAGE}" = "@sellable/mcp@0.1.643" \
11
+ RUN test "${INSTALLER_PACKAGE}" = "@sellable/install@0.1.389" \
12
+ && test "${MCP_PACKAGE}" = "@sellable/mcp@0.1.644" \
13
13
  && test -n "${INSTALLER_INTEGRITY}" \
14
14
  && test "$(npm view "${INSTALLER_PACKAGE}" dist.integrity)" = "${INSTALLER_INTEGRITY}" \
15
15
  && test "$(npm view "${MCP_PACKAGE}" dist.integrity)" = "${MCP_INTEGRITY}" \
@@ -23,9 +23,9 @@ RUN test "${INSTALLER_PACKAGE}" = "@sellable/install@0.1.388" \
23
23
  && mkdir -p /opt/sellable-agent/install-root /opt/sellable-agent/mcp-root \
24
24
  && npm install --prefix /opt/sellable-agent/install-root --omit=dev --ignore-scripts --no-audit --no-fund "${INSTALLER_PACKAGE}" \
25
25
  && npm install --prefix /opt/sellable-agent/mcp-root --omit=dev --ignore-scripts --no-audit --no-fund "${MCP_PACKAGE}" \
26
- && node --input-type=module -e "import { readFileSync } from 'node:fs'; const install = JSON.parse(readFileSync('/opt/sellable-agent/install-root/node_modules/@sellable/install/package.json')); const mcp = JSON.parse(readFileSync('/opt/sellable-agent/mcp-root/node_modules/@sellable/mcp/package.json')); if (install.name !== '@sellable/install' || install.version !== '0.1.388' || mcp.name !== '@sellable/mcp' || mcp.version !== '0.1.643') throw new Error('package identity rejected');" \
26
+ && node --input-type=module -e "import { readFileSync } from 'node:fs'; const install = JSON.parse(readFileSync('/opt/sellable-agent/install-root/node_modules/@sellable/install/package.json')); const mcp = JSON.parse(readFileSync('/opt/sellable-agent/mcp-root/node_modules/@sellable/mcp/package.json')); if (install.name !== '@sellable/install' || install.version !== '0.1.389' || mcp.name !== '@sellable/mcp' || mcp.version !== '0.1.644') throw new Error('package identity rejected');" \
27
27
  && node --input-type=module -e "import { buildExternalRuntimeClosure } from '/opt/sellable-agent/install-root/node_modules/@sellable/install/lib/sellable-agent/external-runtime-builder.mjs'; const built = buildExternalRuntimeClosure({ mcpPackageRoot: '/opt/sellable-agent/mcp-root/node_modules/@sellable/mcp', mcpNodeModulesRoot: '/opt/sellable-agent/mcp-root/node_modules', hermesSourceRoot: '/opt/hermes', ownerUid: 0, ownerGid: 0 }); if (!built.closureDigest) throw new Error('runtime closure rejected');" \
28
- && node --input-type=module -e "import { chmodSync, writeFileSync } from 'node:fs'; const release = { installerPackage: '@sellable/install@0.1.388', installerIntegrity: process.argv[1], mcpPackage: '@sellable/mcp@0.1.643', mcpIntegrity: process.argv[2] }; writeFileSync('/opt/sellable-agent/release.json', JSON.stringify(release) + '\\n', { mode: 0o444 }); chmodSync('/opt/sellable-agent/release.json', 0o444);" "${INSTALLER_INTEGRITY}" "${MCP_INTEGRITY}" \
28
+ && node --input-type=module -e "import { chmodSync, writeFileSync } from 'node:fs'; const release = { installerPackage: '@sellable/install@0.1.389', installerIntegrity: process.argv[1], mcpPackage: '@sellable/mcp@0.1.644', mcpIntegrity: process.argv[2] }; writeFileSync('/opt/sellable-agent/release.json', JSON.stringify(release) + '\\n', { mode: 0o444 }); chmodSync('/opt/sellable-agent/release.json', 0o444);" "${INSTALLER_INTEGRITY}" "${MCP_INTEGRITY}" \
29
29
  && npm cache clean --force
30
30
 
31
31
  COPY entrypoint.sh /usr/local/bin/sellable-agent-container-entrypoint
@@ -2,12 +2,12 @@
2
2
 
3
3
  This image is a dedicated Agent worker/runtime boundary beside an existing Hermes dashboard. It never replaces or reconfigures the dashboard container.
4
4
 
5
- Build from this directory after `@sellable/install@0.1.388` is published:
5
+ Build from this directory after `@sellable/install@0.1.389` is published:
6
6
 
7
7
  ```sh
8
8
  docker build --platform linux/amd64 \
9
9
  --build-arg INSTALLER_INTEGRITY='sha512-…' \
10
- --tag sellable-agent-host:0.1.388 .
10
+ --tag sellable-agent-host:0.1.389 .
11
11
  ```
12
12
 
13
13
  Create a private `bootstrap/` directory owned by root with mode `0700`. It must contain `bootstrap.json`, `provider-auth`, and `slack-app`, each owned by root with mode `0600`. `bootstrap.json` contains only non-secret identities, hashes, package integrities, and the control-plane URL. The other two files contain the provider JSON and global Slack app token whose hashes are pinned by `bootstrap.json`.
@@ -28,7 +28,7 @@ import {
28
28
 
29
29
  const MANIFEST_VERSION = "sellable-agent-external-runtime-closure/v1";
30
30
  const MCP_PACKAGE = "@sellable/mcp";
31
- const MCP_VERSION = "0.1.643";
31
+ const MCP_VERSION = "0.1.644";
32
32
  const SAFE_COMPONENT = /^[A-Za-z0-9@._+-]+$/;
33
33
  const HERMES_EXCLUDES = new Set([".git", ".playwright", "node_modules"]);
34
34
 
@@ -38,8 +38,8 @@ import { installAgentHostServices } from "./service-installer.mjs";
38
38
  const PACKAGE_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), "../..");
39
39
  const VERSION = "sellable-agent-host-bootstrap/v1";
40
40
  const RECEIPT_VERSION = "sellable-agent-host-registration/v1";
41
- const INSTALLER_PACKAGE = "@sellable/install@0.1.388";
42
- const MCP_PACKAGE = "@sellable/mcp@0.1.643";
41
+ const INSTALLER_PACKAGE = "@sellable/install@0.1.389";
42
+ const MCP_PACKAGE = "@sellable/mcp@0.1.644";
43
43
  const HERMES_VERSION = "0.18.0";
44
44
  const SHA256 = /^[a-f0-9]{64}$/;
45
45
  const SHA256_16 = /^[a-f0-9]{16}$/;
@@ -1058,8 +1058,8 @@ export function compileClaimToProfileDesired(activeClaim, config) {
1058
1058
  ]) ||
1059
1059
  !exactObject(runtimeService, ["principalId", "policyHash", "credentialGeneration", "credentialFingerprint"]) ||
1060
1060
  pinned.hermesCli !== "hermes" || pinned.hermesVersion !== "0.18.0" ||
1061
- pinned.installerPackage !== "@sellable/install@0.1.388" ||
1062
- pinned.mcpPackage !== "@sellable/mcp@0.1.643" ||
1061
+ pinned.installerPackage !== "@sellable/install@0.1.389" ||
1062
+ pinned.mcpPackage !== "@sellable/mcp@0.1.644" ||
1063
1063
  !Array.isArray(policy.toolInclude) || policy.toolInclude.length === 0 ||
1064
1064
  !Number.isSafeInteger(slack.generation) || slack.generation < 1 ||
1065
1065
  !/^A[A-Z0-9]{8,20}$/.test(slack.appId ?? "") ||
@@ -131,8 +131,8 @@ function validateDesired(desired) {
131
131
  !Number.isSafeInteger(desired.slackGeneration) || desired.slackGeneration < 1 ||
132
132
  !Number.isSafeInteger(desired.serviceCredentialGeneration) || desired.serviceCredentialGeneration < 1 ||
133
133
  desired.hermesCli !== "hermes" || desired.hermesVersion !== "0.18.0" ||
134
- desired.installerPackage !== "@sellable/install@0.1.388" ||
135
- desired.mcpPackage !== "@sellable/mcp@0.1.643" ||
134
+ desired.installerPackage !== "@sellable/install@0.1.389" ||
135
+ desired.mcpPackage !== "@sellable/mcp@0.1.644" ||
136
136
  !Array.isArray(desired.toolInclude) || desired.toolInclude.length === 0 ||
137
137
  desired.toolInclude.some((tool) => !/^[a-z][a-z0-9_]{0,127}$/.test(tool)) ||
138
138
  new Set(desired.toolInclude).size !== desired.toolInclude.length ||
@@ -19,8 +19,8 @@ import { fileURLToPath } from "node:url";
19
19
  import { deriveContainedProfileId } from "./profile-materializer.mjs";
20
20
 
21
21
  export const PROVISIONING_ACTION = "PROVISION_HERMES_PROFILE";
22
- export const PINNED_INSTALL_PACKAGE = "@sellable/install@0.1.388";
23
- export const PINNED_MCP_PACKAGE = "@sellable/mcp@0.1.643";
22
+ export const PINNED_INSTALL_PACKAGE = "@sellable/install@0.1.389";
23
+ export const PINNED_MCP_PACKAGE = "@sellable/mcp@0.1.644";
24
24
 
25
25
  export function deriveAgentProfileId(workspaceId, agentId, hostId) {
26
26
  return deriveContainedProfileId(workspaceId, agentId, hostId);
@@ -576,7 +576,7 @@ function inspectProvisionedProfile({
576
576
  !matchesReadback(manifest, request) ||
577
577
  manifest.serviceCredentialReference !== serviceCredentialReference ||
578
578
  manifest.installerPackage !== PINNED_INSTALL_PACKAGE ||
579
- manifest.installerVersion !== "0.1.388" ||
579
+ manifest.installerVersion !== "0.1.389" ||
580
580
  manifest.mcpPackage !== PINNED_MCP_PACKAGE ||
581
581
  manifest.credentialKeyVersion !== credentialKeyVersion ||
582
582
  !/^[a-f0-9]{16}$/.test(manifest.credentialFingerprint) ||
@@ -781,7 +781,7 @@ function successReceipt({
781
781
  subject: `agent-profile:${observed.profileId}`,
782
782
  cli: {
783
783
  installPackage: PINNED_INSTALL_PACKAGE,
784
- installVersion: "0.1.388",
784
+ installVersion: "0.1.389",
785
785
  mcpPackage: PINNED_MCP_PACKAGE,
786
786
  command: "hermes profile bootstrap",
787
787
  },
@@ -821,7 +821,7 @@ function failure(code, stages, extra = {}, request = null) {
821
821
  subject: safeIdentity ? `agent-profile:${safeIdentity}` : "agent-profile:unbound",
822
822
  cli: {
823
823
  installPackage: PINNED_INSTALL_PACKAGE,
824
- installVersion: "0.1.388",
824
+ installVersion: "0.1.389",
825
825
  mcpPackage: PINNED_MCP_PACKAGE,
826
826
  command: "hermes profile bootstrap",
827
827
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sellable/install",
3
- "version": "0.1.388",
3
+ "version": "0.1.390-uat.refill-zero-shot.20260721230009",
4
4
  "type": "module",
5
5
  "description": "One-command installer for Sellable MCP in Claude Code, Codex, and Hermes",
6
6
  "bin": {
@@ -1,676 +1,464 @@
1
1
  ---
2
2
  name: refill-sends
3
- description: Plan regular campaign and evergreen campaign send refill work through the skill-led refill workflow.
3
+ description: Refill projected sends across a workspace or selected senders through the canonical fenced coordinator.
4
4
  visibility: public
5
5
  allowed-tools:
6
6
  - mcp__sellable__refill_sends
7
7
  - mcp__sellable__get_refill_target_plan
8
8
  - mcp__sellable__get_scheduler_fill_capacity
9
- - mcp__sellable__run_scheduler_sweep
10
- - mcp__sellable__refresh_paid_inmail_credits
11
9
  - mcp__sellable__get_subskill_asset
12
10
  - mcp__sellable__get_auth_status
13
11
  - mcp__sellable__start_cli_login
14
12
  - mcp__sellable__wait_for_cli_login
15
13
  - mcp__sellable__get_active_workspace
16
14
  - mcp__sellable__list_workspaces
17
- - mcp__sellable__set_active_workspace
18
15
  - mcp__sellable__get_subskill_prompt
19
16
  - mcp__sellable__search_subskill_prompts
20
17
  - mcp__sellable__list_senders
21
- - mcp__sellable__get_sender_routing
22
- - mcp__sellable__get_campaign_waterfall
23
- - mcp__sellable__resolve_campaign_fill_route
24
- - mcp__sellable__get_campaign_refill_state
25
- - mcp__sellable__fill_campaign_horizon
26
- - mcp__sellable__get_campaign
27
- - mcp__sellable__get_campaign_context
28
- - mcp__sellable__update_campaign
29
- - mcp__sellable__get_provider_prompt
30
- - mcp__sellable__get_campaign_message_preparation_status
31
- - mcp__sellable__start_campaign_message_preparation
32
- - mcp__sellable__start_campaign
33
- - mcp__sellable__cancel_campaign_message_preparation
34
- - mcp__sellable__import_leads
35
- - mcp__sellable__wait_for_lead_list_ready
36
- - mcp__sellable__confirm_lead_list
37
- - mcp__sellable__search_signals
38
- - mcp__sellable__select_promising_posts
39
- - mcp__sellable__fetch_post_engagers
40
- - mcp__sellable__search_sales_nav
41
- - mcp__sellable__lookup_sales_nav_filter
42
- - mcp__sellable__search_prospeo
43
- - mcp__sellable__search_prospeo_companies
44
- - mcp__sellable__confirm_prospeo_company_accounts
45
- - mcp__sellable__load_csv_linkedin_leads
46
- - mcp__sellable__load_csv_domains
47
- - mcp__sellable__list_dnc_entries
48
- - mcp__sellable__load_csv_dnc_entries
49
- - mcp__sellable__get_rows
50
- - mcp__sellable__get_rows_minimal
51
- - mcp__sellable__get_table_rows
52
- - mcp__sellable__get_campaign_table_schema
53
18
  - mcp__sellable__select_campaign_cells
54
- - mcp__sellable__queue_campaign_cells
55
- - mcp__sellable__wait_for_campaign_processing
56
- - mcp__sellable__get_sender
57
19
  ---
58
20
 
59
21
  # Refill Sends
60
22
 
61
- Use this public command wrapper for plain operator requests such as "fill",
62
- "refill sends", "max out sends", "load everyone up", or "fill horizon sends".
23
+ Use this skill for “fill”, “refill sends”, “max out sends”, “load everyone up”,
24
+ or “fill horizon sends”. `refill_sends` is the only execution owner. The host
25
+ resolves the request, invokes the coordinator, and follows its exact
26
+ continuation; it never reconstructs the refill ladder from low-level tools.
63
27
 
64
- Host command names:
28
+ Host commands:
65
29
 
66
30
  - Claude Code: `/sellable:refill-sends`
67
31
  - Codex: `$sellable:refill-sends`
68
32
 
69
- Accepted invocation flags in the same user request:
70
-
71
- - `--yolo`: auto-accept the rendered bounded refill packet after the required
72
- fresh state reread.
73
- - `workspaceId: <id>`: required for scheduled automation and `--yolo`; pass it
74
- through on every refill MCP tool call instead of relying on shared config
75
- state.
76
- - `--sender <name-or-id>`: scope to a specific sender; repeat for multiple
77
- senders.
78
- - `--until <YYYY-MM-DD>`: fill through that sender-local date, inclusive,
79
- instead of the default scheduler-forward 48-hour target window.
80
- - `--target-date <YYYY-MM-DD>`: fill only that sender-local date. This means
81
- prepare rows for scheduler-fillable slots on that date, not every remaining
82
- daily-limit slot and not an inclusive through-date.
83
- - `senderIds: <id>, <id>` or `senderNames: <name>, <name>`: explicit selector
84
- alternatives when the host preserves natural-language arguments better than
85
- shell-style flags.
86
- - `actionTypes: send_invite, send_inmail_closed`: optional lane selector. Omit
87
- `actionTypes` to preserve the target planner's lane inference.
88
- - `untilDate: YYYY-MM-DD`: explicit date selector alternative when the host
89
- preserves natural-language arguments better than shell-style flags.
90
- - `targetDate: YYYY-MM-DD`: exact-date selector alternative when the host
91
- preserves natural-language arguments better than shell-style flags.
92
-
93
- When the host can call typed MCP tools, start with:
33
+ ## Entry and exact scope
34
+
35
+ Accepted request fields are `--yolo`, `workspaceId`, `--sender`/`senderIds`/
36
+ `senderNames`, `actionTypes`, `--until`/`untilDate`, `--target-date`/
37
+ `targetDate`, and compatibility `horizonSendDays`. Omitted dates mean the
38
+ scheduler-forward 48-hour window. `untilDate` is sender-local and inclusive;
39
+ `targetDate` is one sender-local scheduler-fillable date. Skip no-send days and
40
+ never extend beyond the requested date. Finish the full D1 reread before D2.
94
41
 
95
42
  ```text
96
43
  refill_sends({ yolo?: boolean, executionMode?: "manual" | "scheduled" | "yolo", requireWorkspace?: boolean, workspaceId?: string, senders?: string[], senderIds?: string[], senderNames?: string[], actionTypes?: ("send_invite" | "send_inmail_closed")[], horizonSendDays?: number, untilDate?: "YYYY-MM-DD", targetDate?: "YYYY-MM-DD", runHandle?: RefillRunHandleV1, targetConfig?: RefillTargetConfigV1, reportingContext?: RefillReportingContextV2, messageTemplateRevision?: MessageTemplateRevisionV1 })
97
44
  ```
98
45
 
99
- That command helper normalizes arguments and returns the execution contract. In
100
- non-yolo mode it does not mutate. In workspace-wide `--yolo`, the MCP command
101
- selects fresh `target.globalActionQueue[0]`, obtains and validates its exact
102
- campaign/table/sender/lane `targetConfig`, and only then executes one fenced,
103
- checkpointed slice. If it returns `status:"in_progress"`, immediately invoke
104
- its exact `continuation.arguments`. `kind:"active_exact_run"` carries the exact
105
- `targetConfig`, `runId`, and `fence`; `kind:"next_exact_target"` carries the
106
- unchanged workspace envelope and next preflighted `targetConfig` with no stale
107
- run fence. Do this automatically inside the same user command until terminal
108
- workspace projected coverage or a concrete blocker. If an exact run terminalizes
109
- as `loaded_awaiting_scheduler` and the fresh workspace queue reselects the same
110
- campaign/table/sender/lane, the coordinator returns that scheduler-owned
111
- workspace terminal with no continuation instead of opening another identical
112
- fence. Do not ask the user to type `continue` and do not start a second run while
113
- a fence is active.
114
-
115
- Route intent must stay identical from the rendered packet through execution.
116
- Workspace-wide `--yolo` defaults to `intent:"auto"`, so use `intent:"auto"`
117
- for its opening `get_refill_target_plan`, packet rereads, and `refill_sends`
118
- call. Never render a `plain` planner packet and then execute an `auto` yolo
119
- coordinator. If the operator explicitly supplies another supported intent,
120
- preserve that exact intent on every planner, coordinator, and continuation call.
121
-
122
- `targetConfig` and the server-issued `runHandle` are separate values and must
123
- survive each continuation unchanged except for a server-issued takeover fence.
124
- Only a literal `messageTemplateRevision` with `source:"user_approved"`, the
125
- approved `templateMarkdown`, prior/new authority digests, 1–500 exact cohort
126
- row IDs, and request/effect/revision-operation IDs can authorize
127
- `revise_message_template_and_rerun`. No envelope means no revision. A
128
- `forceRerun:true` queue call cannot infer or authorize template mutation.
129
- When the user supplied a complete revision except for the current prior digest,
130
- acquire the exact run first and reread with its unchanged `targetConfig` and
131
- `runHandle`. Use only `reportingContext.source.templateAuthorityDigest` from
132
- that fenced reread as `priorTemplateAuthorityDigest`, then immediately continue
133
- the same run with the complete literal revision—even when the ordinary refill
134
- queue is empty or the sender is otherwise ineligible. An empty ordinary queue
135
- must not suppress an explicit revision. If the fenced reread returns no digest,
136
- stop with `template_authority_missing` or the returned
137
- `templateAuthorityBlocker`; never guess the digest.
138
- After the revision receipt is committed and dispatched, the next mutation must
139
- be the planner-provided revision-derived `approve_messages` action. Preserve
140
- its exact `rowSelector:{type:"rowIds",rowIds:[...]}` cohort,
141
- `approvalMode:"approve"`, compiled readiness authority, and
142
- `reportingContext`, even if the ordinary sender lane is ineligible. Never use
143
- `queue_campaign_cells` to flip Approved cells: that path cannot produce the
144
- authoritative approval/lane-scope receipt. Require the bounded preparation
145
- receipt's `laneScope` before considering the selected first-touch lane
146
- approved. Zero sender capacity remains a scheduler blocker and never authorizes
147
- reconnection, campaign start, or direct send.
148
-
149
- Before that approval mutation, perform an exact authoritative copy audit with
150
- `select_campaign_cells({ columnRole:"generateMessage",
151
- rowSelector:{type:"rowIds",rowIds:[...]},
152
- includeGeneratedMessageReview:true })` over the complete revision cohort in
153
- batches of at most 20. Require every
154
- `authoritativeGeneratedMessageReview.complete:true`, every row status to be
155
- completed-like, the exact user-approved rendered subject/body, the expected
156
- `templateAuthorityDigest`, and `matchesCurrentTemplateAuthority:true`. This
157
- projection is the only MCP copy-review source of truth. Never use formula
158
- Message/Subject columns, campaign message previews, table previews, or CSV
159
- exports to validate revised Generate Message results. If any row is missing,
160
- non-current, or text/digest mismatched, stop before approval and report
161
- `regenerated_copy_integrity_failed`; never regenerate again implicitly or
162
- approve the cohort on counts/revision hashes alone.
163
-
164
- Safe primitives are paid-credit refresh,
165
- existing-row message preparation, generated-message approval, receipt-proven
166
- same-source row copy, one bounded Signal Discovery search derived only from
167
- the campaign's already-approved positive ICP criteria when no reusable source
168
- rows or tab keywords remain, one bounded SAME-provider-source add for a cold
169
- campaign whose eligible/prepared rows are receipt-proven exhausted (Sales Nav
170
- or Prospeo, same saved search — never a source-family switch), the start of a
171
- route-resolver start-eligible PAUSED campaign-backed sequence campaign selected
172
- by the packet, a request-scoped exact-date product scheduler sweep, and
173
- read-only wait rereads. Each source add and campaign start is one bounded
174
- primitive followed by a full replan.
175
- Approved-criteria keywords and the attempt/exhaustion marker are scoped to the
176
- current fenced refill run: archive temporary derived search tabs after the
177
- source import is materialized, keep normal source-list/import receipts, and
178
- retry from the campaign's current approved rubric in every new refill run.
179
- Same-source copy, same-provider-source add, and source
180
- fallback are safe only after receipt-proven exhaustion:
181
- `hasMoreFrontierRows:false`, zero `approvalCandidates`, no `stuckActiveCells`,
182
- and no non-terminal `approvedNotDispatched` work. It does not run unbounded
183
- approval, lower paid-InMail thresholds/floors, switch an existing campaign's
184
- source family, create NEW campaigns, archive/delete, launch, or send directly;
185
- it never raw-writes scheduler fields.
186
- Continue with the workflow below for route selection, state rereads, approval
187
- gating, source import, preparation, and bounded approval.
188
-
189
- The approval scope is the exact `workspaceId`, target date/window, sender ids,
190
- action types, original planner lane chains, and refill side-effect classes.
191
- Fresh planner-selected campaign/table/source ids, readiness authority,
192
- `actionKey`, `targetShapeRevision`, `stateRevision`, and sent/scheduled/ready
193
- counts are per-rung progress inside that envelope. Execute only
194
- `target.globalActionQueue[0]`, then perform a full authoritative reread before
195
- deciding any next action.
196
-
197
- Sequence readiness is a positive authority contract. Before any preparation,
198
- approval, or exact-date sweep, require a validated compiled campaign and a
199
- positive exact target readiness identity from the backend compiler. Preserve
200
- `profileSchemaVersion`, `compilerVersion`, `campaignDigest`,
201
- `actionColumnId`, action, `pathDigest`, `dependsOnColumnIds`,
202
- `runCondition`, the selected target's `capabilities`, `requestId`,
203
- `effectId`, and `dateGuards` unchanged. If any field is missing, unknown,
204
- blocked, or disagrees with the fresh target plan, stop with the typed readiness
205
- repair blocker; prompts never compile, repair, or reinterpret the sequence.
206
-
207
- Always prepare rows according to the persisted sequence. Never infer no-message
208
- behavior from a missing Generate Message column; use only the selected target's
209
- `capabilities`. Do not choose campaign-union behavior: mixed INVITE -> DM
210
- sequences keep distinct target paths, and the exact selected target path decides
211
- whether message, subject, comment, reaction, or row approval is required.
212
- Refuse approval outside the bounded authorized cohort; bounded enrichment and LLM prerequisite calls
213
- are allowed only when authorized by the exact readiness
214
- packet and returned with a receipt. Never call a LinkedIn
215
- outreach/provider-send surface from refill; product scheduling remains the only
216
- send path.
217
-
218
- Always execute one bounded primitive, then perform a full authoritative reread
219
- and continue the same `--yolo` invocation when the fresh action remains inside
220
- the original sender/action/date/lane-chain/side-effect envelope. For an
221
- exact-date sweep, immediately re-attest `dateGuards`: the target date must be
222
- strictly after sender-local today and inside the fill horizon. Completion
223
- requires exact request/effect-attributed campaign, table, sender,
224
- `actionColumnId`, action, and date scheduler-owned scheduled readback.
225
- Prepared, approved, and ready rows remain intermediate evidence.
226
-
227
- ## Workspace Contract
228
-
229
- First resolve the target workspace id from the user's request, automation config,
230
- or install-time workspace mapping. Pass `workspaceId` on every scheduled or
231
- `--yolo` refill tool call, including setup/read calls such as
232
- `refill_sends`, `get_refill_target_plan`, `list_senders`,
233
- `get_sender_routing`, `resolve_campaign_fill_route`,
234
- `get_campaign_refill_state`, `get_scheduler_fill_capacity`,
235
- `run_scheduler_sweep`, and any later refill mutation covered by the packet.
236
- Missing `workspaceId` in scheduled or `--yolo` mode is a blocker; stop with
237
- `WORKSPACE_REQUIRED` instead of running against an implicit or guessed
238
- workspace.
239
-
240
- Do not solve scheduled or `--yolo` workspace uncertainty by changing the shared
241
- active workspace. Manual interactive workspace switching remains a separate
242
- diagnostic/setup flow, outside automation.
243
-
244
- Examples:
46
+ ```mermaid
47
+ flowchart TD
48
+ A["Parse the operator request"] --> AUTH{"Sellable auth valid?"}
49
+ AUTH -- No --> LOGIN["Run the returned login flow and wait"]
50
+ LOGIN --> AUTH
51
+ AUTH -- Yes --> WS["Resolve exact workspace by ID, exact name, or install mapping"]
52
+ WS --> WSG{"One accessible workspace match?"}
53
+ WSG -- No --> WB["Output: blocked_retryable / WORKSPACE_REQUIRED or workspace_ambiguous"]
54
+ WSG -- Yes --> SENDERS["Resolve each sender inside that workspace by ID, exact name, or unambiguous prefix"]
55
+ SENDERS --> SG{"Every selector resolves exactly?"}
56
+ SG -- No --> SB["Output: blocked_retryable / sender_selector_unresolved with candidates"]
57
+ SG -- Yes --> DATE["Normalize 48h, exact targetDate, or inclusive untilDate in sender-local time"]
58
+ DATE --> PLAN["Call refill_sends with the exact workspace and scope"]
59
+ ```
60
+
61
+ Scheduled and `--yolo` execution require an explicit `workspaceId` on every
62
+ call. Never change the shared active workspace to control an automation, never
63
+ fall back to its full fleet, and never mutate the workspace that merely happens
64
+ to be active. If the native tool is unavailable, use only the host’s exact
65
+ configured Sellable MCP transport; otherwise stop with `sellable_mcp_unavailable`
66
+ instead of manually reproducing the workflow.
67
+
68
+ Before product work, load the closed workflow asset completely and verify it:
245
69
 
246
70
  ```text
247
- refill_sends({ yolo:true, executionMode:"yolo", requireWorkspace:true, workspaceId:"cmlq1v8ms0000jx04ang4hi7e" })
248
- get_refill_target_plan({ intent:"auto", approvalMode:"approve", workspaceId:"cmlq1v8ms0000jx04ang4hi7e" })
249
- list_senders({ workspaceId:"cmlq1v8ms0000jx04ang4hi7e" })
71
+ get_subskill_prompt({ subskillName: "refill-sends-workflow" })
72
+ get_subskill_asset({ subskillName: "refill-sends-workflow", assetPath: "core/flow.v1.json" })
250
73
  ```
251
74
 
252
- Clover scheduled automation must name the Clover workspace explicitly:
75
+ Continue chunks until `hasMore:false`; require
76
+ `workflow:"refill-sends-workflow"` and a `v1` version. Route intent is immutable:
77
+ workspace-wide `--yolo` uses `intent:"auto"` from first plan through terminal.
78
+
79
+ ## Coordinator lifecycle
80
+
81
+ ```mermaid
82
+ flowchart TD
83
+ A["Fresh workspace refill plan"] --> FULL{"Projected coverage sent + scheduled is full?"}
84
+ FULL -- Yes --> COMPLETE["Terminal: complete"]
85
+ FULL -- No --> ACTIVE{"Active exact fenced run exists?"}
86
+ ACTIVE -- Yes --> RESUME["Resume only issued runId, fence, targetConfig, reportingContext"]
87
+ ACTIVE -- No --> PICK["Select globalActionQueue 0"]
88
+ PICK --> PREFLIGHT["Exact campaign, table, sender, action, lane preflight"]
89
+ PREFLIGHT --> ACT["Execute one bounded planner action"]
90
+ RESUME --> ACT
91
+ ACT --> OUTCOME{"Canonical outcome?"}
92
+ OUTCOME -- "Committed or canonical no-op" --> A
93
+ OUTCOME -- "Still running" --> WAIT["Bounded read-only wait or receipt reconciliation"]
94
+ WAIT --> A
95
+ OUTCOME -- "Source exhausted" --> NEXT{"Another planner-ranked existing target?"}
96
+ NEXT -- Yes --> HANDOFF["Terminalize exact fence as next_exact_target"]
97
+ HANDOFF --> A
98
+ NEXT -- No --> NC["Terminal: new_campaign_required; ask whether to create it"]
99
+ OUTCOME -- "Provider unavailable after one retry" --> RETRY["Terminal: blocked_retryable; universeExhausted false"]
100
+ OUTCOME -- "Scope, config, readiness, or receipt drift" --> BLOCK["Terminal: blocked_retryable with exact blocker"]
101
+ OUTCOME -- "Planner marker done" --> DONE["Terminalize marker; never dispatch it"]
102
+ ```
253
103
 
254
- ```text
255
- refill_sends({ yolo:false, executionMode:"scheduled", requireWorkspace:true, workspaceId:"cmlq1v8ms0000jx04ang4hi7e" })
256
- get_refill_target_plan({ intent:"plain", approvalMode:"mark_ready", workspaceId:"cmlq1v8ms0000jx04ang4hi7e" })
104
+ If `status:"in_progress"`, invoke only the returned
105
+ `continuation.arguments`. `targetConfig` and `runHandle` are separate and remain
106
+ unchanged except for a server-issued takeover fence. `active_exact_run` resumes
107
+ that fence; `next_exact_target` carries no stale run fence. Never open a second
108
+ or overlapping run, ask the user to type “continue”, redispatch a control marker, or
109
+ turn `done`, `next_campaign`, or a continuation object into a product action.
110
+
111
+ ## Per-sender campaign and lane waterfall
112
+
113
+ Use managed evergreen order when healthy. Otherwise build the same waterfall
114
+ on the fly from existing eligible campaigns the sender is already attached to.
115
+ This makes regular and evergreen campaigns differ only in where ordering comes
116
+ from, not in how refill proceeds.
117
+
118
+ ```mermaid
119
+ flowchart TD
120
+ A["All existing campaign-backed sequences attached to one sender"] --> FILTER["Exclude wrong workspace/sender, archived, completed, direct, disconnected, or sequence-invalid targets"]
121
+ FILTER --> STALE{"Managed waterfall complete and live?"}
122
+ STALE -- Yes --> MANAGED["Use configured priority order"]
123
+ STALE -- No --> DERIVE["Derive deterministic order from dashboard-active campaigns"]
124
+ DERIVE --> P1["Prefer canonical source/lane priority"]
125
+ P1 --> P2["Then future scheduled and ready inventory"]
126
+ P2 --> P3["Then recent successful sends and source health"]
127
+ P3 --> P4["Then active before exact start-eligible paused, recency, stable ID"]
128
+ MANAGED --> PIN["Pin exact per-sender and per-action lane chain for this run"]
129
+ P4 --> PIN
130
+ PIN --> LANE["Select highest-priority non-exhausted lane"]
131
+ LANE --> REFILL["Run row lifecycle"]
132
+ REFILL --> EX{"Lane structurally exhausted?"}
133
+ EX -- No --> REFILL
134
+ EX -- Yes --> MORE{"Another pinned existing lane or source family?"}
135
+ MORE -- Yes --> LANE
136
+ MORE -- No --> END["Output: lanes_exhausted, then new_campaign_required if coverage still short"]
257
137
  ```
258
138
 
259
- First call `get_auth_status({})`. If auth is not OK, follow the returned login
260
- guidance before route resolution. Do not run refill research against an implicit
261
- or guessed workspace.
262
-
263
- Treat "refill senders", "fill senders", "load everyone up", and "max out
264
- senders" as sender-scoped requests. The target set is senders enrolled in active
265
- campaign-backed sequence campaigns, not the first active campaign returned by a
266
- resolver.
267
-
268
- ## Sender Selector Resolution
269
-
270
- When a request names a sender or workspace ("damiano r", "refill Kyle"), resolve
271
- that selector EXACTLY inside the CORRECT workspace before any mutation. A
272
- selector resolves only by exact sender id, exact display name, or an
273
- unambiguous display-name prefix within `list_senders({ workspaceId })` for the
274
- resolved workspace. `refill_sends` performs this resolution internally: a
275
- `--yolo`/scheduled invocation with `senders`/`senderNames` first pins each
276
- selector to an exact sender id in the requested workspace and passes only ids
277
- downstream.
278
-
279
- If any selector does not resolve, or resolves ambiguously, `refill_sends`
280
- returns `sender_selector_unresolved` with the candidate sender list and does NOT
281
- run. Never silently fall back to the active workspace's full fleet, and never
282
- mutate a different workspace than the one that owns the named sender. A name
283
- that belongs to another workspace (for example running "damiano r" while Clover
284
- is active) must fail loudly so the caller re-runs with the correct
285
- `workspaceId`; it must never create prep jobs in the active workspace instead.
286
- Resolve the workspace first (via `list_workspaces`/install mapping) when the
287
- named sender is not present in the currently resolved workspace's fleet.
288
-
289
- Goal-mode continuation: a skill cannot create or invoke `/goal` by itself. When
290
- this command is already running inside an active Codex goal, keep that goal open
291
- until every selected sender lane is filled by projected coverage
292
- (`sent + scheduled`) for the scheduler-forward target window, Christian
293
- explicitly stops/statuses the run, or a concrete non-scheduler blocker appears.
294
- Do not call the goal complete or blocked only because the current state is
295
- `awaiting_scheduler_after_ready_buffer`; treat it as loaded, awaiting scheduler.
296
-
297
- Load the internal workflow prompt and deterministic flow asset before taking
298
- any operational step:
139
+ Campaign enrollment/removal, missing pinned targets, or loss of eligibility is
140
+ typed scope drift; a fresh planner may rederive, but the host cannot silently
141
+ substitute a target. A current dashboard-active `PAUSED` campaign is eligible
142
+ only when the planner names that exact campaign as start-eligible. Starting it
143
+ may let the product scheduler schedule/send approved sequence actions and must
144
+ be reported. DMs are follow-ups, not refill horizon targets. Mixed sequences
145
+ keep distinct exact target paths; do not choose campaign-union behavior.
146
+
147
+ ## Connection and InMail capacity overlay
148
+
149
+ ```mermaid
150
+ flowchart TD
151
+ S["Sender capacity and exact selected days"] --> C{"Connection slots available now or later in rolling week?"}
152
+ S --> F{"Paid-InMail credit facts fresh?"}
153
+ F -- No --> RF["Refresh exact selected sender once per run, then replan"]
154
+ RF --> P{"Credits meet existing threshold, normally 5?"}
155
+ F -- Yes --> P
156
+ C -- Yes --> INV["Enable existing invite lanes"]
157
+ C -- No --> INVCAP["Cap invite lane with timing evidence"]
158
+ P -- Yes --> MAIL["Enable existing paid-InMail or cascade lane"]
159
+ P -- No --> MAILCAP["Skip paid lane; never lower threshold"]
160
+ INV --> UNION["Rank enabled existing lanes per sender"]
161
+ MAIL --> UNION
162
+ INVCAP --> ALT{"Paid lane enabled?"}
163
+ MAILCAP --> ALT2{"Invite or same-campaign connection fallback enabled?"}
164
+ ALT -- Yes --> UNION
165
+ ALT -- No --> CAP["Output: blocked_retryable / capacity or window"]
166
+ ALT2 -- Yes --> UNION
167
+ ALT2 -- No --> CAP
168
+ UNION --> POOL["Pool shared-campaign prepared inventory; never double-enrich"]
169
+ ```
299
170
 
300
- ```text
301
- get_subskill_prompt({ subskillName: "refill-sends-workflow" })
302
- get_subskill_asset({ subskillName: "refill-sends-workflow", assetPath: "core/flow.v1.json" })
171
+ `rollingWeeklyInvite.capacityFreedDuringWindow:true` means later capacity is
172
+ schedulable; use its timing fields rather than treating the opening gate as a
173
+ full-day blocker. Credit freshness precedes scheduler wait. Fresh facts below
174
+ threshold authorize only an already-eligible existing connection fallback or
175
+ another existing campaign—not a threshold change or new campaign.
176
+
177
+ ## Row lifecycle
178
+
179
+ For every selected lane, use the smallest action that can reduce its gap.
180
+
181
+ ```mermaid
182
+ flowchart TD
183
+ A["Lane still has projected gap"] --> ACTIVE{"Active import, preparation, or stuck work?"}
184
+ ACTIVE -- "Healthy active" --> WAIT["Bounded read-only wait; honor wait.deadlineAt"]
185
+ WAIT --> A
186
+ ACTIVE -- "Stuck or anomalous" --> REPAIR["Run only planner-bounded repairable cells or return blocker"]
187
+ REPAIR --> A
188
+ ACTIVE -- No --> APPROVE{"Generated rows can be approved?"}
189
+ APPROVE -- Yes --> AP["Approve exact bounded cohort with readiness authority"]
190
+ AP --> REPLAN["Record receipt and full authoritative replan"]
191
+ APPROVE -- No --> ENRICH{"Existing rows can be enriched or prepared?"}
192
+ ENRICH -- Yes --> PREP["Enrich, generate, or rerun exact bounded cohort"]
193
+ PREP --> REPLAN
194
+ ENRICH -- No --> SOURCE{"Selected source can add qualified rows?"}
195
+ SOURCE -- Yes --> ADD["Copy/import bounded rows from exact selected source"]
196
+ ADD --> REPLAN
197
+ SOURCE -- No --> EXPAND["Run source-family acquisition flow"]
198
+ EXPAND --> REPLAN
199
+ REPLAN --> A
200
+ ```
201
+
202
+ Structural exhaustion requires receipt-proven
203
+ `hasMoreFrontierRows:false`, zero `approvalCandidates`, no fresh active prep,
204
+ no `stuckActiveCells`, and no non-terminal `approvedNotDispatched` work. A dry
205
+ preparation receipt is run-scoped and prevents the identical no-op from
206
+ repeating. `USER_ADDED_ROWS_LIMIT_EXCEEDED` may use a bounded same-source split
207
+ through `load_csv_linkedin_leads` into the exact review batch; a true campaign
208
+ table hard cap returns a capacity blocker and never deletes rows.
209
+
210
+ ## Sender Post Engagers acquisition
211
+
212
+ ```mermaid
213
+ flowchart TD
214
+ A["Sender Post Engagers lane needs rows"] --> UNUSED{"Unused selected post/engager rows exist?"}
215
+ UNUSED -- Yes --> USE["Import/copy bounded unused rows"]
216
+ UNUSED -- No --> REFRESH["Refresh this sender's latest recent authored posts"]
217
+ REFRESH --> SELECT["Select new unprocessed relevant high-engagement posts"]
218
+ SELECT --> FETCH["Fetch/import their engagers and dedupe"]
219
+ FETCH --> YIELD{"Usable qualified yield?"}
220
+ YIELD -- Yes --> USE
221
+ YIELD -- No --> EX["Exhaust only this Post Engagers lane"]
222
+ EX --> NEXT["Advance to Shared Signal Discovery or next pinned existing lane"]
223
+ ```
224
+
225
+ The campaign must be sender-owned: exactly that sender, authored posts only.
226
+ Never invent keywords, search third-party posts, or run general Signal Discovery
227
+ against the Post Engagers campaign.
228
+
229
+ ## Shared Signal Discovery acquisition
230
+
231
+ ```mermaid
232
+ flowchart TD
233
+ A["Shared Signal lane needs rows"] --> UNUSED{"Unused selected rows or unprocessed posts exist?"}
234
+ UNUSED -- Yes --> USE["Import/copy bounded rows"]
235
+ UNUSED -- No --> STALE["Re-search stale existing topics for newly published posts"]
236
+ STALE --> SY{"Projected qualified yield meets min remaining gap or 100?"}
237
+ SY -- Yes --> USE
238
+ SY -- No --> DERIVE["Derive up to five concise positive-ICP topics from approved campaign intent"]
239
+ DERIVE --> DY{"Projected qualified yield meets floor?"}
240
+ DY -- Yes --> USE
241
+ DY -- No --> AGENT["Request 3-5 new agent-supplied topics through the same fenced continuation"]
242
+ AGENT --> SEARCH["Search once, excluding searchedKeywords, then project actual returned candidates"]
243
+ SEARCH --> PROVIDER{"All LinkedIn providers failed?"}
244
+ PROVIDER -- Yes --> RETRY["Retry the same bounded search once"]
245
+ RETRY --> RECOVER{"Provider recovered?"}
246
+ RECOVER -- No --> BLOCK["Output: blocked_retryable / provider_unavailable; universeExhausted false"]
247
+ RECOVER -- Yes --> AY{"Actual qualified yield meets floor?"}
248
+ PROVIDER -- No --> AY
249
+ AY -- Yes --> USE
250
+ AY -- No --> EX["Receipt-proven signal_yield_below_floor"]
251
+ EX --> NEXT["Advance to next pinned existing lane"]
252
+ ```
253
+
254
+ Provider outage never consumes a keyword tier or proves exhaustion. Temporary
255
+ derived search state is run-scoped; later runs may rediscover new posts. Agent
256
+ keywords are supplied only through the exact run continuation, never by user
257
+ steering or a second run.
258
+
259
+ ## Sales Nav and Prospeo acquisition
260
+
261
+ ```mermaid
262
+ flowchart TD
263
+ A["Cold-provider lane needs rows"] --> UNUSED{"Unused exact-source rows exist?"}
264
+ UNUSED -- Yes --> USE["Import/copy bounded rows"]
265
+ UNUSED -- No --> DEEP["Continue latest/unfetched pages of exact saved Sales Nav or Prospeo search"]
266
+ DEEP --> DY{"Qualified yield?"}
267
+ DY -- Yes --> USE
268
+ DY -- No --> OPTIONAL{"Known activity/recency filter exists?"}
269
+ OPTIONAL -- No --> NEXT["Advance to next pinned existing lane"]
270
+ OPTIONAL -- Yes --> CLONE["Clone exact approved search; remove one optional activity/recency filter"]
271
+ CLONE --> PROOF{"Original unchanged and every hard-fit filter fingerprint preserved?"}
272
+ PROOF -- No --> BLOCK["Output: blocked_retryable / provider_search_drift"]
273
+ PROOF -- Yes --> TRIAL["Import bounded trial candidates into existing gated source"]
274
+ TRIAL --> GATE["Run current ICP, exclusion, DNC, persona, geography, and company-fit gates"]
275
+ GATE --> GY{"Qualified yield?"}
276
+ GY -- Yes --> USE
277
+ GY -- No --> QUARANTINE["Rejects remain unsendable; advance lane"]
278
+ QUARANTINE --> NEXT
279
+ ```
280
+
281
+ The original saved search is immutable. An automatic trial may remove only a
282
+ known activity/recency signal; role, seniority, geography, industry, company
283
+ size, domain, include/exclude, DNC, and every other hard-fit constraint remain
284
+ byte-for-byte. Imported candidates are not approvals: current campaign gates
285
+ quarantine bad fits. If rubric rejects dominate, enter the bounded sample-quality
286
+ trial below before another source add. Do not repeat a spent trial or switch
287
+ outside planner order.
288
+
289
+ ## Rubric sample-quality trial
290
+
291
+ Rubric rejection may mean the source is poor or that one or several required
292
+ checks are unnecessarily narrow. Decide from an exact sample, not pass-rate alone.
293
+
294
+ ```mermaid
295
+ flowchart TD
296
+ A["Rows fail ICP/rubric gate"] --> STALE{"Score stale, errored, or computed under mismatched authority?"}
297
+ STALE -- Yes --> RESCORE["Rerun exact bounded cohort under unchanged current rubric"]
298
+ RESCORE --> RESULT{"Now qualified?"}
299
+ RESULT -- Yes --> CONTINUE["Continue row lifecycle"]
300
+ RESULT -- No --> SAMPLE
301
+ STALE -- No --> SAMPLE["Take exact representative rejected sample from prep receipt"]
302
+ SAMPLE --> SNAP["Snapshot complete rubric and prior digest"]
303
+ SNAP --> PROPOSE["Propose smallest 1-3 required-to-advisory bundle that creates useful new passes"]
304
+ PROPOSE --> HARD{"Touches employment, DNC/opt-out, legal/compliance, or explicit exclusion?"}
305
+ HARD -- Yes --> REJECT["Reject proposal; preserve complete prior rubric"]
306
+ HARD -- No --> SIM["Simulate bundle from recorded per-prospect evaluations; no mutation"]
307
+ SIM --> DELTA{"Any newly passing prospects?"}
308
+ DELTA -- No --> REJECT
309
+ DELTA -- Yes --> QUALITY["Review only newly passing profiles in campaign and customer context"]
310
+ QUALITY --> REPLY{"If these prospects replied, would the customer be comfortable and consider the replies relevant?"}
311
+ REPLY -- No --> REJECT
312
+ REPLY -- Yes --> DRIFT{"Live rubric digest still equals prior digest?"}
313
+ DRIFT -- No --> BLOCK["Output: blocked_retryable / rubric config drift"]
314
+ DRIFT -- Yes --> APPLY["Guarded apply entire required-to-advisory bundle"]
315
+ APPLY --> EXACT["Force exact bounded ICP rescore for accepted sample"]
316
+ EXACT --> VERIFY{"Dispatch and accepted quality receipt valid?"}
317
+ VERIFY -- Yes --> KEEP["Keep bundle; receipt before/after digests, changed checks, sample counts, and verdict"]
318
+ VERIFY -- No --> ROLLBACK["Restore complete prior snapshot and receipt rollback"]
319
+ REJECT --> BETTER["Keep rows unsendable; continue better supply or next existing lane"]
320
+ ROLLBACK --> BETTER
321
+ ```
322
+
323
+ `--yolo` may change more than one required rubric at a time only through this
324
+ single bounded reversible bundle. The change demotes selected required checks to
325
+ advisory scoring; it does not erase their evidence. The acceptance test is the
326
+ newly passing delta: if any reviewed prospect would make the customer unhappy,
327
+ surprised, or consider the reply irrelevant, keep the complete prior rubric and
328
+ continue the source waterfall. Never expose raw prospect profiles in the public
329
+ receipt.
330
+
331
+ ## Scheduler and uncertain-receipt recovery
332
+
333
+ ```mermaid
334
+ flowchart TD
335
+ A["Ready buffer covers remaining projected gap"] --> CREDIT{"Every selected paid lane has fresh credit facts?"}
336
+ CREDIT -- No --> RF["Refresh each exact selected sender once, then full replan"]
337
+ RF --> A
338
+ CREDIT -- Yes --> DATE{"Exact targetDate?"}
339
+ DATE -- Yes --> SWEEP["Dispatch one request-scoped product scheduler sweep"]
340
+ DATE -- No --> WAIT["Enter coordinator-owned bounded scheduler wait"]
341
+ SWEEP --> RECEIPT{"Receipt state?"}
342
+ RECEIPT -- Terminal --> READ["Canonical target-plan and request/effect readback"]
343
+ RECEIPT -- Active --> POLL["Poll only issued continuation until wait.deadlineAt"]
344
+ RECEIPT -- uncertain_outcome --> RECON["Reconcile same request key read-only; never redispatch"]
345
+ POLL --> RECEIPT
346
+ RECON --> TERM["Terminal: blocked_retryable with request identity intact"]
347
+ WAIT --> READ
348
+ READ --> FULL{"Projected coverage full?"}
349
+ FULL -- Yes --> COMPLETE["Terminal: complete"]
350
+ FULL -- No --> READY{"Ready buffer still covers gap?"}
351
+ READY -- Yes --> LOADED["Terminal: loaded_awaiting_scheduler"]
352
+ READY -- No --> PREP["Return to row lifecycle"]
353
+ ```
354
+
355
+ A scheduler sweep is a visible workspace-wide scheduling side effect: existing
356
+ product gates may schedule unrelated eligible cells in that workspace/date.
357
+ It never sends directly or raw-writes scheduler fields. `cellsConsidered is
358
+ allocation-attempt count`; `readyCellsFound` is prefilter inventory. Inspect
359
+ `campaignScopeSummary`, `prefiltered`, `skipped`, and `deferred`. For ready
360
+ closed-InMail cells with stale paid-credit evidence, use
361
+ `refresh_paid_inmail_credits_then_rerun`. `wait_for_capacity_or_window` means do
362
+ not source or prep more rows; `no_ready_cells_continue_refill_prep` returns to
363
+ the row lifecycle. `cellsScheduled:0` alone is not failure.
364
+
365
+ ## Explicit message-template revision
366
+
367
+ Ordinary refill never changes copy. Only a literal user-approved revision
368
+ envelope may enter this side path.
369
+
370
+ ```mermaid
371
+ flowchart TD
372
+ A["Literal messageTemplateRevision with approved markdown, digests, 1-500 row IDs, request/effect IDs"] --> DIGEST{"Current prior authority digest present?"}
373
+ DIGEST -- No --> FENCE["Acquire exact run and read reportingContext source templateAuthorityDigest"]
374
+ FENCE --> DG{"Digest returned?"}
375
+ DG -- No --> BLOCK["Terminal: blocked_retryable / template_authority_missing"]
376
+ DG -- Yes --> REVISE["Apply revision to exact cohort"]
377
+ DIGEST -- Yes --> REVISE
378
+ REVISE --> AUDIT["select_campaign_cells in batches <=20 with includeGeneratedMessageReview:true"]
379
+ AUDIT --> MATCH{"Every authoritativeGeneratedMessageReview complete, exact text/digest, matchesCurrentTemplateAuthority true?"}
380
+ MATCH -- No --> FAIL["Stop before approval: regenerated_copy_integrity_failed"]
381
+ MATCH -- Yes --> APPROVE["Approve only exact rowIds using compiled readiness authority"]
382
+ APPROVE --> RECEIPT{"Bounded preparation receipt includes laneScope?"}
383
+ RECEIPT -- No --> FAIL
384
+ RECEIPT -- Yes --> PLAN["Return to canonical refill plan"]
385
+ ```
386
+
387
+ Formula Message/Subject columns, previews, exports, and counts are not copy
388
+ authority. `forceRerun:true` never implies a revision or approval.
389
+
390
+ ## Manual approval and `--yolo`
391
+
392
+ ```mermaid
393
+ flowchart TD
394
+ A["Render fresh bounded packet in normal chat as Markdown"] --> MODE{"--yolo present?"}
395
+ MODE -- Yes --> AUTO["Auto-accept all planner-ranked transitions among existing campaigns, lanes, sources, and safe refill primitives"]
396
+ MODE -- No --> ASK["Open host-native structured question with exactly Accept and Decline"]
397
+ ASK --> DECISION{"Operator choice?"}
398
+ DECISION -- Accept --> RUN["Execute only rendered envelope"]
399
+ DECISION -- Decline --> STOP["Stop with no mutation"]
400
+ AUTO --> RUN
401
+ ```
402
+
403
+ Use `request_user_input` in Codex and `AskUserQuestion` in Claude Code. The
404
+ full packet belongs in the normal chat message immediately before the question:
405
+ workspace, sender scope, campaign-by-campaign plan table, action, target/cap,
406
+ source, blocker/skip reason, exact IDs, expected side effects, forbidden actions,
407
+ and stop condition. The question body must be compact and refer back to the
408
+ posted packet instead of duplicating it. `--yolo` may switch among every fresh
409
+ planner-ranked existing campaign/lane/source family and start the exact selected
410
+ `PAUSED` campaign. It does not create campaigns, direct-send, archive/delete,
411
+ change sender limits or paid-InMail thresholds, reassign senders, or invent
412
+ source selection outside the planner.
413
+
414
+ ## Typed outputs
415
+
416
+ ```mermaid
417
+ flowchart TD
418
+ A["Fresh canonical state"] --> C{"Terminal classification"}
419
+ C -- "sent + scheduled meets target" --> COMPLETE["complete: report saturation ledger"]
420
+ C -- "ready buffer covers gap after bounded scheduler wait" --> LOADED["loaded_awaiting_scheduler: report expected pickup and stop"]
421
+ C -- "recoverable external, capacity, active-work, or receipt condition" --> BLOCKED["blocked_retryable: exact blocker, receipt, and safe retry condition"]
422
+ C -- "current lane spent but another existing target remains" --> EX["lanes_exhausted internally; continue next_exact_target"]
423
+ C -- "every existing campaign/lane/source spent or infeasible" --> NEW["new_campaign_required: propose missing shape and ask whether to create it"]
424
+ C -- "scope/config/readiness changed" --> DRIFT["blocked_retryable: typed drift; never guess"]
303
425
  ```
304
426
 
305
- Continue both loads until `hasMore:false`; parse the flow JSON and verify
306
- `workflow:"refill-sends-workflow"` with a `v1` version. Then follow that
307
- workflow exactly. The default path is read-only research: compute the refill
308
- target plan first, resolve the route, identify the sender-relevant campaign
309
- that most recently had scheduler-owned sends, read refill state for that target,
310
- report the next safe step using campaign names first, and stop before mutation
311
- unless the user has explicitly approved the exact workspace,
312
- campaign/table/source ids, caps/dates, approval mode, expected side effects,
313
- and stop/rollback condition.
314
-
315
- Immediately after loading the internal workflow, call
316
- `get_refill_target_plan`. The target plan is the canonical first fact receipt:
317
- eligible senders, selected sender-local days, gross target, actual sent
318
- coverage, scheduler-owned scheduled coverage across active enrolled campaigns,
319
- projected coverage (`sent + scheduled`), inferred per-sender send lane/action
320
- selections, ready buffer, remaining projected gap, paid-InMail credit/threshold
321
- feasibility, `targetShapeRevision`, and `stateRevision`. Refill target lanes are
322
- connection invites (`send_invite`), standalone paid InMails
323
- (`send_inmail_closed`), or unified Sales Nav cascades represented publicly as
324
- `send_inmail_closed` with `campaignClassification:"sales_nav_cascade"`. For a
325
- Sales Nav cascade, refill the selected campaign first; its sequence can route
326
- prospects to Open InMail, paid InMail while fresh credits are >= 5, or
327
- same-campaign connection fallback without asking for separate open/paid/
328
- connection campaigns. DMs (`send_dm`) are follow-up actions, not refill horizon
329
- target capacity, and must not be counted as refill sent, scheduled, or ready
330
- coverage. When `actionTypes` are omitted, trust the target plan's inferred lane
331
- rather than asking which campaign class to fill. If a stale target plan selects
332
- `send_dm` or `send_inmail_open` as the lane, stop and re-plan with the current
333
- planner before mutation.
334
- Short form: trust the target plan's inferred lane when it is a connection invite,
335
- paid-InMail refill lane, or unified Sales Nav cascade.
336
- Connection-only evergreen campaigns with exact `["send_invite"]` sequence proof
337
- are plain invite capacity: treat them as `selectedLane:"send_invite"` only. Do
338
- not infer DM, InMail, Sales Nav cascade, paid-credit refresh, message
339
- generation, follow-up, sequence mutation, launch/start, scheduling override, or
340
- direct-send work from a connection-only lane.
341
-
342
- Structured planner packet:
343
-
344
- - `target.eligibleSenderLedger` is the public sender eligibility ledger.
345
- - `target.senderRefillPlans[]` is the canonical sender-level packet; read and
346
- display it before mutation.
347
- - Each sender packet includes `campaignRanking.options`, `sourcePlan`,
348
- `refillReceipt`, `nextActions`, and `manualAlternates`.
349
- - `refillReceipt` is the public ladder receipt. It carries the selected
350
- campaign/sender/lane summary, skipped rungs, existing-row frontier proof, and
351
- any absolute `wait.deadlineAt`.
352
- - Preserve these coverage labels exactly: `Need to prepare`, `Goal`,
353
- `Already sent`, `Scheduled`, `Ready and waiting to be scheduled`,
354
- `Still need to schedule`, and `Still need to prepare`. Never render an
355
- unlabeled `Still need` value.
356
- - `target.globalActionQueue` is the only cross-sender yolo execution queue.
357
- Execute only the globally ranked primitive at
358
- `target.globalActionQueue[0]`, then rerun `get_refill_target_plan` with the
359
- same `workspaceId` before choosing another action. In `--yolo`, repeat this
360
- through exact `continuation.arguments`: resume active targets with their
361
- `targetConfig`/`runId`/`fence`, and start each next planner-preflighted target
362
- with its `targetConfig` and a new fence, until workspace projected coverage
363
- fills, the coordinator returns the same-target `loaded_awaiting_scheduler`
364
- terminal, or a concrete blocker appears.
365
- - When that primitive is `run_scheduler_sweep`, show `workspaceWide:true` and
366
- its reason in the approval/yolo packet so the operator sees that the sweep
367
- may schedule other eligible workspace cells on the approved date.
368
- - `manualAlternates` are not yolo actions. Threshold/floor lowering, NEW
369
- campaign creation, source-family switches, and the capSaturated
370
- `manual_source_replenishment` dead-end (needs human table cleanup) are manual
371
- continuations only. Same-provider-source continuation
372
- (`continue_sales_nav_source`, `continue_prospeo_source`) and
373
- `start_paused_campaign` for a start-eligible campaign are `--yolo` primitives,
374
- not manual alternates.
375
-
376
- Refill action ladder: approve generated rows only when an explicit bounded
377
- approval gate exists, process all existing same-campaign unenriched/unprepared
378
- rows in bounded batches before any source work, then copy bounded net-new rows
379
- from the selected source (`selectedLeadListId`, provider, and source
380
- fingerprint preserved), then use provider-aligned same-source-more against the
381
- campaign's OWN saved provider search (Sales Nav or Prospeo for cold campaigns).
382
- Same-provider-source-more is a bounded `--yolo` add. A DIFFERENT source or a
383
- source-family switch changes the reply-rate baseline and stays a manual
384
- alternate, not a `--yolo` side effect.
385
- For Signal Discovery, consume bounded net-new eligible source rows first. If
386
- none remain and the campaign has no reusable tab keywords or unselected posts,
387
- derive at most five search keywords from its saved positive ICP criteria without
388
- changing rubrics or thresholds. Broadening rounds follow a strict keyword-source
389
- ORDER because the post universe refreshes over time — a keyword searched days or
390
- weeks ago yields NEW posts today: (1) `keywordSource:"stale_researched"` FIRST
391
- re-searches the campaign's EXISTING keywords whose last search is stale (tab
392
- timestamp before the current run day, or no stored timestamp), deduping to posts
393
- not already scraped/selected; (2) `"derived"` then derives fresh keywords from the
394
- campaign's stored search intent; (3) `"agent_supplied"` last. When a
395
- `broaden_signal_search` action returns `keywordSource:"agent_supplied"`, both
396
- earlier tiers are receipt-proven spent this run but the 45-day universe is not
397
- proven dry: read the campaign brief, ICP, and persona and invent 3-5 concrete NEW
398
- GTM-topic keywords that are NOT in the action's `searchedKeywords` negative list —
399
- still without changing rubrics or thresholds, and never re-issuing a spent search.
400
- When a run stops asking for agent-supplied keywords (blocker
401
- `signal_agent_keywords_invalid`, or a pending `agent_supplied` broaden with no
402
- keywords), re-invoke the SAME `refill_sends` command with the run's `runId` and
403
- `fence` plus `agentKeywords: [your 3-5 keywords]`; the fenced run validates them
404
- against the negative list, consumes them once into the pending broaden round, and
405
- resumes automatically. An out-of-envelope payload is typed-refused so you can
406
- correct and re-invoke. Signal exhaustion is a
407
- quantified yield projection, not a fuzzy engagement read: every tier projects
408
- new-lead yield as the sum of scrapable engager counts across the NEW recent (<=45
409
- day) candidate posts times the campaign's observed headline-ICP pass rate, and the
410
- floor `min(remaining prep gap, 100)` is only ever evaluated on the ACTUAL
411
- candidates a search round returned — never as a pre-search short-circuit. A
412
- below-floor round at the stale or derived tier consumes that tier and escalates to
413
- the next one; the agent tier is the always-available terminal tier, so only an
414
- agent-supplied round that actually searched and yielded below floor — zero new
415
- posts being the projection-zero special case — is receipt-proven lane exhaustion
416
- (`signal_yield_below_floor`, recording the keyword-source tier, candidate post
417
- count, engager sum, observed pass rate, and threshold): update run state, replan,
418
- and continue with the exact next ranked campaign in the original sender lane chain
419
- during the same run. Do not persist derived keywords or lane exhaustion into
420
- campaign configuration; a later refill run rereads the current approved rubric and
421
- may re-search refreshed keywords or discover new posts and engagers.
422
-
423
- Sender-owned Post Engagers is the structural exception to general Signal
424
- Discovery broadening. A campaign named `<sender> - Post Engagers` with exactly
425
- one attached sender may select only that sender's authored posts; third-party
426
- keyword results can never refill it. Once its bounded sender-owned continuation
427
- has no reusable post/frontier, treat `sender_owned_post_required` as exhaustion
428
- of that campaign lane only, record it on the sender cursor, and immediately
429
- replan to the exact next ranked waterfall lane—normally Shared Signal
430
- Discovery—inside the same `--yolo` run. Do not request agent keywords, load the
431
- campaign brief for keyword invention, or dispatch a general Signal search against
432
- the Post Engagers campaign. General stale/derived/agent keyword broadening resumes
433
- normally only after the planner selects the shared Signal Discovery campaign.
434
- Source/copy/fallback requires receipt-proven exhaustion of earlier rungs:
435
- `existingRowFrontier.hasMoreFrontierRows:false`, zero `approvalCandidates`, no
436
- fresh active prep, no `stuckActiveCells`, and no non-terminal
437
- `approvedNotDispatched` rows. Treat anomalies, `stuckActiveCells`, and
438
- non-terminal `approvedNotDispatched` as diagnose-and-report gates, not
439
- exhaustion. Terminal `approvedNotDispatched` blockers may be reported, then the
440
- ladder can proceed.
441
-
442
- Repairable draft fallback: when a run ends `lanes_exhausted` (or the route
443
- reports no schedulable campaign) and `resolve_campaign_fill_route`
444
- skippedTargets contains a non-archived DRAFT campaign blocked only by
445
- `campaign_sequence_missing` whose table already has prospect rows and a
446
- completed builder pipeline (brief plus Enrich/ICP/Generate Message/Approved
447
- columns), `--yolo` may finish that campaign's setup with existing product
448
- tools and continue the waterfall into it: (1) `update_campaign` to attach
449
- exactly the workspace's already-eligible refill senders from the current
450
- eligible sender ledger — never invent or reassign other senders; (2)
451
- `attach_recommended_sequence` WITHOUT a currentStep override so the backend
452
- selects the tier-correct cascade while the approved-message gate stays
453
- intact; (3) invoke `refill_sends --yolo` again with the same
454
- workspace scope so the fresh route targets the now start-eligible campaign and
455
- the normal bounded prep/approve/start/schedule ladder fills it. This repairs
456
- only existing campaigns already holding imported prospects; it never creates
457
- campaigns, never touches archived campaigns, and messaging still flows through
458
- the campaign's own brief, rubric, and bounded approval gates.
459
-
460
- Run-local paid-credit guard: in `--yolo`, the `refill_sends` MCP command
461
- automatically maintains a checkpointed `refreshedPaidInmailSenderIds` set for
462
- the entire fenced run across resume calls. If its first target plan has stale/missing paid-InMail credit
463
- facts, it refreshes each selected sender at most once, reruns
464
- `get_refill_target_plan`, and returns the post-refresh `targetPlan` before
465
- choosing the next prep/source-copy/bounded-approval/read-only wait action. If fresh facts are still below
466
- threshold, below-threshold paid-InMail facts fall back to an existing connection
467
- lane, the same Sales Nav cascade campaign's connection branch, or a manual
468
- continuation.
469
-
470
- Freshness gate precedes scheduler wait: if any selected
471
- `target.senderRefillPlans[].paidInmail.status` is `missing_credit_facts` or
472
- `stale_credit_facts`, or the target plan contains a
473
- `refresh_paid_inmail_credits` candidate, do not enter `wait_for_scheduler` even
474
- when `remainingReadyOrProjectedGap:0`. Refresh the exact selected sender credit
475
- facts once, rerun `get_refill_target_plan`, and only then decide whether
476
- scheduler wait is the next safe action. If facts remain missing/stale after the
477
- single refresh attempt, stop with a paid-InMail freshness blocker instead of
478
- waiting on scheduler pickup.
479
- The standalone MCP refresh surface is the scoped route
480
- `/api/v3/mcp/senders/:senderId/refresh-inmail-credits` with explicit
481
- `workspaceId`; do not use active-workspace mutation as the automation control
482
- path.
483
-
484
- Compact refill lessons: sender-level target plan is final truth; trust
485
- `schedulerGate.sendable` and scheduler gate blockers, not raw
486
- `unipileAccountStatus` labels alone; use compact prep status checks for
487
- long-running jobs; reread target plans after prep or cancel; treat ready rows as
488
- intermediate; avoid huge parallel target-plan reads when output is large. If a
489
- campaign produces prepared/ready rows but sender-level projected coverage does
490
- not move after one bounded settle loop, pivot to compact prep status or a
491
- scheduler-proven lane. Do not keep waiting on campaign-level ready counts.
492
-
493
- For exact-date requests, pass `targetDate` to `get_refill_target_plan`. If you
494
- need raw proof, call the read-only `get_scheduler_fill_capacity` query for the
495
- same sender/action/date; it tells the MCP how many cells the product scheduler
496
- will try to place and does not import, approve, schedule, refresh credits, or
497
- mutate.
498
- Treat `rollingWeeklyInvite.capacityFreedDuringWindow:true` as proof that
499
- rolling-weekly capacity frees later in the send window and remains schedulable;
500
- use its timing fields instead of treating the window-start gate as a permanent
501
- full-day blocker.
502
- When the refill loop has ready rows and needs scheduler pickup now, use
503
- `run_scheduler_sweep` with the same explicit `workspaceId`; it can place cells
504
- within existing scheduler gates and returns the receipt, but it never sends or
505
- bypasses limits.
506
- A scheduler sweep is a visible workspace-wide scheduling side effect. The
507
- request packet limits the expected target changes to its exact date, senders,
508
- action types, campaigns, tables, and stable sources, but the product scheduler
509
- may also place unrelated eligible workspace work; disclose that possibility in
510
- the approval packet and receipt. For an exact-date sweep action, execute it
511
- once, then reread the full target plan before taking or presenting another
512
- action.
513
- The exact-date `run_scheduler_sweep` may schedule eligible workspace cells
514
- through existing product gates; it never sends directly or raw-writes scheduler
515
- fields. Only non-exact or no-sweep scheduler waits are read-only.
516
- On resume, reconcile the current target plan and matching sweep status/receipt
517
- before retrying. A matching same-key terminal receipt replays; an
518
- `uncertain_outcome` stops for reconciliation and must not schedule again.
519
- Scheduler-run receipt interpretation: `cellsConsidered is allocation-attempt
520
- count`, not total ready supply, while `readyCellsFound` is ready inventory found
521
- before prefilters. Inspect `campaignScopeSummary` before assuming the selected
522
- refill campaign/table was included; if absent, do not infer that target was
523
- ready-but-blocked. Interpret `prefiltered` as ready cells removed before
524
- allocation, `skipped` as considered cells blocked by scheduler gates, and
525
- `deferred` as considered cells waiting on windows/capacity/cooldown. For ready
526
- closed-InMail cells with stale paid-credit prefilter/defer reasons, refresh
527
- paid-InMail credits once through existing tools, then rerun `run_scheduler_sweep`
528
- or read `action:"status"`; `refresh_paid_inmail_credits_then_rerun` is that
529
- path. `wait_for_capacity_or_window` means report loaded/capped/waiting and do
530
- not source or prep more rows; `no_ready_cells_continue_refill_prep` means return
531
- to the refill/prep ladder. Do not treat `cellsScheduled:0` alone as failure.
532
- If the target plan is complete by projected coverage, report that the selected
533
- target is already filled and no-op without asking for approval. If the ready
534
- buffer covers the projected gap, paid InMail credit facts are fresh for every
535
- selected paid-InMail lane, but scheduled coverage is still short, keep the run
536
- open in a persistent read-only scheduler wait loop. Honor the emitted wait
537
- metadata: reread `get_refill_target_plan` after about 30 seconds by default (or
538
- the action's explicit `rereadAfterMs`), never use a blind hard-coded sleep, and
539
- stop at the named bound using `receipt_deadline > unchanged_poll_bound >
540
- run_budget` precedence. A matching active/uncertain request key is never
541
- redispatched. Continue until projected coverage fills the target, a concrete
542
- non-scheduler blocker appears, a named bound stops the run, or Christian
543
- explicitly asks to stop or only receive a status report. Treat
544
- `awaiting_scheduler_after_ready_buffer` as an in-progress wait state, not a
545
- close-out condition.
546
- Wait actions are gates, not competing goals. When `wait_for_active_work` or
547
- `wait_for_scheduler` includes receipt `wait.deadlineAt`, honor that absolute
548
- deadline; if it is expired on this call, escalate to diagnostics with the
549
- receipt evidence instead of issuing another blind wait.
550
- If paid InMail credit facts are stale or missing and the first target plan
551
- contains `refresh_paid_inmail_credits`, do not present that as the operator's
552
- next action in `--yolo`. Do not present paid-credit refresh as the next operator
553
- action after `refill_sends` returns `autoPaidInmailRefresh` and the
554
- post-refresh `targetPlan`. Trust `refill_sends.autoPaidInmailRefresh`: it should
555
- show the exact sender ids refreshed once, sender-credit-cache write receipts,
556
- and a returned post-refresh `targetPlan`. Continue from that post-refresh packet.
557
- If paid InMail is below threshold after the fresh credit read, report the exact
558
- campaign/table/column threshold action or same-campaign connection fallback;
559
- `--yolo` does not lower paid-InMail thresholds or create campaigns.
560
-
561
- If the plain route's managed waterfall targets are stale, for example skipped
562
- targets show archived/completed shared slots or the returned targets do not cover
563
- the named sender, immediately run the active route refetch before declaring a
564
- sender blocked. Current dashboard-active `PAUSED` campaign-backed sequence
565
- campaigns are start-eligible refill candidates; read refill state before deciding
566
- whether to prep, approve, start, or skip. Do not treat them as archived inventory.
567
-
568
- For interactive Codex or Claude Code sessions, that approval must use the
569
- host-native structured question gate, not plain chat:
570
-
571
- - Codex: use `request_user_input`.
572
- - Claude Code: use `AskUserQuestion`.
573
-
574
- The approval question must present the final refill step with exactly two
575
- choices: `Accept` and `Decline`. Render the full operator output packet in
576
- normal chat immediately before opening the structured approval question so it
577
- can be displayed as Markdown. The chat packet must include workspace, sender
578
- scope, a campaign-by-campaign table with campaign name, sender names, action,
579
- target count/cap, source/list, and blocker/skip reason, then exact ids,
580
- expected side effects, forbidden actions, and the stop condition. The structured
581
- question body must be compact and refer back to the posted packet instead of
582
- duplicating it. Treat `Accept` as permission for only the rendered target, caps,
583
- mode, and side effects. Treat `Decline` as a hard stop with no mutation.
584
-
585
- If Christian includes `--yolo` in the same refill request, treat that flag as
586
- auto-accept for the rendered bounded refill packet after the required fresh state
587
- reread. For sender-scoped language with no named senders, `--yolo` means all
588
- eligible healthy senders enrolled in active campaign-backed sequence campaigns in
589
- the requested `workspaceId`. Without `--yolo`, if Christian did not name senders, ask
590
- which eligible enrolled senders to refill before choosing campaigns or mutating.
591
-
592
- `--yolo` covers the exact sender set, action/date target, caps, original
593
- planner-emitted lane chains, approval mode, and refill side effects. It may
594
- authorize `start_campaign` only when the fresh packet's selected evergreen lane,
595
- selected campaign ranking, and original lane chain name the same `PAUSED`,
596
- dashboard-active, campaign-backed sequence refill target. Starting a paused
597
- campaign can let the product scheduler schedule/send
598
- approved eligible sequence actions, so include that expected side effect in the
599
- packet. It does not authorize starting unrelated, archived, completed, draft, or
600
- direct campaigns, separate launch/send actions, archive or delete cleanup,
601
- direct scheduler writes, sender reassignment, paid-InMail threshold changes,
602
- connection campaign creation, paid-InMail credit refreshes outside the exact
603
- rendered sender packet, or campaigns outside those
604
- selected for the eligible sender set. Refuse if a fresh selection leaves the
605
- original lane chain or changes the sender/action/date/cap/side-effect envelope.
606
- Campaign/table/action-key/readiness revisions and sent/scheduled counts may
607
- change as the fresh planner advances the approved lane chain; those changes do
608
- not require a second approval. Treat leaving that stable envelope as scope
609
- drift and refuse it.
610
-
611
- Short form: start only the exact selected `PAUSED` campaign authorized by all
612
- three fresh planner proofs; leave every unselected paused fallback unchanged.
613
-
614
- In `--yolo`, continue until projected coverage fills or a concrete blocker is
615
- proven. After each
616
- apply/prep/source-copy/bounded-approval/read-only wait result, reread state, settle processing when needed, and move to
617
- the next selected sender or start-eligible same-packet campaign instead of
618
- stopping after the first partial result. Receipt-proven source exhaustion must
619
- advance the planner run-state cursor before choosing the next lane. If no
620
- in-envelope safe action remains,
621
- return concrete continuation options with campaign names, exact ids, which option
622
- is still covered by the current packet, and which option needs a new approval
623
- packet.
624
-
625
- For `--yolo` fill/schedule requests, completion means projected saturation
626
- (`sent + scheduled`) for the scheduler-forward target window, not merely
627
- prepared/approved/ready rows. Maintain a target-window saturation ledger per
628
- selected sender: selected send days, gross capacity, actual sent cells, future
629
- scheduler-owned scheduled cells with non-null `scheduledFor`, projected count,
630
- ready-to-schedule buffer, remaining projected gap, paid-InMail feasibility,
631
- `targetShapeRevision`, `stateRevision`, and the next MCP primitive that can
632
- reduce the gap. After every apply/prep/source-copy/bounded-approval/read-only wait result, wait for processing, reread
633
- the target plan/refill state, recompute the ledger, then keep applying safe
634
- bounded actions until projected coverage fills the target window or a concrete
635
- non-scheduler blocker is proven.
636
- If ready rows cover the projected gap but the scheduler has not picked them up
637
- yet, report loaded, awaiting scheduler and run the persistent read-only
638
- scheduler wait loop. Do not finish the refill goal, mark it complete, or mark it
639
- blocked only because it is still `awaiting_scheduler_after_ready_buffer`; keep
640
- waiting unless Christian stops the run or the host cannot continue.
641
-
642
- Carry the canonical `refill_reporting.v2` DTO through progress, continuation,
643
- replay, and terminal reporting without recomputing counts or losing live epoch,
644
- snapshot sequence, reset reason, watermark, blockers, compatibility, or
645
- terminal outcome. Redact raw copy and prospect fields.
646
-
647
- Future scheduled coverage and already sent actions are distinct; only
648
- scheduler-owned future scheduled actions count as scheduled coverage. For
649
- multiple target dates, finish D1 execution and the full D1 reread before
650
- planning D2. Completion proof is product-native Sellable MCP evidence only:
651
- target plan, campaign refill state, scheduler capacity, sweep/status, and
652
- bounded receipts. Never use individual cell ids, Prisma, SQL, direct database
653
- access, or production-environment scripts as completion proof.
654
-
655
- In `--yolo`, the default fill target is the scheduler-forward 48-hour window
656
- unless `--target-date`/`targetDate`, `--until`/`untilDate`, or an explicit
657
- compatibility `horizonSendDays` is provided. If a target date is provided,
658
- compute bounded gaps for only that sender-local date using scheduler-fillable
659
- slots. If an until date is provided, compute bounded gaps through that
660
- sender-local date inclusive, skipping no-send days and never extending beyond
661
- that date without a new packet. For each target sender, compute the bounded gap
662
- from that sender's healthy daily capacity, existing future scheduler-owned
663
- scheduled sends, and rows already ready to schedule across active campaigns
664
- enrolled with that sender. Then pick the best same-sender campaign to fill the
665
- gap: prefer recent/future scheduler-owned sends for that sender, then strongest
666
- recent result evidence, then source health. Do not stop
667
- after filling only one sender when the request was sender-scoped. If a
668
- same-source copy hits the campaign-table row cap, split the current source into
669
- a bounded LinkedIn profile source list, confirm only that smaller list into the
670
- same campaign, and operate on the copied review batch. Do not fall back to
671
- on-demand campaigns or unrelated active-campaign fills.
672
-
673
- Public concepts are regular campaign and evergreen campaign. Internal direct
674
- campaign types are unsupported refill targets. `fill_campaign_horizon` is only a
675
- legacy evergreen-only lower-level primitive after route and refill-state
676
- evidence proves an evergreen target.
427
+ Do not keep polling after a terminal. `loaded_awaiting_scheduler` is a valid
428
+ terminal, not permission for a host-owned loop. The sole refill-ladder expansion
429
+ that asks the user in `--yolo` is `new_campaign_required`; refill itself never
430
+ creates that campaign.
431
+
432
+ ## Non-negotiable authority and proof
433
+
434
+ - Before preparation, approval, or exact-date scheduling, require a validated
435
+ compiled campaign and positive exact target readiness identity from the
436
+ backend compiler: `profileSchemaVersion`, `compilerVersion`,
437
+ `campaignDigest`, `actionColumnId`, action, `pathDigest`,
438
+ `dependsOnColumnIds`, `runCondition`, the selected target's `capabilities`,
439
+ `requestId`, `effectId`, and `dateGuards`. Never infer no-message behavior
440
+ from a missing Generate Message column. Prepare rows according to the
441
+ persisted sequence; bounded enrichment and LLM prerequisite calls are
442
+ allowed only when authorized by the exact readiness packet and receipted.
443
+ - Refuse approval outside the bounded authorized cohort. The exact selected
444
+ target path controls message, subject, comment, reaction, and row approval.
445
+ For exact dates, require the date strictly after sender-local today and inside
446
+ the fill horizon. Prepared, approved, and ready rows remain intermediate
447
+ evidence; completion requires request/effect-attributed scheduler-owned
448
+ scheduled readback.
449
+ - Execute one bounded primitive, then perform a full authoritative reread.
450
+ Never call a LinkedIn outreach/provider-send surface from refill. No direct
451
+ sends, raw scheduler writes, broad approval, threshold/limit changes,
452
+ sender reassignment, destructive cleanup, or new campaign creation.
453
+ - Maintain the target-window saturation ledger per sender: selected days,
454
+ gross capacity, actual sent, future scheduler-owned scheduled with non-null
455
+ `scheduledFor`, projected coverage (`sent + scheduled`), ready buffer,
456
+ remaining gap, paid-InMail feasibility, `targetShapeRevision`, and
457
+ `stateRevision`. Future scheduled coverage and already sent actions are
458
+ distinct.
459
+ - Carry canonical `refill_reporting.v2` unchanged through progress,
460
+ continuation, replay, and terminal output. Completion proof is Sellable MCP
461
+ evidence only: target plan, campaign refill state, scheduler capacity,
462
+ sweep/status, and bounded receipts. Never use individual cell ids, Prisma,
463
+ SQL, direct database access, or production-environment scripts as completion
464
+ proof. Redact raw copy and prospect fields.