@sellable/mcp 0.1.513 → 0.1.514

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.
@@ -474,6 +474,39 @@ export declare const allTools: ({
474
474
  required: string[];
475
475
  additionalProperties: boolean;
476
476
  };
477
+ } | {
478
+ name: string;
479
+ description: string;
480
+ inputSchema: {
481
+ type: string;
482
+ properties: {
483
+ intent: {
484
+ type: string;
485
+ enum: string[];
486
+ description: string;
487
+ };
488
+ campaignId: {
489
+ type: string;
490
+ description: string;
491
+ };
492
+ tableId: {
493
+ type: string;
494
+ description: string;
495
+ };
496
+ limit: {
497
+ type: string;
498
+ minimum: number;
499
+ maximum: number;
500
+ description: string;
501
+ };
502
+ workspaceId: {
503
+ type: string;
504
+ description: string;
505
+ };
506
+ };
507
+ required: string[];
508
+ additionalProperties: boolean;
509
+ };
477
510
  } | {
478
511
  name: string;
479
512
  description: string;
@@ -2295,6 +2328,24 @@ export declare const allTools: ({
2295
2328
  };
2296
2329
  required: string[];
2297
2330
  };
2331
+ } | {
2332
+ name: string;
2333
+ description: string;
2334
+ inputSchema: {
2335
+ type: string;
2336
+ properties: {
2337
+ senderId: {
2338
+ type: string;
2339
+ description: string;
2340
+ };
2341
+ workspaceId: {
2342
+ type: string;
2343
+ description: string;
2344
+ };
2345
+ };
2346
+ required: string[];
2347
+ additionalProperties: boolean;
2348
+ };
2298
2349
  } | {
2299
2350
  name: string;
2300
2351
  description: string;
@@ -2583,6 +2634,43 @@ export declare const allTools: ({
2583
2634
  };
2584
2635
  required: never[];
2585
2636
  };
2637
+ } | {
2638
+ name: string;
2639
+ description: string;
2640
+ inputSchema: {
2641
+ type: string;
2642
+ properties: {
2643
+ workspaceId: {
2644
+ type: string;
2645
+ description: string;
2646
+ };
2647
+ senderIds: {
2648
+ type: string;
2649
+ items: {
2650
+ type: string;
2651
+ };
2652
+ };
2653
+ runState: {
2654
+ type: string;
2655
+ description: string;
2656
+ };
2657
+ intent: {
2658
+ type: string;
2659
+ enum: string[];
2660
+ description: string;
2661
+ };
2662
+ journal: {
2663
+ type: string;
2664
+ description: string;
2665
+ };
2666
+ journalNote: {
2667
+ type: string;
2668
+ description: string;
2669
+ };
2670
+ };
2671
+ required: string[];
2672
+ additionalProperties: boolean;
2673
+ };
2586
2674
  } | {
2587
2675
  name: string;
2588
2676
  description: string;
@@ -7193,6 +7281,33 @@ export declare const allTools: ({
7193
7281
  type: string;
7194
7282
  description: string;
7195
7283
  };
7284
+ runId: {
7285
+ type: string;
7286
+ description: string;
7287
+ };
7288
+ fence: {
7289
+ type: string;
7290
+ description: string;
7291
+ };
7292
+ dryRun: {
7293
+ type: string;
7294
+ description: string;
7295
+ };
7296
+ approvalMode: {
7297
+ type: string;
7298
+ enum: string[];
7299
+ description: string;
7300
+ };
7301
+ senderIds: {
7302
+ type: string;
7303
+ items: {
7304
+ type: string;
7305
+ };
7306
+ };
7307
+ intent: {
7308
+ type: string;
7309
+ enum: string[];
7310
+ };
7196
7311
  };
7197
7312
  required: string[];
7198
7313
  additionalProperties: boolean;
@@ -7541,6 +7656,59 @@ export declare const allTools: ({
7541
7656
  required: string[];
7542
7657
  additionalProperties: boolean;
7543
7658
  };
7659
+ } | {
7660
+ name: string;
7661
+ description: string;
7662
+ inputSchema: {
7663
+ type: string;
7664
+ properties: {
7665
+ capacityRequests: {
7666
+ type: string;
7667
+ minItems: number;
7668
+ items: {
7669
+ type: string;
7670
+ properties: {
7671
+ senderId: {
7672
+ type: string;
7673
+ description: string;
7674
+ };
7675
+ actionType: {
7676
+ type: string;
7677
+ enum: string[];
7678
+ description: string;
7679
+ };
7680
+ cellId: {
7681
+ type: string;
7682
+ description: string;
7683
+ };
7684
+ columnId: {
7685
+ type: string;
7686
+ description: string;
7687
+ };
7688
+ };
7689
+ required: string[];
7690
+ additionalProperties: boolean;
7691
+ };
7692
+ description: string;
7693
+ };
7694
+ targetDate: {
7695
+ type: string;
7696
+ pattern: string;
7697
+ description: string;
7698
+ };
7699
+ mode: {
7700
+ type: string;
7701
+ enum: string[];
7702
+ description: string;
7703
+ };
7704
+ workspaceId: {
7705
+ type: string;
7706
+ description: string;
7707
+ };
7708
+ };
7709
+ required: string[];
7710
+ additionalProperties: boolean;
7711
+ };
7544
7712
  } | {
7545
7713
  name: string;
7546
7714
  description: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sellable/mcp",
3
- "version": "0.1.513",
3
+ "version": "0.1.514",
4
4
  "type": "module",
5
5
  "description": "Sellable MCP server for Claude Code and Codex campaign workflows",
6
6
  "main": "dist/index.js",
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: refill-sends-v2
3
- description: Plan refill sends v2 work through the read-only three-lane dry-run planner.
3
+ description: Execute refill sends v2 through the fenced evergreen refill loop, with dry-run and resume support.
4
4
  visibility: public
5
5
  allowed-tools:
6
6
  - mcp__sellable__refill_sends_v2
@@ -15,19 +15,20 @@ allowed-tools:
15
15
 
16
16
  # Refill Sends V2
17
17
 
18
- Use this public command wrapper for refill planning requests such as "refill
19
- sends", "fill evergreen senders", "load everyone up", "plan Damiano", or "show a
20
- dry-run for this workspace".
18
+ Use this command for refill requests such as "refill sends", "fill evergreen
19
+ senders", "load everyone up", "run Damiano", or "show a dry run for this
20
+ workspace".
21
21
 
22
22
  Host command names:
23
23
 
24
24
  - Claude Code: `/sellable:refill-sends-v2`
25
25
  - Codex: `$sellable:refill-sends-v2`
26
26
 
27
- This is plan-only, read-only, dry-run, zero mutations. It creates no campaigns,
28
- does not prepare or approve messages, does not start campaigns, does not
29
- schedule or send messages, does not refresh paid InMail credit facts, and does
30
- not change source configuration. Execution remains in the Phase 86 workflow.
27
+ `refill_sends_v2` is the execution surface. In real-run mode it starts or
28
+ resumes a fenced refill run, reads a fresh packet, executes only the packet's
29
+ named bounded work, verifies the result, and returns either a terminal report, a
30
+ blocked report, or an in-progress resume handle. In dry-run mode it stays
31
+ read-only and writes only a dry-run journal.
31
32
 
32
33
  ## Entry
33
34
 
@@ -36,61 +37,120 @@ active workspace readback. Scheduled or autonomous usage must carry
36
37
  `workspaceId` on every tool call. If the workspace is missing or ambiguous, stop
37
38
  with `WORKSPACE_REQUIRED`; do not switch the shared active workspace.
38
39
 
39
- Start with the command contract:
40
+ For a real refill run:
40
41
 
41
42
  ```text
42
- refill_sends_v2({ workspaceId })
43
+ refill_sends_v2({ workspaceId, intent:"auto", senderIds?, approvalMode? })
43
44
  ```
44
45
 
45
- Then load the internal workflow prompt and flow asset:
46
+ For read-only inspection:
46
47
 
47
48
  ```text
48
- get_subskill_prompt({ subskillName: "refill-sends-v2-workflow" })
49
- get_subskill_asset({ subskillName: "refill-sends-v2-workflow", assetPath: "core/flow.v1.json" })
49
+ refill_sends_v2({ workspaceId, dryRun:true, intent:"auto", senderIds? })
50
50
  ```
51
51
 
52
- Continue both loads until `hasMore:false`; parse the flow JSON and verify
53
- `workflow:"refill-sends-v2-workflow"` with a `v1` version before the planner
54
- call.
52
+ To resume an in-progress run, pass the handle back exactly:
55
53
 
56
- Call the dry-run planner with the explicit workspace id:
54
+ ```text
55
+ refill_sends_v2({ workspaceId, runId, fence })
56
+ ```
57
+
58
+ If a stale handle loses the lease, the tool reports the holder status and the
59
+ approximately 10 minute lockout window. Reinvoke with the current handle or wait
60
+ for lease expiry; never guess a fence.
61
+
62
+ The default `intent:"auto"` inspects `managed_waterfall`,
63
+ `dashboard_evergreen`, and `active_campaign` lane sources. Report the lane
64
+ source and lane chain as proof for every selected sender.
65
+
66
+ If membership blocks a workspace read, report the structured
67
+ `workspace_access` blocker instead of retrying auth or switching workspaces.
68
+
69
+ ## Authority
70
+
71
+ The run may execute only packet-named bounded work:
72
+
73
+ - approve generated messages that are ready for review (`approve_messages`);
74
+ - enrich the next batch of unenriched rows (`enrich_more`);
75
+ - copy leads already found into the campaign table (`reconcile_source_copy`);
76
+ - re-run errored enrichment/message cells (`rerun_errored_cells`);
77
+ - find more leads via this campaign's original source (`add_leads_same_source`)
78
+ only for mechanical same-source continuations;
79
+ - start the paused campaign so ready rows can schedule (`start_campaign`) only
80
+ when the campaign is in the packet's pinned lane chain;
81
+ - refresh paid InMail credit facts during bootstrap and once per scheduler-wait
82
+ entry when the packet requires it.
83
+
84
+ The loop records planned -> did -> outcome before and after every foreign
85
+ mutation. It refuses stale packets by fingerprint and validates packet
86
+ coherence before dispatch. The planner owns repair decisions: if a batch needs
87
+ repair, the next packet names the repair rung; the loop bounds and refuses, it
88
+ does not improvise fixes.
89
+
90
+ Do not create campaigns, switch provider families, lower thresholds, write
91
+ scheduler fields, send messages, archive/delete campaigns, or mutate
92
+ brief/filter/message/sequence/sender config unless a separate exact approval
93
+ packet explicitly authorizes that work. In short: no campaign creation, no
94
+ provider-family switch, no threshold lowering, no scheduler writes, no
95
+ launch/send/archive/delete, and no brief/filter/message/sequence/sender
96
+ mutation.
97
+
98
+ ## Continuations
99
+
100
+ Sales Nav and Prospeo source continuations that are not yolo-safe are blocked
101
+ as executable scoped handoffs. The report will include text shaped like:
57
102
 
58
103
  ```text
59
- get_refill_plan_v2({ workspaceId, intent:"auto", senderIds?, runState?, journal?, journalNote? })
104
+ $sellable:create-campaign [campaignId]
60
105
  ```
61
106
 
62
- The default `intent:"auto"` checks the three lane-source families in order:
63
-
64
- - `managed_waterfall` for product-managed evergreen waterfall lanes.
65
- - `dashboard_evergreen` for dashboard-visible evergreen campaigns.
66
- - `active_campaign` for regular active campaigns that should refill through a
67
- deterministic lane chain.
68
-
69
- Use `senderIds` only when the user scoped the request to exact senders. Use
70
- `runState` only for an explicit what-if dry run; otherwise let the MCP read
71
- local lane memory hints. Leave `journal` enabled unless the user specifically
72
- asked for no local journal.
73
-
74
- ## Report
75
-
76
- Review the returned packet before giving a recommendation:
77
-
78
- - `planRevision` starts with `pr1:`.
79
- - `stateRevision` identifies the current readback.
80
- - `packet` is the operator-facing facts receipt.
81
- - `plans[]` contains per-sender lane decisions and itinerary details.
82
- - `globalActionQueue[]` is a dry-run sequence only.
83
- - `evergreen.laneOrder[].source` uses `managed_waterfall`,
84
- `dashboard_evergreen`, or `active_campaign`.
85
- - `evergreen.senderLaneSelections[].laneChain` records the ordered lane chain
86
- when a regular active campaign chain is selected.
87
- - Terminals can include `done` or `no_refillable_campaigns`.
88
- - A membership 403 must return the structured `workspace_access` blocker; report
89
- it as a read-only blocker, not a login retry.
90
- - `journalPath` points to the dry-run markdown journal written by the MCP.
91
- - `sideEffects` must be all false.
92
-
93
- Report campaign names and sender names first, ids second. Include the lane
94
- source, lane chain when present, chosen rung, itinerary, plan revision, journal
95
- path, and any blockers or warnings. Stop after the report; do not execute the
96
- action queue.
107
+ That handoff is run in a separate thread only after explicit approval. Scope is
108
+ strict: add leads through this campaign's existing source, keep the same
109
+ saved-search identity, import mode `add` into the same source list, and do
110
+ source work only. It is never a new campaign, provider-family switch, brief
111
+ change, rubric change, message change, sequence change, or sender change.
112
+
113
+ ## Verification
114
+
115
+ The loop verifies whole rows before replanning. It reattaches to an own active
116
+ prep job instead of re-dispatching, waits on foreign prep jobs without adopting
117
+ their outcomes, waits on active enrichment/import work by rereading the packet,
118
+ and treats capped scheduler capacity as complete.
119
+
120
+ Scheduler waits are cross-invocation. Each tool call polls briefly under the
121
+ host guard, heartbeats the run lease, and may return:
122
+
123
+ ```text
124
+ { status:"in_progress", runId, fence, gate, guidance }
125
+ ```
126
+
127
+ Reinvoke with that handle. The approximately five minute scheduler budget is at
128
+ the run level, not one tool call. A window-closed or loaded-awaiting scheduler
129
+ report must include remaining-ready count, exact expected pickup time, and the
130
+ resume handle; never treat bare "awaiting scheduler" copy as a final answer.
131
+
132
+ After the first prep batch, read the conversion-verdict journal line. It is
133
+ rendered from packet facts: `supplyCensus`, `censusReason`, and
134
+ `pipelineDiagnosis.firstFailing`. If the verdict is honest rubric failure, the
135
+ human decision is to update the rubric or change lead source; refill does not
136
+ mutate those automatically.
137
+
138
+ ## Results
139
+
140
+ Terminal reasons are:
141
+
142
+ - `complete`
143
+ - `capped_by_scheduler`
144
+ - `loaded_awaiting_scheduler`
145
+ - `lanes_exhausted`
146
+ - `blocked`
147
+ - `no_refillable_campaigns`
148
+ - `not_an_evergreen_workspace`
149
+
150
+ Report campaign names and sender names first, ids second. Include the terminal
151
+ reason or resume handle, lane source, lane chain, chosen label with token
152
+ secondary, plan revision, journal path, blocked continuation packets, and
153
+ firstFailing checklist when present.
154
+
155
+ Real-run journals live under `~/.sellable/refill/runs` by default and append an
156
+ index line. Dry runs include the dry marker and do not create run records.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: refill-sends-v2-workflow
3
- description: Internal read-only workflow for refill sends v2 dry-run planning.
3
+ description: Internal execution workflow for refill sends v2 fenced runs.
4
4
  visibility: internal
5
5
  allowed-tools:
6
6
  - mcp__sellable__refill_sends_v2
@@ -15,113 +15,124 @@ allowed-tools:
15
15
 
16
16
  # Refill Sends V2 Workflow
17
17
 
18
- This internal workflow is the read-only, dry-run, zero mutations plan surface
19
- for refill sends v2. The workflow has three gates: bootstrap, plan, and report.
20
- It covers product-managed evergreen workspaces, dashboard evergreen workspaces,
21
- and non-evergreen active campaign workspaces without creating new campaigns.
18
+ This internal workflow mirrors the run-record gates enforced by
19
+ `refill_sends_v2`: bootstrap, plan, execute, verify, and report. Dry runs stay
20
+ read-only. Real runs execute only packet-named bounded work under a fenced run
21
+ lease and return terminal, blocked, or in-progress results.
22
22
 
23
23
  Runtime flow asset: after loading this prompt, load
24
24
  `get_subskill_asset({ subskillName: "refill-sends-v2-workflow", assetPath: "core/flow.v1.json" })`
25
25
  through MCP, continue chunks until `hasMore:false`, parse the JSON, and verify
26
- `workflow:"refill-sends-v2-workflow"` with a `v1` version before any planner
27
- call. If the asset cannot load or parse, stop with
26
+ `workflow:"refill-sends-v2-workflow"` with a `v1` version before driving the
27
+ loop. If the asset cannot load or parse, stop with
28
28
  `blocked:refill_sends_v2_workflow_asset_unavailable`; do not emulate it from
29
29
  local files or memory.
30
30
 
31
- ## G0 Bootstrap Facts
31
+ ## G0 Bootstrap
32
32
 
33
- Resolve the request-scoped `workspaceId` using read-only auth and workspace
34
- tools. Scheduled or autonomous runs must not rely on implicit workspace state.
35
- Call `refill_sends_v2({ workspaceId })` first to receive the command contract,
36
- then call `get_refill_plan_v2({ workspaceId, intent:"auto" })` for the dry-run
33
+ Resolve request-scoped `workspaceId` using read-only auth and workspace tools.
34
+ Scheduled or autonomous runs must not rely on implicit workspace state.
35
+
36
+ Start or resume the execution loop:
37
+
38
+ ```text
39
+ refill_sends_v2({ workspaceId, intent:"auto", senderIds?, approvalMode?, runId?, fence? })
40
+ ```
41
+
42
+ Use `dryRun:true` only when the user asked for read-only inspection. A dry run
43
+ writes a journal only. A real run creates or resumes a run record, refreshes
44
+ paid-credit trust only when packet facts require it, and returns a resume handle
45
+ when the current invocation must pause.
46
+
47
+ ## G1 Plan
48
+
49
+ The loop reads `get_refill_plan_v2` with runState fed back from the run record.
50
+ It captures `planRevision`, `stateRevision`, and `targetShapeRevision`, then
51
+ validates the packet before dispatch. Packet coherence is required: fingerprint
52
+ shape, tool input completeness, lane-chain membership, census-vs-rung
53
+ consistency, and terminal/action exclusivity. An incoherent packet is replanned
54
+ once, then blocked as `packet_incoherent`.
55
+
56
+ The packet's `supplyCensus`, `censusReason`, and `pipelineDiagnosis` checklist
57
+ are the reason for the next rung. Repair is replan-driven: the planner names
58
+ the repair rung, such as re-run errored enrichment/message cells
59
+ (`rerun_errored_cells`), approve generated messages that are ready for review
60
+ (`approve_messages`), or start the paused campaign so ready rows can schedule
61
+ (`start_campaign`). The loop bounds and refuses repair loops; it never invents
62
+ a fix list.
63
+
64
+ Use plain-language labels first and tokens second, for example "copy leads
65
+ already found into the campaign table (`reconcile_source_copy`)".
66
+
67
+ ## G2 Execute
68
+
69
+ The loop executes only the packet head action whose fingerprint was captured at
70
+ G1. Stale packets are refused and replanned.
71
+
72
+ Allowed packet-named work:
73
+
74
+ 1. start the paused campaign so ready rows can schedule (`start_campaign`) when
75
+ the campaign is in the pinned lane chain;
76
+ 2. approve generated messages that are ready for review (`approve_messages`);
77
+ 3. enrich the next batch of unenriched rows (`enrich_more`);
78
+ 4. copy leads already found into the campaign table (`reconcile_source_copy`);
79
+ 5. re-run errored enrichment/message cells (`rerun_errored_cells`);
80
+ 6. find more leads via this campaign's original source
81
+ (`add_leads_same_source`) for mechanical same-source continuations;
82
+ 7. wait for the scheduler to pick up ready rows (`wait_for_scheduler`).
83
+
84
+ Never create campaigns, switch providers, lower thresholds, write scheduler
85
+ fields, send/archive/delete, or mutate brief/filter/message/sequence/sender
86
+ config inside refill.
87
+
88
+ Sales Nav and Prospeo continuations that need human source work are blocked as
89
+ scoped handoffs:
90
+
91
+ ```text
92
+ $sellable:create-campaign [campaignId]
93
+ ```
94
+
95
+ The handoff scope is source work only: same saved-search identity, import mode
96
+ `add` into the same source list, no new campaign, no provider-family switch, and
97
+ no brief/rubric/message/sequence/sender changes without a separate approval
37
98
  packet.
38
99
 
39
- Optional inputs:
40
-
41
- - `senderIds`: exact sender scope when the user named senders.
42
- - `intent`: defaults to `auto`. Use explicit `evergreen`, `plain`, or `active`
43
- only for a what-if comparison.
44
- - `runState`: synthetic v1 lane memory for what-if dry runs. Use it only when
45
- the user intentionally wants a hypothetical cursor, pin, cooldown state, or
46
- laneChain replay.
47
- - `journal:false`: skip the local journal only when explicitly requested.
48
- - `journalNote`: append operator context to the dry-run terminal section.
49
-
50
- ## G1 Lane Sources
51
-
52
- The planner reports the lane source for every selected lane:
53
-
54
- 1. `managed_waterfall`: product-managed evergreen waterfall lanes.
55
- 2. `dashboard_evergreen`: dashboard-visible evergreen campaigns that are not in
56
- the managed waterfall snapshot.
57
- 3. `active_campaign`: regular active campaigns ranked into a deterministic
58
- laneChain for non-evergreen refill planning.
59
-
60
- If no lane family has refillable campaigns, report `no_refillable_campaigns`.
61
- If the user lacks membership on a requested workspace, report the structured
62
- `workspace_access` blocker with the workspace id and journal path.
63
-
64
- ## G2 Refill Ladder
65
-
66
- The planner chooses from this locked rung order:
67
-
68
- 1. `start_campaign`: a paused selected lane has scheduler-ready supply but must
69
- be live before the scheduler can pick it up.
70
- 2. `approve_messages`: generated rows are ready for a bounded approval step.
71
- 3. `wait_for_active_work`: enrichment, generation, import, or preparation is
72
- already active, so the next honest action is to wait for that work to finish.
73
- 4. `enrich_more`: exact-set enrichment can close the current ready gap.
74
- 5. `reconcile_source_copy`: same-source rows exist and can be copied without
75
- changing source family or source fingerprint.
76
- 6. `add_leads_same_source`: the same source path can add bounded new supply.
77
- 7. `paid_lane_fallback`: paid InMail is blocked or below threshold, so the same
78
- waterfall can fall back to an existing supported lane.
79
- 8. `next_campaign`: the current lane is exhausted, cooling down, pinned away, or
80
- unsafe, and the next surviving campaign lane should be inspected.
81
- 9. `wait_for_scheduler`: ready or projected supply covers the target, and only
82
- scheduler pickup remains.
83
- 10. `done`: the sender or lane is complete for the selected target window, a
84
- scheduler cap makes additional preparation unnecessary, or
85
- `no_refillable_campaigns` is the honest terminal.
86
-
87
- Forward-only lane rule: a lane can move to a later rung or a later surviving
88
- campaign, but the dry-run packet must not jump backward to invent work that the
89
- facts do not support. Pinned-campaign rule: a pinned campaign stays selected
90
- until the planner proves a blocker, cooldown, exhaustion, or safer next lane.
91
- Capped-is-complete rule: when scheduler capacity is lower than the requested
92
- target and ready/projected coverage satisfies that cap, report `done` with the
93
- cap reason. Honest-itinerary rule: show what was checked, what was skipped, and
94
- why; never hide uncertainty behind a confident source recommendation.
95
-
96
- Guardrails: no campaign creation, no provider-family switch, no threshold
97
- lowering, no scheduler writes, no launch/send/archive/delete, no
98
- brief/filter/message/sequence/sender mutation, no message preparation, no
99
- message approval, no paid InMail credit refresh, no source mutation, no row
100
- queueing, and no campaign status change.
101
-
102
- ## Journal
103
-
104
- `get_refill_plan_v2` writes a local dry-run journal unless `journal:false` is
105
- passed. Read the returned `journalPath` from the tool result and include it in
106
- the report. The journal contains bootstrap, plan, and terminal sections plus an
107
- index line under the run journal directory. If the tool returns
108
- `journalPath:null` with a `journalWriteFailed` warning, report the warning and
109
- continue with the dry-run packet; do not retry by writing local files yourself.
110
-
111
- ## Report Contract
112
-
113
- Completion means packet reviewed and journal written. Merely calling
114
- `get_refill_plan_v2` is not completion proof.
115
-
116
- Report these facts:
117
-
118
- - workspace id and selected sender scope;
119
- - plan revision and state revision;
120
- - selected campaign, lane source, and laneChain per sender;
121
- - chosen rung and dry-run action queue;
122
- - itinerary, skipped lanes, blockers, cooldowns, and warnings;
123
- - `no_refillable_campaigns` terminals and `workspace_access` blockers;
124
- - journal path;
125
- - side effects proof, which must show all false values.
126
-
127
- Stop after the report. This workflow never executes the action queue.
100
+ ## G3 Verify
101
+
102
+ Verification is whole-row and bounded. The loop records planned -> did ->
103
+ outcome, then verifies before replanning.
104
+
105
+ - Own active prep jobs are reattached by `requestSource` and `requestHash`; do
106
+ not re-dispatch prep while the job is active.
107
+ - Foreign prep jobs are waited out or blocked as `foreign_prep_active`; their
108
+ outcomes are not adopted.
109
+ - Active enrichment/import work is handled by bounded packet rereads.
110
+ - The conversion verdict after the first prep batch is journaled from packet
111
+ facts. The plan gate withholds a second unproven prep batch when
112
+ `censusReason` is `conversion_blocked`.
113
+ - Honest rubric failure means a human updates the rubric or changes lead source.
114
+ - Capped scheduler capacity is complete.
115
+
116
+ Scheduler wait is cross-invocation. If the tool returns `in_progress` with
117
+ sweep guidance, re-invoke `refill_sends_v2` with `{runId, fence}`. The scheduler
118
+ budget is cumulative at the run level. Zero pickup after a confirmed sweep is
119
+ debugged from `pipelineDiagnosis`; it is not waited out forever.
120
+
121
+ ## G4 Report
122
+
123
+ Completion proof is the run record at `g4_terminal` with a named doneReason and
124
+ journal evidence showing planned -> did -> outcome plus a terminal block and
125
+ index line. A plan packet alone is not completion proof.
126
+
127
+ Terminal reasons are `complete`, `capped_by_scheduler`,
128
+ `loaded_awaiting_scheduler`, `lanes_exhausted`, `blocked`,
129
+ `no_refillable_campaigns`, and `not_an_evergreen_workspace`.
130
+
131
+ `loaded_awaiting_scheduler` remains the run-record token, but report copy must
132
+ include remaining-ready count, exact expected pickup time, and the resume
133
+ handle. Do not render bare "loaded, awaiting scheduler" as a final answer.
134
+
135
+ Report campaign names and sender names first, ids second. Include lane source,
136
+ lane chain, chosen label with token secondary, plan revision, terminal reason or
137
+ resume handle, journal path, blocked continuation packets, and firstFailing
138
+ checklist when present.