---
name: refill-sends
description: Refill projected sends across a workspace or selected senders through the canonical fenced coordinator.
visibility: public
allowed-tools:
  - mcp__sellable__refill_sends
  - mcp__sellable__refill_v3_advance
  - mcp__sellable__refill_v3_world_state
  - mcp__sellable__refill_v3_continue
  - mcp__sellable__get_campaigns
  - mcp__sellable__get_refill_target_plan
  - mcp__sellable__get_scheduler_fill_capacity
  - mcp__sellable__get_subskill_asset
  - mcp__sellable__get_auth_status
  - mcp__sellable__start_cli_login
  - mcp__sellable__wait_for_cli_login
  - mcp__sellable__get_active_workspace
  - mcp__sellable__list_workspaces
  - mcp__sellable__get_subskill_prompt
  - mcp__sellable__search_subskill_prompts
  - mcp__sellable__list_senders
  - mcp__sellable__select_campaign_cells
  - mcp__sellable__wait_for_campaign_processing
---

# Refill Sends

Use this skill for “fill”, “refill sends”, “max out sends”, “load everyone up”,
or “fill horizon sends”. Exactly one coordinator owns execution: `refill_v3_advance`
on the V3 route, `refill_sends` on the V1 route. The host resolves the request,
invokes that coordinator, and follows its exact continuation; it never
reconstructs the refill ladder from low-level tools.

Host commands:

- Claude Code: `/sellable:refill-sends`
- Codex: `$sellable:refill-sends`

## Route selection — do this FIRST

