---
name: refill-sends-workflow
description: Closed execution contract for the canonical fenced refill-sends coordinator.
visibility: internal
allowed-tools:
  - mcp__sellable__refill_sends
  - mcp__sellable__refill_v3_advance
  - mcp__sellable__refill_v3_continue
  - mcp__sellable__list_workspaces
  - mcp__sellable__get_active_workspace
  - mcp__sellable__get_subskill_asset
  - mcp__sellable__get_refill_target_plan
  - mcp__sellable__get_scheduler_fill_capacity
  - mcp__sellable__select_campaign_cells
  - mcp__sellable__wait_for_campaign_processing
---

# Refill Sends Workflow

This is the closed executor companion to the public `refill-sends` skill. It
carries BOTH routes, and it decides between them itself.

## Route selection — do this FIRST

Decide the route before loading any flow asset and before the next coordinator
call. Do NOT treat an earlier tool call as the decision: if a refill was already
opened on the wrong route, this section overrides it. The predicate is the same
MECHANICAL one the public skill uses, observable in the connected Sellable MCP
tool list:

> **Is `refill_v3_advance` one of the tools this server exposes?**

- **Yes → take the Refill V3 route.** This is the DEFAULT for every workspace on
  a server that exposes the tool. Load `core/flow.v3.json` and follow
  [Refill V3](#refill-v3-one-deterministic-step-per-call). Do not load
  `core/flow.v1.json`, do not call `get_refill_target_plan`, and do not call
  `refill_sends` — including when an earlier call in this session already did.
- **No → take the V1 route.** The installed server predates Refill V3, so
  `refill_sends` is the only coordinator available. Load `core/flow.v1.json` and
  follow the V1 sections below.

Never infer the route from a workspace flag, a campaign field, a tool result, or
the operator's wording: no such cutover signal exists. Report the selected route
explicitly before the first coordinator call.

## Which asset to load

The asset follows the route selected above:

- **V3 → `core/flow.v3.json`.**
- **V1 → `core/flow.v1.json`.**

The public Mermaid diagrams define product order; the loaded flow asset defines
the deterministic gates. Exactly one coordinator owns execution per route:
`refill_v3_advance` on V3, `refill_sends` on V1. Do not manually reproduce
either ladder with lower-level campaign, source, preparation, approval, or
scheduler tools.

## V1 route

Everything from here to [Refill V3](#refill-v3-one-deterministic-step-per-call)
applies only when `core/flow.v1.json` was the loaded asset. On this route
`refill_sends` is the only execution owner. Do not manually reproduce the ladder
with lower-level campaign, source, preparation, approval, or scheduler tools.

Every mode renders the same read-only D1 plan before any mutation. Default
non-yolo mode asks for explicit acceptance of that packet and later transitions.
In `--yolo`, the coordinator may
perform every fresh planner-ranked safe transition among existing campaigns,
lanes, and source families. It may not create a new campaign.

The host must load the entire asset and verify a COMPATIBLE version before D1:
same major (`v1`) and minor at or above the minimum `v1.9`, so `v1.9` and
`v1.10` are accepted while `v1.8` and `v2.x` are not. It reports that version
explicitly. A missing, unparseable, wrong-major, or below-minimum version is
`workflow_version_mismatch` and stops before planner or mutation. Exact-string
matching is forbidden here: the asset ships in the npm package while the
verifying skill installs separately, so an exact pin makes every additive
change breaking.

## Closed-loop executor

```mermaid
flowchart TD
    A["Receive exact workspace, sender/action selectors, and date envelope"] --> ASSET["Verify complete flow.v1.json, workflow refill-sends-workflow, compatible version v1 >= v1.9"]
    ASSET --> D1["Call get_refill_target_plan read-only with immutable route intent"]
    D1 --> RENDER["Render sender/campaign waterfall, coverage ledger, bounded side effects, forbidden actions, and stop condition"]
    RENDER --> D2["Call refill_sends with workspaceCoordinator true and displayed revision/action pins"]
    D2 --> STATUS{"Coordinator status?"}
    STATUS -- plan_ready --> PR["Render returned targetPlan and exactTargetPlan before planned_workspace_run"]
    PR --> D2
    STATUS -- in_progress --> CONT["Call only exact continuation.arguments"]
    CONT --> D2
    STATUS -- complete --> C["Report projected saturation and stop"]
    STATUS -- loaded_awaiting_scheduler --> L["Report workspace-wide loaded scheduler-owned terminal and stop"]
    STATUS -- blocked_retryable --> BK{"blocker = agent_supplied_signal_keywords_required?"}
    BK -- yes --> AK["Derive 3-5 NEW GTM-topic keywords from the campaign brief/ICP, none in report.searchedKeywords, then re-invoke refill_sends with report.continuation.argumentsTemplate (runId, fence, agentKeywords filled). One handoff per fence."]
    AK --> D2
    BK -- no --> B["Report exact blocker, receipt, and retry condition; stop"]
    STATUS -- new_campaign_required --> N["Ask whether to create the proposed campaign; refill stops"]
```

MANDATORY agent-keyword handoff: a `blocked` result whose blocker is
`agent_supplied_signal_keywords_required` is NOT a terminal — it is a request
addressed to YOU, the operating agent. Do not report it and stop. Derive 3-5
concrete NEW GTM-topic keywords from the campaign brief/ICP (topics this ICP
posts and comments about), none of which appear in `report.searchedKeywords`,
fill them into `report.continuation.argumentsTemplate.agentKeywords`, and
re-invoke `refill_sends` with exactly that template (it carries the run's
`runId` and `fence`). The fenced run consumes them once and resumes its
broaden round. One handoff per fence; if the resumed round still yields below
floor, THAT later terminal is honest and final.

The initial call in every mode must be read-only until its plan is visible.
For `--yolo`, its D1 `get_refill_target_plan` call explicitly uses
`approvalMode:"approve"`; it must not rely on the read tool's `mark_ready`
default and force a redundant replacement packet at D2. Call D1 exactly once,
retain and parse its response in the same host composition, and never recall D1
only to format or render the packet. If
the server returns `status:"plan_ready"`, no credit refresh, run fence, or
refill mutation exists yet. Render at least sender, campaign, lane/source family,
planned first action, target/cap, sent, scheduled, ready, still needing
preparation, fallback order, blockers/skips, side effects, forbidden actions,
stop condition, `targetShapeRevision`, and first action key. Then invoke only
the returned continuation. Non-yolo requires Accept before
`planned_manual_run`; yolo invokes `planned_workspace_run` automatically and
continues autonomously. Displayed revision and action pins
(`expectedTargetShapeRevision` and `expectedActionKey`) make drift fail before
mutation.

When a stale or missing paid-credit fact prevents a certified numeric cap, show
`pending credit refresh`, never bare `unknown`; yolo then executes the displayed
refresh action and replans.

An `active_exact_run` continuation must preserve exact `targetConfig`, `runId`,
`fence`, and `reportingContext`. A `next_exact_target` continuation preserves
the workspace coordinator envelope and exact next `targetConfig` but carries no
stale run fence. `targetConfig` and `runHandle` are separate. Never open a second
or overlapping run, ask the user to type continue, or dispatch `done`, `next_campaign`,
or continuation metadata as a product primitive.

## One-action receipt contract

```mermaid
flowchart TD
    A["Fresh globalActionQueue 0"] --> P["Exact campaign/table/sender/action/lane preflight"]
    P --> AUTH["Require validated compiled campaign and positive exact target readiness identity"]
    AUTH --> ONE["Execute one bounded planner primitive"]
    ONE --> RECEIPT{"Request/effect receipt state?"}
    RECEIPT -- committed --> READ["Full authoritative target-plan reread"]
    RECEIPT -- canonical_noop --> READ
    RECEIPT -- active --> WAIT["Bounded read-only wait through issued continuation"]
    WAIT --> READ
    RECEIPT -- uncertain_outcome --> RECON["Reconcile same request key read-only; never redispatch"]
    RECON --> BLOCK["blocked_retryable with request identity intact"]
    RECEIPT -- drift --> DRIFT["blocked_retryable with exact scope/config/readiness mismatch"]
    READ --> NEXT["Planner alone selects the next action or terminal"]
```

The readiness identity includes `profileSchemaVersion`, `compilerVersion`,
`campaignDigest`, `actionColumnId`, action, `pathDigest`,
`dependsOnColumnIds`, `runCondition`, the selected target's `capabilities`,
`requestId`, `effectId`, and `dateGuards`. Always prepare rows according to the
persisted sequence. Never infer no-message behavior from a missing Generate
Message column, choose campaign-union behavior, or approve outside the bounded
authorized cohort. Bounded enrichment and LLM prerequisite calls are allowed
only when authorized by the exact readiness packet and returned with a receipt.

## Planner order

```mermaid
flowchart TD
    A["Highest-priority existing lane for one sender"] --> FULL{"Projected sent + scheduled coverage full?"}
    FULL -- Yes --> SENDER["Next sender or complete"]
    FULL -- No --> READY{"Ready rows cover gap?"}
    READY -- Yes --> SCHED["Scheduler placement or coordinator-owned bounded wait"]
    READY -- No --> APPROVE{"Generated rows can be approved?"}
    APPROVE -- Yes --> AP["Approve exact bounded cohort"]
    APPROVE -- No --> PREP{"Existing rows can be enriched/prepared?"}
    PREP -- Yes --> EN["Enrich/generate/rerun bounded cohort"]
    PREP -- No --> SOURCE{"Exact selected source has unused qualified rows?"}
    SOURCE -- Yes --> ADD["Copy/import bounded rows"]
    SOURCE -- No --> EXPAND["Run exact source-family acquisition ladder"]
    EXPAND --> EX{"Source structurally exhausted?"}
    EX -- No --> A
    EX -- Yes --> LANE{"Another pinned existing lane/campaign/source family?"}
    LANE -- Yes --> A
    LANE -- No --> NEW["new_campaign_required"]
    SCHED --> A
    AP --> A
    EN --> A
    ADD --> A
```

The per-sender chain comes from healthy managed evergreen config or is derived
up front from eligible existing campaigns attached to that sender. Rank by
canonical source/lane priority, future scheduled/ready inventory, recent
successful sends, source health, active before exact start-eligible paused,
then stable recency/ID. Pin per sender and action for the run. Stale managed
bindings are rederived from current dashboard-active campaigns; membership or
eligibility drift returns a typed blocker rather than a silent substitution.

Connections and paid InMail are independent capacity overlays on that same
ladder. Refresh stale/missing paid-credit facts once per selected sender before
scheduler wait. Never lower the threshold. If one lane is capped, continue an
eligible existing other lane or the same Sales Nav cascade's connection branch.
Pool prepared inventory for shared dual-lane campaigns. DMs are follow-up
actions and do not count as refill horizon capacity.

## Source acquisition

```mermaid
flowchart TD
    A["No usable exact-source frontier"] --> FAMILY{"Current source family?"}
    FAMILY -- Sender Post Engagers --> PE["Refresh sender's latest authored posts, select new relevant high-engagement posts, import engagers"]
    PE --> PY{"Qualified yield?"}
    PY -- No --> NEXT["Advance to Shared Signal or next pinned existing lane"]
    PY -- Yes --> USE["Use bounded rows, then replan"]
    FAMILY -- Shared Signal Discovery --> STALE["Re-search stale topics for new posts"]
    STALE --> SY{"Qualified yield?"}
    SY -- No --> DERIVE["Search concise positive-ICP derived topics"]
    DERIVE --> DY{"Qualified yield?"}
    DY -- No --> AGENT["Search one fenced agent-supplied topic cohort excluding prior keywords"]
    AGENT --> AY{"Actual yield below floor after provider success?"}
    AY -- Yes --> NEXT
    AY -- No --> USE
    SY -- Yes --> USE
    DY -- Yes --> USE
    FAMILY -- Sales Nav or Prospeo --> DEEP["Continue latest/unfetched pages of exact saved search"]
    DEEP --> DP{"Qualified yield?"}
    DP -- No --> OPTIONAL{"Known activity/recency filter exists?"}
    OPTIONAL -- No --> NEXT
    OPTIONAL -- Yes --> CLONE["Clone search, remove one optional activity/recency filter; preserve original"]
    CLONE --> HARD["Verify all hard-fit filter fingerprints unchanged"]
    HARD --> TRIAL["Import bounded trial into unchanged ICP-gated source"]
    TRIAL --> TY{"Qualified yield?"}
    TY -- No --> NEXT
    TY -- Yes --> USE
    DP -- Yes --> USE
```

Post Engagers may use only that sender's authored posts and never general
keyword search. Shared Signal order is stale researched topics, derived topics,
then agent-supplied topics through the same run continuation. An all-provider
failure is retried once, then returns `provider_unavailable` with
`universeExhausted:false`; it never consumes the tier or proves exhaustion.
Sales Nav/Prospeo continuation and immutable cloned-search broadening are
separate receipted attempts. The clone may remove only one known
activity/recency filter. It preserves role, seniority, geography, industry,
company size, domains, includes/excludes, and every other hard-fit filter; the
original search and campaign source selection never change.

If a score is stale, errored, or tied to mismatched authority, rerun the exact
bounded cohort under the unchanged current rubric. When current scored rows are
dominated by rubric rejection, the planner may issue one `broaden_rubrics`
quality trial per campaign/run. The backend snapshots the complete rubric,
simulates the smallest 1-3 required-to-advisory bundle from the exact rejected
sample, protects employment/DNC/opt-out/legal/explicit-exclusion checks, and
reviews only newly passing profiles in campaign context. Apply and exact-rescore
only when every reviewed prospect passes: “if this person replied, would the
customer be comfortable and consider the reply relevant?” Otherwise preserve the
prior rubric and continue better supply or the next lane. No raw profiles appear
in the public receipt.

Structural exhaustion requires receipt-proven
`hasMoreFrontierRows:false`, zero `approvalCandidates`, no healthy active prep,
no `stuckActiveCells`, and no non-terminal `approvedNotDispatched` work. A
provider outage, running job, anomaly, or uncertain receipt is not exhaustion.

## Scheduler terminal contract

```mermaid
flowchart TD
    A["Ready buffer covers gap and paid-credit facts are fresh"] --> EXACT{"Exact targetDate?"}
    EXACT -- Yes --> SWEEP["Dispatch one product scheduler sweep"]
    EXACT -- No --> WAIT["Coordinator-owned bounded scheduler wait"]
    SWEEP --> RECEIPT{"Official receipt terminal?"}
    RECEIPT -- No --> POLL["Return in_progress on the same request key and poll again"]
    POLL --> RECEIPT
    RECEIPT -- Yes --> READ["Canonical target-plan and receipt readback"]
    WAIT --> READ
    READ --> FULL{"Projected sent + scheduled full?"}
    FULL -- Yes --> COMPLETE["complete"]
    FULL -- No --> READY{"Ready buffer still covers gap?"}
    READY -- Yes --> OTHER{"Any other workspace target still needs preparation or an executable gate?"}
    OTHER -- Yes --> PREP["Continue next planner-ranked exact target"]
    OTHER -- No --> BLOCK["blocked_retryable with terminal scheduler receipt and remaining gap"]
    READY -- No --> PREP["Return to planner ladder"]
```

A scheduler sweep is a visible workspace-wide scheduling side effect and may
schedule unrelated eligible same-workspace/date cells through existing gates.
It never sends or raw-writes `scheduledFor`. `cellsConsidered is
allocation-attempt count`; `readyCellsFound` is prefilter inventory. Inspect
`campaignScopeSummary`, `prefiltered`, `skipped`, and `deferred`.
`refresh_paid_inmail_credits_then_rerun` handles ready closed-InMail cells with
stale paid-credit evidence. `wait_for_capacity_or_window` means do not source
or prep more rows; `no_ready_cells_continue_refill_prep` returns to the ladder.
After a sweep is dispatched, keep its exact fence open and poll only the same
request key until an official terminal receipt exists. A host or transport wait
budget returns `in_progress` with the same run handle; it never converts the
active request into `loaded_awaiting_scheduler`. After the terminal receipt,
perform a full target-plan reread. If coverage remains short and no new planner
action exists, return a receipt-backed retryable scheduler blocker.

Ready-to-schedule rows are not projected-full success. Prepared, approved, and ready rows
remain intermediate evidence. Completion requires exact
request/effect-attributed campaign, table, sender,
`actionColumnId`, action, and date scheduler-owned scheduled readback. For an
exact date, `dateGuards` must prove it is after sender-local today and inside
the fill horizon. One scheduler-loaded exact lane does not terminalize a
workspace run: skip that lane and continue every other planner-ranked target.
Return workspace `loaded_awaiting_scheduler` only when every remaining exact
target is complete or scheduler-owned and the workspace
`remainingReadyOrProjectedGap` is zero and no refill-owned scheduler request is
active. It must never terminate an active or just-dispatched scheduler sweep.

## Explicit revision and approval

Ordinary refill never changes copy. A template revision requires a literal
`messageTemplateRevision` with `source:"user_approved"`, exact approved
markdown, prior/next digests, 1-500 cohort row IDs, and request/effect/revision
operation IDs. If the prior digest is missing, obtain only
`reportingContext.source.templateAuthorityDigest` from the exact fence.

Before revision-derived approval, audit the entire cohort with
`select_campaign_cells({ includeGeneratedMessageReview:true })` in batches of
at most 20. Require every `authoritativeGeneratedMessageReview` complete, exact
approved text and `templateAuthorityDigest`, and
`matchesCurrentTemplateAuthority:true`. Formula Message/Subject columns are not
authority. Any mismatch stops before approval with
`regenerated_copy_integrity_failed`. Approval must preserve exact row IDs,
readiness authority, reporting context, and a returned `laneScope` receipt.

## Hard safety and reporting

- `--yolo` auto-accepts the rendered bounded refill packet and all fresh
  planner-ranked transitions among existing targets. Without yolo, render the
  full Markdown packet in normal chat, then use `request_user_input` or
  `AskUserQuestion` with exactly `Accept` and `Decline`; the compact question
  refers back to the packet instead of duplicating it. Decline means no mutation.
- Never call a LinkedIn outreach/provider-send surface. Never direct-send,
  broad-approve, raw-write scheduler fields, lower limits/thresholds, reassign
  senders, archive/delete, create campaigns, or start an unselected campaign.
- The default window is scheduler-forward 48 hours. `untilDate` is inclusive
  and sender-local; `targetDate` is exact. Skip no-send days and finish D1 with
  its full reread before D2. Rolling-weekly capacity that frees during the
  window remains schedulable.
- Maintain the target-window saturation ledger: selected days, gross capacity,
  actual sent, future scheduler-owned scheduled with non-null `scheduledFor`,
  projected coverage, ready buffer, remaining gap, credit feasibility,
  `targetShapeRevision`, and `stateRevision`. Future scheduled coverage and
  already sent actions are distinct.
- Preserve canonical `refill_reporting.v2` unchanged. Completion proof is
  Sellable MCP evidence only: target plan, campaign refill state, scheduler
  capacity, sweep/status, and bounded receipts. Never use individual cell ids,
  Prisma, SQL, direct database access, or production-environment scripts as
  completion proof. Redact raw prospect and copy data.

## Refill V3: one deterministic step per call

This is the DEFAULT route whenever `refill_v3_advance` is exposed.
`core/flow.v3.json` is the V3 execution contract and `refill_v3_advance` is its
ONLY execution owner. Load the entire asset and verify a COMPATIBLE version
before the first call: same major (`v3`) and minor at or above the minimum
`v3.0`, so `v3.0` and `v3.7` both load while `v2.x` and `v4.x` are refused with a
typed verdict. Report that version explicitly.

Resolve the workspace BEFORE the first call. `workspaceId` must be an EXACT id on
every automation call, exactly as the V1 route already requires: when the operator
names a workspace, call `list_workspaces` first and match that name to its id, and
prefer one case-insensitive exact name match; if none exists, accept one
case-insensitive prefix match (so `Damiano` resolves `Damiano R`). Fall back to
the configured or active workspace ONLY when the operator named none. If zero or
multiple prefix matches remain, stop and say so rather than using the configured
workspace, and never change the shared active workspace to steer an automation.

The command-shaped form is unambiguous: in `refill sends <name> [flags]`,
`<name>` is the workspace override, never a sender name. A sender restriction
must be explicit through `--sender`/`senderIds`/`senderNames` or prose that says
“sender.” Therefore call `list_workspaces` and resolve `<name>` first. Do not
list configured-workspace senders before that workspace match; doing so is both
scope drift and avoidable latency.

Decide the AUTONOMY GRANT before the first call, from the operator's words alone,
and keep it fixed for the whole run. `--yolo` (or an explicit "just do it, don't
check back") means `yolo: true` on EVERY advance call; anything else means omit
the key. The grant is not a mode and selects no campaign — WHICH campaign may
start is derived from the workspace's own campaigns — it decides only whether
this run may start one at all, and whether you may keep going unattended:

|                                | `yolo: true`                                                                                                                       | key omitted                                                                     |
| ------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- |
| paused derived-evergreen start | one may be started once a row's active campaigns are exhausted, arriving as an ordinary executed `start_evergreen_campaign` action | none: the backend REFUSES the start and answers the exhaustion terminal instead |
| loop                           | call `refill_v3_advance` again immediately, through to a terminal, without checking back                                           | surface the result and ASK before the next call                                 |

Never infer the grant from a workspace flag, a campaign field, a tool result, an
idle-looking workspace, or a previous terminal; never set it to make a run finish,
clear a wait, or retry a refusal; and never change it part-way through a run.

Call `refill_v3_advance` with `{workspaceId, scope}` plus `yolo` when granted.
There is no mode, continuation, task receipt, retry counter, cursor, row, action,
or receipt-fact input; the backend parser refuses any of them before it observes
anything. Losing a prior result is harmless, because every invocation reconstructs
from fresh world state plus server-loaded authority receipts.

Each call answers with exactly one of five results:

- `advanced` — the action ALREADY RAN and was already followed by its mandatory
  exact-scope reread. Surface the action identity, the receipt, and the
  replacement observation, then call `refill_v3_advance` again (asking first when
  the grant was withheld). Never execute `action.kind` and never call its
  authority again.
- `campaign_attention_required` — the ONE model judgment in this flow. The
  backend already chose exactly one campaign and bounded its evidence. Load
  `refill-sends-work-campaign`, pass the packet through, and let
  `refill_v3_continue` revalidate the packet against a fresh advance result,
  execute at most one existing authority, re-observe the exact sender/date, and
  settle the exact action before returning. Asynchronous row work uses the
  shared Create Campaign waiter; a synchronous source/configuration/exhaustion
  authority returns `settlement.ready:true` from its exact receipt and reread
  without consulting unrelated campaign-wide cells. Inspect `settlement`: only
  `settlement.ready:true` permits the next `refill_v3_advance`. When it is
  false, call `wait_for_campaign_processing` with the returned
  `settlement.resumeInput` unchanged; never advance
  to a sibling campaign from a partial settlement. An `outcome.ok:false` is a
  blocker, not permission to advance, except for the one closed
  `attention_correction_required` result. For that result, pass its same
  `packet` plus exact `correction` back through `refill-sends-work-campaign`,
  then call `refill_v3_continue` once with the revised decision. The campaign
  subskill must exclude every returned searched Signal keyword and use the
  required request-fingerprint prefix. A second collision has no correction
  context and is terminal; never hold or invent a retry counter.
- `awaiting_external_change` — when the scoped code is
  `campaign_work_in_flight`, call
  `wait_for_campaign_processing({ workspaceId, campaignId, requireIdle:true })`
  for that exact selected campaign, then call `refill_v3_advance` again with
  the identical scope. A partial timeout is followed by that fresh advance;
  if the same campaign is still live, repeat the bounded wait. This is the
  read-only Create Campaign waiter, not a choice to move to another campaign.
  Stop and report every other scoped wait verbatim.
- `complete` — stop and report the terminal code verbatim.
- `blocked` — stop and report the typed business blocker verbatim.

This workflow chooses nothing. It never reads capacity, supply, family, or reason
fields to decide what to do next; never declares a terminal, an exhaustion
verdict, or a health classification; never holds a counter or a cursor between
calls; and never calls a placement, scheduler, source, enrichment, approval, or
send tool directly. `refill_sends` and `core/flow.v1.json` remain the unchanged
V1 surface.