Decide the route before parsing scope, before loading any flow asset, and before
any tool call other than auth/workspace resolution. The predicate is
MECHANICAL and 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. Go to [Refill V3](#refill-v3) and follow it;
  the V1 ladder in the rest of this document does not apply.
- **No → take the V1 route.** The installed server predates Refill V3, so
  `refill_sends` is the only coordinator available. Follow this document from
  [Entry and exact scope](#entry-and-exact-scope) onward.

Never infer the route from a workspace flag, a campaign field, a tool result, or
the operator's wording: no such cutover signal exists, and guessing at one is
what makes an ordinary refill request silently take the wrong route. Report the
selected route explicitly before the first coordinator call.

## Entry and exact scope

Everything from here down to [Refill V3](#refill-v3) is the **V1 route**. Skip it
entirely when route selection chose V3. On this route `refill_sends` is the only
execution owner and the host follows its exact continuation; it never
reconstructs the refill ladder from low-level tools.

Accepted request fields are `--yolo`, `workspaceId`, `--sender`/`senderIds`/
`senderNames`, `actionTypes`, `--until`/`untilDate`, `--target-date`/
`targetDate`, and compatibility `horizonSendDays`. Omitted dates mean the
scheduler-forward 48-hour window. `untilDate` is sender-local and inclusive;
`targetDate` is one sender-local scheduler-fillable date. Skip no-send days and
never extend beyond the requested date. Finish the full D1 reread before D2.
Ordinary refill defaults to `yolo:true`; only an explicit `yolo:false` or a
clear request for review-first/manual execution opts out.

```text
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 })
```

```mermaid
flowchart TD
    A["Parse the operator request"] --> AUTH{"Sellable auth valid?"}
    AUTH -- No --> LOGIN["Run the returned login flow and wait"]
    LOGIN --> AUTH
    AUTH -- Yes --> WS["Resolve exact workspace by ID, exact name, or install mapping"]
    WS --> WSG{"One accessible workspace match?"}
    WSG -- No --> WB["Output: blocked_retryable / WORKSPACE_REQUIRED or workspace_ambiguous"]
    WSG -- Yes --> SENDERS["Resolve each sender inside that workspace by ID, exact name, or unambiguous prefix"]
    SENDERS --> SG{"Every selector resolves exactly?"}
    SG -- No --> SB["Output: blocked_retryable / sender_selector_unresolved with candidates"]
    SG -- Yes --> DATE["Normalize 48h, exact targetDate, or inclusive untilDate in sender-local time"]
    DATE --> ROUTE{"Does this server expose refill_v3_advance?"}
    ROUTE -- Yes --> V3["Refill V3 route: load core/flow.v3.json and call refill_v3_advance"]
    ROUTE -- No --> D1["D1: call get_refill_target_plan read-only with exact scope"]
    D1 --> RENDER["Render sender/campaign waterfall, coverage ledger, first action, side effects, forbidden actions, and stop condition"]
    RENDER --> D2["D2: call refill_sends with workspaceCoordinator true plus displayed targetShapeRevision and actionKey"]
```

Scheduled and `--yolo` execution require an explicit `workspaceId` on every
call. Never change the shared active workspace to control an automation, never
fall back to its full fleet, and never mutate the workspace that merely happens
to be active. If the native tool is unavailable, use only the host’s exact
configured Sellable MCP transport; otherwise stop with `sellable_mcp_unavailable`
instead of manually reproducing the workflow.

Before product work, load the closed workflow asset completely and verify it:

```text
get_subskill_prompt({ subskillName: "refill-sends-workflow" })
get_subskill_asset({ subskillName: "refill-sends-workflow", assetPath: "core/flow.v1.json" })
```

Continue chunks until `hasMore:false`; require `workflow:"refill-sends-workflow"`
and a COMPATIBLE version: same major (`v1`) and minor at or above the minimum
`v1.9` — so `v1.9`, `v1.10` pass while `v1.8` and `v2.x` do not. Report the
verified version before D1. A missing, unparseable, wrong-major, or
below-minimum version is `workflow_version_mismatch`: stop before
`get_refill_target_plan` or mutation. Do NOT require exact string equality: the
flow asset ships in the npm package while this skill is installed separately,
so an exact pin makes every additive contract change a breaking one.
Route intent is immutable: ordinary refill uses `intent:"auto"` from first plan
through terminal; explicit `yolo:false` uses the review-first path.

## Mandatory visible plan before execution

Every refill in every mode begins with D1, a read-only
`get_refill_target_plan` with the exact workspace, sender/action selectors, and
date envelope. For default yolo execution, the first D1 call must explicitly pass
`approvalMode:"approve"`; never rely on the planner's `mark_ready` default and
then discover the scope mismatch at D2. Invoke D1 exactly once, retain and parse
that response in the same host composition, and never call D1 again only to
format or render it. Render its plan in normal chat before any mutation. At minimum,
show one row per selected sender/campaign lane with sender, campaign, lane/source
family, planned first action, target/cap, sent, scheduled, ready, still needing
preparation, fallback order, and blocker/skip reason. Then show the bounded side
effects, forbidden actions, stop condition, `targetShapeRevision`, and first
`globalActionQueue[0].actionKey`.

If a stale or missing paid-credit fact prevents a certified fillable cap, show
`pending credit refresh`, never bare `unknown`. The target remains visible and
yolo executes the displayed refresh action before replanning the numeric cap.

After rendering D1, explicit `yolo:false` asks Accept or Decline and invokes
only the returned `planned_manual_run` after Accept. Default `yolo:true` does
not ask: call
`refill_sends` D2 with `workspaceCoordinator:true`, the identical scope, and
both `expectedTargetShapeRevision` and `expectedActionKey` copied from the
displayed packet. The server may itself return `status:"plan_ready"`; render
that returned plan before invoking `planned_manual_run` or
`planned_workspace_run`. Thus the modes share the exact same visible plan and
revision fence. The only difference is that non-yolo pauses for decisions while
`--yolo` auto-accepts the displayed packet and autonomously traverses every
planner-ranked existing campaign, lane, source, safe rubric trial, and scheduler
handoff until terminal. If either pin drifts, render the fresh read-only
replacement packet; do not mutate from the stale plan. No credit refresh, fence,
or product mutation may exist before this render.

## Coordinator lifecycle

```mermaid
flowchart TD
    A["Fresh read-only workspace refill plan rendered to the user"] --> FULL{"Projected coverage sent + scheduled is full?"}
    FULL -- Yes --> COMPLETE["Terminal: complete"]
    FULL -- No --> ACTIVE{"Active exact fenced run exists?"}
    ACTIVE -- Yes --> RESUME["Resume only issued runId, fence, targetConfig, reportingContext"]
    ACTIVE -- No --> PICK["Select globalActionQueue 0"]
    PICK --> PREFLIGHT["Exact campaign, table, sender, action, lane preflight"]
    PREFLIGHT --> ACT["Execute one bounded planner action"]
    RESUME --> ACT
    ACT --> OUTCOME{"Canonical outcome?"}
    OUTCOME -- "Committed or canonical no-op" --> A
    OUTCOME -- "Still running" --> WAIT["Bounded read-only wait or receipt reconciliation"]
    WAIT --> A
    OUTCOME -- "Source exhausted" --> NEXT{"Another planner-ranked existing target?"}
    NEXT -- Yes --> HANDOFF["Terminalize exact fence as next_exact_target"]
    HANDOFF --> A
    NEXT -- No --> NC["Terminal: new_campaign_required; ask whether to create it"]
    OUTCOME -- "Provider unavailable after one retry" --> RETRY["Terminal: blocked_retryable; universeExhausted false"]
    OUTCOME -- "Scope, config, readiness, or receipt drift" --> BLOCK["Terminal: blocked_retryable with exact blocker"]
    OUTCOME -- "Planner marker done" --> DONE["Terminalize marker; never dispatch it"]
```

If `status:"plan_ready"`, render its full plan packet first. Non-yolo asks for
Accept or Decline before `planned_manual_run`; `--yolo` invokes
`planned_workspace_run` automatically. If `status:"in_progress"`, invoke only the returned
`continuation.arguments`. `targetConfig` and `runHandle` are separate and remain
unchanged except for a server-issued takeover fence. `active_exact_run` resumes
that fence; `next_exact_target` carries no stale run fence. Never open a second
or overlapping run, ask the user to type “continue”, redispatch a control marker, or
turn `done`, `next_campaign`, or a continuation object into a product action.

## Per-sender campaign and lane waterfall

Use managed evergreen order when healthy. Otherwise build the same waterfall
on the fly from existing eligible campaigns the sender is already attached to.
This makes regular and evergreen campaigns differ only in where ordering comes
from, not in how refill proceeds.

```mermaid
flowchart TD
    A["All existing campaign-backed sequences attached to one sender"] --> FILTER["Exclude wrong workspace/sender, archived, completed, direct, disconnected, or sequence-invalid targets"]
    FILTER --> STALE{"Managed waterfall complete and live?"}
    STALE -- Yes --> MANAGED["Use configured priority order"]
    STALE -- No --> DERIVE["Derive deterministic order from dashboard-active campaigns"]
    DERIVE --> P1["Prefer canonical source/lane priority"]
    P1 --> P2["Then future scheduled and ready inventory"]
    P2 --> P3["Then recent successful sends and source health"]
    P3 --> P4["Then active before exact start-eligible paused, recency, stable ID"]
    MANAGED --> PIN["Pin exact per-sender and per-action lane chain for this run"]
    P4 --> PIN
    PIN --> LANE["Select highest-priority non-exhausted lane"]
    LANE --> REFILL["Run row lifecycle"]
    REFILL --> EX{"Lane structurally exhausted?"}
    EX -- No --> REFILL
    EX -- Yes --> MORE{"Another pinned existing lane or source family?"}
    MORE -- Yes --> LANE
    MORE -- No --> END["Output: lanes_exhausted, then new_campaign_required if coverage still short"]
```

Campaign enrollment/removal, missing pinned targets, or loss of eligibility is
typed scope drift; a fresh planner may rederive, but the host cannot silently
substitute a target. A current dashboard-active `PAUSED` campaign is eligible
only when the planner names that exact campaign as start-eligible. Starting it
may let the product scheduler schedule/send approved sequence actions and must
be reported. DMs are follow-ups, not refill horizon targets. Mixed sequences
keep distinct exact target paths; do not choose campaign-union behavior.

## Connection and InMail capacity overlay

```mermaid
flowchart TD
    S["Sender capacity and exact selected days"] --> C{"Connection slots available now or later in rolling week?"}
    S --> F{"Paid-InMail credit facts fresh?"}
    F -- No --> RF["Refresh exact selected sender once per run, then replan"]
    RF --> P{"Credits meet existing threshold, normally 5?"}
    F -- Yes --> P
    C -- Yes --> INV["Enable existing invite lanes"]
    C -- No --> INVCAP["Cap invite lane with timing evidence"]
    P -- Yes --> MAIL["Enable existing paid-InMail or cascade lane"]
    P -- No --> MAILCAP["Skip paid lane; never lower threshold"]
    INV --> UNION["Rank enabled existing lanes per sender"]
    MAIL --> UNION
    INVCAP --> ALT{"Paid lane enabled?"}
    MAILCAP --> ALT2{"Invite or same-campaign connection fallback enabled?"}
    ALT -- Yes --> UNION
    ALT -- No --> CAP["Output: blocked_retryable / capacity or window"]
    ALT2 -- Yes --> UNION
    ALT2 -- No --> CAP
    UNION --> POOL["Pool shared-campaign prepared inventory; never double-enrich"]
```

`rollingWeeklyInvite.capacityFreedDuringWindow:true` means later capacity is
schedulable; use its timing fields rather than treating the opening gate as a
full-day blocker. Credit freshness precedes scheduler wait. Fresh facts below
threshold authorize only an already-eligible existing connection fallback or
another existing campaign—not a threshold change or new campaign.

## Row lifecycle

For every selected lane, use the smallest action that can reduce its gap.

```mermaid
flowchart TD
    A["Lane still has projected gap"] --> ACTIVE{"Active import, preparation, or stuck work?"}
    ACTIVE -- "Healthy active" --> WAIT["Bounded read-only wait; honor wait.deadlineAt"]
    WAIT --> A
    ACTIVE -- "Stuck or anomalous" --> REPAIR["Run only planner-bounded repairable cells or return blocker"]
    REPAIR --> A
    ACTIVE -- No --> APPROVE{"Generated rows can be approved?"}
    APPROVE -- Yes --> AP["Approve exact bounded cohort with readiness authority"]
    AP --> REPLAN["Record receipt and full authoritative replan"]
    APPROVE -- No --> ENRICH{"Existing rows can be enriched or prepared?"}
    ENRICH -- Yes --> PREP["Enrich, generate, or rerun exact bounded cohort"]
    PREP --> REPLAN
    ENRICH -- No --> SOURCE{"Selected source can add qualified rows?"}
    SOURCE -- Yes --> ADD["Copy/import bounded rows from exact selected source"]
    ADD --> REPLAN
    SOURCE -- No --> EXPAND["Run source-family acquisition flow"]
    EXPAND --> REPLAN
    REPLAN --> A
```

Structural exhaustion requires receipt-proven
`hasMoreFrontierRows:false`, zero `approvalCandidates`, no fresh active prep,
no `stuckActiveCells`, and no non-terminal `approvedNotDispatched` work. A dry
preparation receipt is run-scoped and prevents the identical no-op from
repeating. `USER_ADDED_ROWS_LIMIT_EXCEEDED` may use a bounded same-source split
through `load_csv_linkedin_leads` into the exact review batch; a true campaign
table hard cap returns a capacity blocker and never deletes rows.

## Sender Post Engagers acquisition

```mermaid
flowchart TD
    A["Sender Post Engagers lane needs rows"] --> UNUSED{"Unused selected post/engager rows exist?"}
    UNUSED -- Yes --> USE["Import/copy bounded unused rows"]
    UNUSED -- No --> REFRESH["Refresh this sender's latest recent authored posts"]
    REFRESH --> SELECT["Select new unprocessed relevant high-engagement posts"]
    SELECT --> FETCH["Fetch/import their engagers and dedupe"]
    FETCH --> YIELD{"Usable qualified yield?"}
    YIELD -- Yes --> USE
    YIELD -- No --> EX["Exhaust only this Post Engagers lane"]
    EX --> NEXT["Advance to Shared Signal Discovery or next pinned existing lane"]
```

The campaign must be sender-owned: exactly that sender, authored posts only.
Never invent keywords, search third-party posts, or run general Signal Discovery
against the Post Engagers campaign.

## Shared Signal Discovery acquisition

```mermaid
flowchart TD
    A["Shared Signal lane needs rows"] --> UNUSED{"Unused selected rows or unprocessed posts exist?"}
    UNUSED -- Yes --> USE["Import/copy bounded rows"]
    UNUSED -- No --> STALE["Re-search stale existing topics for newly published posts"]
    STALE --> SY{"Projected qualified yield meets min remaining gap or 100?"}
    SY -- Yes --> USE
    SY -- No --> DERIVE["Derive up to five concise positive-ICP topics from approved campaign intent"]
    DERIVE --> DY{"Projected qualified yield meets floor?"}
    DY -- Yes --> USE
    DY -- No --> AGENT["Request 3-5 new agent-supplied topics through the same fenced continuation"]
    AGENT --> SEARCH["Search once, excluding searchedKeywords, then project actual returned candidates"]
    SEARCH --> PROVIDER{"All LinkedIn providers failed?"}
    PROVIDER -- Yes --> RETRY["Retry the same bounded search once"]
    RETRY --> RECOVER{"Provider recovered?"}
    RECOVER -- No --> BLOCK["Output: blocked_retryable / provider_unavailable; universeExhausted false"]
    RECOVER -- Yes --> AY{"Actual qualified yield meets floor?"}
    PROVIDER -- No --> AY
    AY -- Yes --> USE
    AY -- No --> EX["Receipt-proven signal_yield_below_floor"]
    EX --> NEXT["Advance to next pinned existing lane"]
```

Provider outage never consumes a keyword tier or proves exhaustion. Temporary
derived search state is run-scoped; later runs may rediscover new posts. Agent
keywords are supplied only through the exact run continuation, never by user
steering or a second run.

## Sales Nav and Prospeo acquisition

```mermaid
flowchart TD
    A["Cold-provider lane needs rows"] --> UNUSED{"Unused exact-source rows exist?"}
    UNUSED -- Yes --> USE["Import/copy bounded rows"]
    UNUSED -- No --> DEEP["Continue latest/unfetched pages of exact saved Sales Nav or Prospeo search"]
    DEEP --> DY{"Qualified yield?"}
    DY -- Yes --> USE
    DY -- No --> OPTIONAL{"Known activity/recency filter exists?"}
    OPTIONAL -- No --> NEXT["Advance to next pinned existing lane"]
    OPTIONAL -- Yes --> CLONE["Clone exact approved search; remove one optional activity/recency filter"]
    CLONE --> PROOF{"Original unchanged and every hard-fit filter fingerprint preserved?"}
    PROOF -- No --> BLOCK["Output: blocked_retryable / provider_search_drift"]
    PROOF -- Yes --> TRIAL["Import bounded trial candidates into existing gated source"]
    TRIAL --> GATE["Run current ICP, exclusion, DNC, persona, geography, and company-fit gates"]
    GATE --> GY{"Qualified yield?"}
    GY -- Yes --> USE
    GY -- No --> QUARANTINE["Rejects remain unsendable; advance lane"]
    QUARANTINE --> NEXT
```

The original saved search is immutable. An automatic trial may remove only a
known activity/recency signal; role, seniority, geography, industry, company
size, domain, include/exclude, DNC, and every other hard-fit constraint remain
byte-for-byte. Imported candidates are not approvals: current campaign gates
quarantine bad fits. If rubric rejects dominate — including a receipt-proven
`low_yield` cohort below the 10% planning floor, not only zero-prepared — enter
the bounded sample-quality trial below before another source add. Do not repeat
a spent trial or switch outside planner order.

## Provider prompt before any new provider search

Draining an already-imported source list needs no provider context. But before
any action that runs a NEW provider search — a `broaden_signal_search` round
(especially when supplying `agentKeywords`), a Sales Nav or Prospeo cloned-
search broadening, or any source add that dispatches a fresh search — load the
matching specialized prompt first with
`get_provider_prompt({ provider, campaignOfferId })` and apply its discipline:
sample-first fit checks against the headline ICP criteria, the 10% planning
floor, sample math (target ÷ observed pass rate) for sizing, and the keyword-
quality guidance for Signal Discovery. Inventing broaden keywords or sizing a
scrape without the provider prompt in context repeats the blind-import failure
the sampling discipline exists to prevent. The search tools also enforce this
preflight server-side; loading it late wastes a fenced round on a typed refusal.

## Rubric sample-quality trial

Rubric rejection may mean the source is poor or that one or several required
checks are unnecessarily narrow. Decide from an exact sample, not pass-rate alone.

```mermaid
flowchart TD
    A["Rows fail ICP/rubric gate"] --> STALE{"Score stale, errored, or computed under mismatched authority?"}
    STALE -- Yes --> RESCORE["Rerun exact bounded cohort under unchanged current rubric"]
    RESCORE --> RESULT{"Now qualified?"}
    RESULT -- Yes --> CONTINUE["Continue row lifecycle"]
    RESULT -- No --> SAMPLE
    STALE -- No --> SAMPLE["Take exact representative rejected sample from prep receipt"]
    SAMPLE --> SNAP["Snapshot complete rubric and prior digest"]
    SNAP --> PROPOSE["Propose smallest 1-3 required-to-advisory bundle that creates useful new passes"]
    PROPOSE --> HARD{"Touches employment, DNC/opt-out, legal/compliance, or explicit exclusion?"}
    HARD -- Yes --> REJECT["Reject proposal; preserve complete prior rubric"]
    HARD -- No --> SIM["Simulate bundle from recorded per-prospect evaluations; no mutation"]
    SIM --> DELTA{"Any newly passing prospects?"}
    DELTA -- No --> REJECT
    DELTA -- Yes --> QUALITY["Review only newly passing profiles in campaign and customer context"]
    QUALITY --> REPLY{"If these prospects replied, would the customer be comfortable and consider the replies relevant?"}
    REPLY -- No --> REJECT
    REPLY -- Yes --> DRIFT{"Live rubric digest still equals prior digest?"}
    DRIFT -- No --> BLOCK["Output: blocked_retryable / rubric config drift"]
    DRIFT -- Yes --> APPLY["Guarded apply entire required-to-advisory bundle"]
    APPLY --> EXACT["Force exact bounded ICP rescore for accepted sample"]
    EXACT --> VERIFY{"Dispatch and accepted quality receipt valid?"}
    VERIFY -- Yes --> KEEP["Keep bundle; receipt before/after digests, changed checks, sample counts, and verdict"]
    VERIFY -- No --> ROLLBACK["Restore complete prior snapshot and receipt rollback"]
    REJECT --> BETTER["Keep rows unsendable; continue better supply or next existing lane"]
    ROLLBACK --> BETTER
```

`--yolo` may change more than one required rubric at a time only through this
single bounded reversible bundle. The change demotes selected required checks to
advisory scoring; it does not erase their evidence. The acceptance test is the
newly passing delta: if any reviewed prospect would make the customer unhappy,
surprised, or consider the reply irrelevant, keep the complete prior rubric and
continue the source waterfall. Never expose raw prospect profiles in the public
receipt.

## Scheduler and uncertain-receipt recovery

```mermaid
flowchart TD
    A["Ready buffer covers remaining projected gap"] --> CREDIT{"Every selected paid lane has fresh credit facts?"}
    CREDIT -- No --> RF["Refresh each exact selected sender once, then full replan"]
    RF --> A
    CREDIT -- Yes --> DATE{"Exact targetDate?"}
    DATE -- Yes --> SWEEP["Dispatch one request-scoped product scheduler sweep"]
    DATE -- No --> WAIT["Enter coordinator-owned bounded scheduler wait"]
    SWEEP --> RECEIPT{"Receipt state?"}
    RECEIPT -- Terminal --> READ["Canonical target-plan and request/effect readback"]
    RECEIPT -- Active --> POLL["Return in_progress and poll only the same request key"]
    RECEIPT -- uncertain_outcome --> RECON["Reconcile same request key read-only; never redispatch"]
    POLL --> RECEIPT
    RECON --> TERM["Terminal: blocked_retryable with request identity intact"]
    WAIT --> READ
    READ --> FULL{"Projected coverage full?"}
    FULL -- Yes --> COMPLETE["Terminal: 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["Terminal: receipt-backed blocked_retryable scheduler gap"]
    READY -- No --> PREP["Return to row lifecycle"]
```

A scheduler sweep is a visible workspace-wide scheduling side effect: existing
product gates may schedule unrelated eligible cells in that workspace/date.
It never sends directly or raw-writes scheduler fields. `cellsConsidered is
allocation-attempt count`; `readyCellsFound` is prefilter inventory. Inspect
`campaignScopeSummary`, `prefiltered`, `skipped`, and `deferred`. For ready
closed-InMail cells with stale paid-credit evidence, use
`refresh_paid_inmail_credits_then_rerun`. `wait_for_capacity_or_window` means do
not source or prep more rows; `no_ready_cells_continue_refill_prep` returns to
the row lifecycle. `cellsScheduled:0` alone is not failure. Once a sweep is
dispatched, a host wait budget yields an `in_progress` continuation carrying
the same run identity. Keep polling that request key until its official
terminal receipt, then reread the full target plan. Never convert an active
sweep into `loaded_awaiting_scheduler`.

## Explicit message-template revision

Ordinary refill never changes copy. Only a literal user-approved revision
envelope may enter this side path.

```mermaid
flowchart TD
    A["Literal messageTemplateRevision with approved markdown, digests, 1-500 row IDs, request/effect IDs"] --> DIGEST{"Current prior authority digest present?"}
    DIGEST -- No --> FENCE["Acquire exact run and read reportingContext source templateAuthorityDigest"]
    FENCE --> DG{"Digest returned?"}
    DG -- No --> BLOCK["Terminal: blocked_retryable / template_authority_missing"]
    DG -- Yes --> REVISE["Apply revision to exact cohort"]
    DIGEST -- Yes --> REVISE
    REVISE --> AUDIT["select_campaign_cells in batches <=20 with includeGeneratedMessageReview:true"]
    AUDIT --> MATCH{"Every authoritativeGeneratedMessageReview complete, exact text/digest, matchesCurrentTemplateAuthority true?"}
    MATCH -- No --> FAIL["Stop before approval: regenerated_copy_integrity_failed"]
    MATCH -- Yes --> APPROVE["Approve only exact rowIds using compiled readiness authority"]
    APPROVE --> RECEIPT{"Bounded preparation receipt includes laneScope?"}
    RECEIPT -- No --> FAIL
    RECEIPT -- Yes --> PLAN["Return to canonical refill plan"]
```

Formula Message/Subject columns, previews, exports, and counts are not copy
authority. `forceRerun:true` never implies a revision or approval.

## Manual approval and `--yolo`

```mermaid
flowchart TD
    A["Render fresh bounded packet in normal chat as Markdown"] --> MODE{"--yolo present?"}
    MODE -- Yes --> AUTO["Auto-accept all planner-ranked transitions among existing campaigns, lanes, sources, and safe refill primitives"]
    MODE -- No --> ASK["Open host-native structured question with exactly Accept and Decline"]
    ASK --> DECISION{"Operator choice?"}
    DECISION -- Accept --> RUN["Execute only rendered envelope"]
    DECISION -- Decline --> STOP["Stop with no mutation"]
    AUTO --> RUN
```

Use `request_user_input` in Codex and `AskUserQuestion` in Claude Code. The
full packet belongs in the normal chat message immediately before the question:
workspace, sender scope, campaign-by-campaign plan table, action, target/cap,
source, blocker/skip reason, exact IDs, expected side effects, forbidden actions,
and stop condition. The question body must be compact and refer back to the
posted packet instead of duplicating it. `--yolo` may switch among every fresh
planner-ranked existing campaign/lane/source family and start the exact selected
`PAUSED` campaign. It does not create campaigns, direct-send, archive/delete,
change sender limits or paid-InMail thresholds, reassign senders, or invent
source selection outside the planner.

## Typed outputs

```mermaid
flowchart TD
    A["Fresh canonical state"] --> C{"Terminal classification"}
    C -- "sent + scheduled meets target" --> COMPLETE["complete: report saturation ledger"]
    C -- "all remaining exact targets scheduler-owned and workspace ready-or-projected gap is zero" --> LOADED["loaded_awaiting_scheduler: report expected pickup and stop"]
    C -- "recoverable external, capacity, active-work, or receipt condition" --> BLOCKED["blocked_retryable: exact blocker, receipt, and safe retry condition"]
    C -- "current lane spent but another existing target remains" --> EX["lanes_exhausted internally; continue next_exact_target"]
    C -- "every existing campaign/lane/source spent or infeasible" --> NEW["new_campaign_required: propose missing shape and ask whether to create it"]
    C -- "scope/config/readiness changed" --> DRIFT["blocked_retryable: typed drift; never guess"]
```

One scheduler-loaded exact lane is not a workspace terminal: skip it and
continue other planner-ranked lanes. Workspace `loaded_awaiting_scheduler` is
admissible only when no refill-owned scheduler request is active. After refill
dispatches a sweep, keep the same fence alive until the official terminal
receipt; then return `complete` from the post-receipt readback or a concrete
receipt-backed retryable scheduler blocker if coverage remains short. The sole
refill-ladder expansion
that asks the user in `--yolo` is `new_campaign_required`; refill itself never
creates that campaign. `new_campaign_required` is also the truthful terminal
when receipts prove every existing lane is spent but the planner has no
automatic cold rung to propose (Signal Discovery workspaces): report the
remaining gap and ask the user; a future handler may automate the creation. A
`actionable_supply_not_queued` terminal means actionable row frontiers
(approval/generate/enrich/rubric or unprocessed frontier rows) still exist but
no executable exact edge surfaced: retry once with a fresh command, and if it
repeats report it as a planner-exposure defect instead of treating the
workspace as exhausted. That census counts CONVERTIBLE rows only, de-duplicated
per campaign/table lane — a frontier whose own preparation receipt carries a
non-retryable `icp_or_rubric_rejection` diagnosis is proven unable to convert,
so it never inflates the census. When such receipts are all that remain, the
terminal is `source_supply_exhausted_at_rubric`: a truthful source exhaustion,
not a defect and not retryable. Report the per-lane evidence from the note and
tell the user that widening the rubric or adding fresh source rows is the only
remaining fill path. Whenever a terminal reports a non-empty census, it also
names every exact edge the run refused (`Exact edges this run refused: ...`) —
surface those named blockers rather than describing the workspace as having no
available work.

## Non-negotiable authority and proof

- Before preparation, approval, or exact-date scheduling, require a validated
  compiled campaign and positive exact target readiness identity from the
  backend compiler: `profileSchemaVersion`, `compilerVersion`,
  `campaignDigest`, `actionColumnId`, action, `pathDigest`,
  `dependsOnColumnIds`, `runCondition`, the selected target's `capabilities`,
  `requestId`, `effectId`, and `dateGuards`. Never infer no-message behavior
  from a missing Generate Message column. Prepare rows according to the
  persisted sequence; bounded enrichment and LLM prerequisite calls are
  allowed only when authorized by the exact readiness packet and receipted.
- Refuse approval outside the bounded authorized cohort. The exact selected
  target path controls message, subject, comment, reaction, and row approval.
  For exact dates, require the date strictly after sender-local today and inside
  the fill horizon. Prepared, approved, and ready rows remain intermediate
  evidence; completion requires request/effect-attributed scheduler-owned
  scheduled readback.
- Execute one bounded primitive, then perform a full authoritative reread.
  Never call a LinkedIn outreach/provider-send surface from refill. No direct
  sends, raw scheduler writes, broad approval, threshold/limit changes,
  sender reassignment, destructive cleanup, or new campaign creation.
- Maintain the target-window saturation ledger per sender: selected days,
  gross capacity, actual sent, future scheduler-owned scheduled with non-null
  `scheduledFor`, projected coverage (`sent + scheduled`), ready buffer,
  remaining gap, paid-InMail feasibility, `targetShapeRevision`, and
  `stateRevision`. Future scheduled coverage and already sent actions are
  distinct.
- Carry canonical `refill_reporting.v2` unchanged through progress,
  continuation, replay, and terminal output. 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 copy and prospect fields.

## Refill V3

This is the DEFAULT route whenever `refill_v3_advance` is exposed (see
[Route selection](#route-selection--do-this-first)). `refill_v3_advance` is then
the only execution owner and `refill-sends-workflow` plus its `core/flow.v3.json`
asset is the contract to load — load the prompt and that asset, verify the `v3`
compatible range, and report the version before the first call. Start from the
base request `{workspaceId, scope}`, add `yolo:true` by default, and call it
again after each `advanced` or
settled `campaign_attention_required` result. A `run_step_pending` result means
the server is still completing the already-consumed step: wait its
`retryAfterMs`, then call advance with the same public inputs. The MCP retains
the identical token internally; never copy a token, route to AI, or execute an
authority for this transport-reconciliation wait. `refill_v3_continue` itself
settles the exact packet action before it returns. Bounded enrichment and an
already-active preparation job use the shared Create Campaign waiter and
durable preparation owner; directly queued row repair uses the campaign waiter.
Synchronous source, configuration, rubric, and exhaustion authorities already
return after their own exact reread, so they return `settlement.ready:true` from
that bounded authority receipt and never wait on unrelated campaign-wide
processing cells.

When an explicit `yolo:false` request names a campaign, resolve that visible name with one
authorized bounded `get_campaigns` call carrying an explicit `limit`. Require
exactly one case-insensitive exact match; if none or more than one match, stop
without preview or mutation. Retain only the returned stable campaign ID. Call
`refill_v3_world_state({workspaceId, scope, campaignId})`, render that exact
campaign's two-lane readiness and its explicit zero product side effects, then
stop at the product-native question with exactly Accept and Decline. Do not call
`refill_v3_advance` before Accept. Accept calls
`refill_v3_advance({workspaceId, scope, campaignId})`; Decline stops. Carry the
same campaignId through every advance, continue, replay, partial-wait, and
post-wait repeat. Display-name changes never alter this retained identity, and
no sibling campaign may replace it.
Call the next advance only when `settlement.ready` is true. When it is false,
call `wait_for_campaign_processing` with `settlement.resumeInput` **unchanged**;
that carries the exact `preparationJobId`, `requirePreparationTerminal:true`,
and `requireIdle:true`. Repeat the returned `resumeInput` until ready; never
fall back to a stats-only campaign wait for this settlement. An
`awaiting_external_change` result with
`deferral.code:"campaign_work_in_flight"` and a campaign id is a bounded
read-only continuation, not permission to walk the next campaign: call
`wait_for_campaign_processing({ workspaceId, campaignId, requireIdle:true })`,
then call `refill_v3_advance` again with the identical scope and yolo grant.
This reuses Create Campaign's campaign-table waiter and guarantees the same
campaign is freshly replanned after its cohort settles. If the bounded wait
returns a partial timeout, surface that checkpoint and make the fresh advance;
if it reports the same live campaign, repeat the bounded wait. Stop on every
other `awaiting_external_change`, `complete`, or `blocked` result. For each of
those terminal results, report **every** entry in `terminalEvidence.rows`, one
per scoped sender/date/lane—not only the headline deferral or blocker. Include
`targetSlots`, `takenSlots`, `remainingTargetGap`, `fillableSlotsNow`,
`blockedGap`, `terminalClassification`, `reasonCodes`, `approvedDelta`, and
`scheduledDelta`. When `reasonCodes` contains
`paid_inmail_credit_refresh_failed`, also report `paidCreditRefreshAttempt`
exactly: `attemptedAt`, `outcome`, `errorClass`, `errorDetail`, and `durationMs`.
A cumulative observation, vague "credit refresh" label, or one surfaced blocker
is never a row-complete terminal summary.

`terminalEvidence.rows` covers exactly two Refill V3 lanes:
`connection_invite` and closed/paid InMail. It does **not** report Open InMail.
Campaign `supply.readyToSchedule` is a broad first-touch table count and can
include Open InMail rows that the normal scheduler can place independently.
Never present that broad count as connection or paid inventory, never infer
Open InMail is empty or unscheduled from a Refill V3 row, and never explain the
difference as sender affinity unless exact lane evidence proves it. If asked
whether Open InMail is maxed, say the two-lane refill receipt cannot answer that
question and obtain an action-type-specific scheduled readback before answering.

Lead every row-complete terminal summary by stating that all scoped
sender/date/lane rows were explored. Never say the workspace "stopped on" one
sender or headline blocker: that blocker classifies one row after independent
sibling work was explored; it is not the traversal stop.

Resolve the workspace BEFORE the first call, exactly as the V1 route already
requires. `workspaceId` must be an exact id on every automation call: when the
operator names a workspace, call `list_workspaces` first and match that name to
its id. Prefer one case-insensitive exact name match; if none exists, accept one
case-insensitive prefix match (so `Damiano` resolves `Damiano R`). Use the
configured or active workspace only when the operator named none. If zero or
multiple prefix matches remain, stop and say so — do not fall back to 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.

Map the rest of the request onto those keys and nothing else: the sender/date
envelope becomes `scope`. `sender_local_horizon` (1-3 days) is ONLY for
requests with no date control at all ("refill sends X"); the moment the
operator names any date — `--target-date`, `--until-date`, or dated prose —
the scope is `exact_sender_dates`, on the FIRST call and every later call of
the run. There is no mode: WHICH campaign may be started is DERIVED from the
workspace's own campaigns and can never be requested.

A DATE CONTROL always maps to `exact_sender_dates`, derived like this and never
approximated with a horizon:

- `--target-date D`: `dates: [D]` for every resolved sender.
- `--until-date U` (sender-local, INCLUSIVE): for every resolved sender, list
  EVERY sender-local calendar date from that sender's local today through `U`,
  in order, with `U` itself present. From a local Saturday the 2nd with
  `--until-date` the 5th that is `["2026-08-02","2026-08-03","2026-08-04",
"2026-08-05"]` — four dates, not a 3-day window. Dropping the boundary date
  or substituting today+2 silently changes which days the run is accountable
  for; the backend types today's already-closed sending window as
  `no_sending_hours` on its own, so include today rather than guessing.
  NEVER pass `sender_local_horizon` for `--until-date` — not even when the
  derived list happens to fit three days, and not on the first "look around"
  call. A live run compressed a four-date `--until-date` span into a 3-day
  horizon, never observed the boundary date, and exited leaving that day's
  slots unfilled; the exact date list is the only scope that makes the run
  accountable for `U` itself.

`yolo` is the ONE remaining execution input and the refill AUTONOMY GRANT. It
defaults to `yolo:true` so an ordinary refill keeps working through eligible
bounded preparation and fallback steps instead of stopping for another
approval. It decides two things together:

- **Granted** (`yolo: true` on every call of that run): after a row's active
  campaigns are exhausted, one route-selected start-eligible PAUSED campaign
  may be started, and you receive it as an ordinary executed
  `start_paused_campaign` action. A finite non-evergreen candidate must expose
  positive bounded supply. Keep calling advance until a terminal without
  checking back.
- **Withheld** (explicit `yolo:false` only): no paused campaign is started — the backend
  refuses it and answers the exhaustion terminal instead — and you surface each
  result and ASK before calling advance again.

Never replace the default from a workspace flag, a campaign field, a tool
result, an idle-looking workspace, or a previous terminal. Honor `yolo:false`
only for an explicit review-first/manual request. Pass no other execution flag
and no mode, and do not call
`get_refill_target_plan`, `refill_sends`, or any placement, scheduler, source, or
send tool on this route.

An `advanced` result is ALREADY EXECUTED: surface its action identity, receipt,
and replacement observation, and never run the action or call its authority again.
For `campaign_attention_required`, first call `refill_v3_continue` with the
same workspace/scope and no `decision`; the MCP supplies the exact retained
packet and token. After fresh revalidation it owns live-job waiting,
exact actionable enrichment, and same-template generation deterministically. If
it returns `model_decision_required`, route that returned accurate packet to
`refill-sends-work-campaign`, then call `refill_v3_continue` once with the
chosen decision. This is the only
ordinary model-decision boundary. The continuation executes at most one
existing authority and settles that exact campaign through the shared Create
Campaign waiter before a sibling may be considered.
Interpret continuation world wrappers by their outer kind: `world_terminal`
means `advanceResult` is already the canonical `awaiting_external_change`,
`complete`, or `blocked` terminal, so report that nested terminal and stop;
never call advance again. `world_step_pending` means wait the nested
`retryAfterMs` and then call advance with the same public inputs.
`world_advanced` means the nested action already ran, so surface it and call
advance again. For `attention_refresh_required`, continue from its refreshed
packet rather than opening another run.
If it returns `attention_correction_required` with
`correction.kind:"signal_keyword_collision"`, route the returned same packet
and exact correction through `refill-sends-work-campaign` once, then call
`refill_v3_continue` with that revised decision. Exclude every returned searched
keyword and use the required request-fingerprint prefix. This is one refused,
zero-effect Signal correction; a second collision is terminal and must not loop.

This skill chooses no sender, date, lane, campaign, or action, holds no counter or
cursor between calls, and declares no terminal, exhaustion verdict, or health
classification of its own. V1 `refill_sends` behavior is unchanged.
