@sellable/mcp 0.1.420 → 0.1.423

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.
@@ -6,7 +6,7 @@ async function postCampaignFillRoute(body) {
6
6
  export const campaignFillRoutingToolDefinitions = [
7
7
  {
8
8
  name: "resolve_campaign_fill_route",
9
- description: 'Audit-only resolver to call before interpreting plain "fill campaigns", "load everyone up", or similar fill requests. It decides whether the workspace has evergreen/horizon targets, currently active campaign-backed sequence targets, or no targets so the agent must ask what to create. It does not create, append, prepare, approve, schedule, launch, archive, delete, send, or call lower-level fill tools. Only call fill_campaign_horizon when this resolver returns route "evergreen_horizon" or the user explicitly requested evergreen/horizon and the returned target is eligible. Exact target narrowing uses campaignId or tableId only; never fuzzy campaign names.',
9
+ description: 'Audit-only resolver to call before interpreting plain "fill campaigns", "load everyone up", or similar fill requests. It decides whether the workspace has evergreen/horizon targets, currently active campaign-backed sequence targets, or no targets so the agent must ask what to create. It does not create, append, prepare, approve, schedule, launch, archive, delete, send, or call lower-level fill tools. Only call fill_campaign_horizon when this resolver returns route "evergreen_horizon" or the user explicitly requested evergreen/horizon and the returned target is eligible; otherwise route plain fill through regular campaign refill before any two-send-day horizon preparation. Exact target narrowing uses campaignId or tableId only; never fuzzy campaign names.',
10
10
  inputSchema: {
11
11
  type: "object",
12
12
  properties: {
@@ -373,7 +373,7 @@ export function getPostFindLeadsScoutRegistry() {
373
373
  codex: 'After confirm_lead_list copies source rows and the initial campaign-table execution slice exists, ask the filter-choice question immediately. Do not spawn anything before that question. After the answer, launch only Message Drafting. The filter-choice answer is the post-import user gate for this single worker; do not ask another question about starting it in step-wise or YOLO mode. The registry lookup is not a launch: after get_post_find_leads_scout_registry, immediately invoke Task/spawn_agent or the host background-agent mechanism before loading filter-leads.md, before saving rubrics, and before treating skip-filters as ready for message review. Both choices route through this kickoff; do not let filters_skipped jump straight from filter-choice to message-generation. If filters are chosen, the parent stays on Filter Rules and drafts/saves rubrics with MCP tools while Message Drafting runs in the background. If filters are skipped, move to Messages/message review only after Message Drafting has started or is ready; update_campaign(currentStep=messages) is not proof of launch. If the named Message Drafting custom agent is unavailable, spawn a generic gpt-5.5 xhigh Message Drafting background agent with the same lean campaign/table basis. When the background worker starts, persist workerDetails.messageDraftBuilder with statusSource "branch", status "branch-running", runId, startedAt, updatedAt, basisToken when known, and basis containing campaignId, selectedLeadListId, workflowTableId, filterChoice, and reviewBatchRowHash or reviewBatchRowIds; workerStatuses.messageDraftBuilder may be "running" as a simple badge only. Never put rich proof under workerStatuses and never use workerStatuses.messageDrafting. If no background-agent tool is callable, start the same full message branch inline before filter drafting or before skip-filter message review, record workerDetails.messageDraftBuilder with statusSource "parent-thread-fallback" and status "fallback-active", and require the same live context, prompt, assets, and validation gate before message review; do not wait until filters are saved and then call the registry.',
374
374
  claude: "After confirm_lead_list copies source rows and the initial campaign-table execution slice exists, ask the filter-choice question immediately. Do not invoke any Task/Agent before that question. After the answer, invoke only Message Drafting. If filters are chosen, parent drafts/saves rubrics with MCP tools while Message Drafting runs, asks filter approval, then joins Message Drafting. If filters are skipped, invoke only Message Drafting and move to Messages/message review.",
375
375
  parentThreadRule: 'Named agents are optional acceleration, but message drafting is not optional. The only normal background worker is Message Drafting. The filter-choice answer is the campaign-scoped go-ahead for this single post-import worker; do not ask another question to start it in step-wise or YOLO mode. If a named agent is unavailable, use a generic gpt-5.5 xhigh Message Drafting background agent. source work and filter work stay in the parent thread with MCP tools. If post-find-leads-message-scout is available, run it as the background Message Draft Builder after the filter-choice answer. The registry lookup is not a launch: get_post_find_leads_scout_registry only identifies the worker, and Message Drafting counts as started only after Task/spawn_agent or the host background-agent tool is invoked, or after the parent begins the same full message branch inline because no background-agent tool is callable. This launch must happen before loading filter-leads.md, save_rubrics, filter approval, or skip-filter message review; currentStep=messages is not proof of launch. If post-find-leads-message-scout is absent, do not customer-surface install status. Do not silently treat message drafting as started; the main thread must either launch the background worker or execute the same message branch from CampaignOffer state, selected source state, workflowTableId, and initial campaign-table execution slice rows. For a spawned worker, record workerDetails.messageDraftBuilder with statusSource branch / status branch-running, runId, startedAt, updatedAt, and basis containing campaignId, selectedLeadListId, workflowTableId, filterChoice, and reviewBatchRowHash or reviewBatchRowIds. workerStatuses.messageDraftBuilder is optional simple badge text only ("running", "ready", "blocked", "idle"); never put runId/statusSource/basis under workerStatuses and never use workerStatuses.messageDrafting. If no background-agent tool is callable, start that same full message branch inline before filter drafting or before skip-filter message review, record workerDetails.messageDraftBuilder with statusSource parent-thread-fallback / status fallback-active then ready, and require the same live context, prompt, assets, and validation gate before message review; do not report that as a background worker failure. If neither branch nor inline fallback can run, return blocked/retry-needed; do not wait until filters are saved and then call the registry. The Message Drafting handoff must be lean. Do not paste copied row counts, brief hashes, review-batch hashes, full reviewBatchRowIds, broad row data, or local debug artifacts into the spawn prompt. Local markdown/json files are not normal-path inputs. The filter-choice question is the first post-import user gate; do not load post-lead registries or filter references before it. Message drafting starts after the filter-choice answer, must load get_subskill_prompt({ subskillName: "generate-messages" }), and must load every required message asset named by generate-messages Mode 0 through get_subskill_asset before drafting. Reference Asset Loading means loading the required pre-draft reference pack before drafting; return blocked/retry-needed if required assets cannot be loaded; load ai-tells.md because it is never optional. The branch or parent-thread fallback loads the full generate-messages prompt and every referenced asset through get_subskill_asset. After generating/revising the candidate and before returning ready, must load get_subskill_prompt({ subskillName: "create-campaign-v2-validation" }) as the final internal validation gate, must read live campaign table state through scoped MCP/product tools, and must reject mismatched selectedLeadListId/workflowTableId/campaign/workspace input. Do not block when filters were chosen but leadScoringRubrics are not yet visible in the branch read; the parent owns save_rubrics and filter approval in parallel, so Message Drafting should return status ready with basisStatus usable_initial when campaign/list/table identity and the non-empty execution slice match. Do not use any alternate, local-artifact, or examples-only message prompt. User copy feedback, message QA, or rewrite requests before approve-message must be routed back to Message Drafting with the current recommendation, lean campaign/table basis, and latest user text; the parent must not rewrite or QA the template from memory and must not call update_campaign_brief before approve-message. The worker validates internally and returns only templateRecommendation, tokenFillRules, renderedGoodSample, status, approveOrReviseRecommendation, validationStatus, outputAt, outputHash, and blocked/retry detail. Do not render renderedFallbackSample, risk notes, or a qaReceipt on the normal happy path. On the filter path, save_rubrics keeps the browser on Filter Rules after save_rubrics so the user can approve the saved criteria; after saved-filter approval, move to Filter Leads with currentStep=apply-icp-rubric whether Message Drafting is ready or still running. Wait there for message approval. Enrichment, filtering, Generate Message cells, sender setup, sequence attach, and launch wait for template approval on the Use Template path. On the skip path, move to Messages/message review after Message Drafting has started or is ready and wait for message approval before enrichment or Settings. Do not render message review from checklist or shortcut instructions; message review requires a messageDraftRecommendation whose basis proves the generate-messages prompt, required message assets, and validation gate ran for the current campaign/table execution slice. Do not automatically rerun Message Drafting after filters/enrichment finish; show the initial draft by default and offer an enriched rewrite only with explicit user opt-in. Handoff and recommendation output are Markdown with labeled fields, not raw JSON.',
376
- prepareMessagesRule: `Default create-campaign stays on the existing reviewBatchLimit:15 first campaign-table execution slice. For plain post-mint fill/load/refill requests, load get_subskill_prompt({ subskillName: "refill-sends-workflow" }), then call resolve_campaign_fill_route({ intent:"plain" }), list_senders, and get_campaign_refill_state for enough candidate campaigns to identify the campaign that most recently had scheduler-owned sends for the relevant sender set before any mutation. Route outcomes are route:"evergreen_horizon", route:"active_campaigns", and route:"ask_create"; stay in the same campaignOfferId/campaignId context after minting. Plain fill is not an alias for fill_campaign_horizon or campaign creation. fill_campaign_horizon is evergreen-only and is not the generic regular-campaign fill path. Campaign creation is allowed only after route:"ask_create" and explicit user selection; it is never the default response to plain fill. Treat "fill up/load sends" as capacity-fill preparation, and treat "refill senders", "fill senders", "max out senders", and "load everyone up" as sender-scoped capacity-fill preparation. For sender-scoped requests with no named senders, --yolo means all eligible healthy senders enrolled in active campaign-backed sequence campaigns in the active workspace; without --yolo, ask which eligible enrolled senders to refill before choosing campaigns or mutating. In non-yolo interactive Codex or Claude Code sessions, the sender/campaign/action approval packet must be asked through request_user_input/AskUserQuestion with exactly Accept and Decline; do not use plain chat approval. The approval question body must show workspace, sender scope, a campaign-by-campaign table, exact ids/caps/dates, side effects, forbidden actions, and stop condition before mutation. Default --yolo horizon is two sender-local send days, skipping no-send-hour days. For campaign-scoped fill/refill, select the best recent-send campaign and calculate the bounded gap from healthy sender daily capacity minus future scheduler-owned scheduled sends and ready-to-schedule rows, then prepare only that gap. For sender-scoped fill/refill, calculate the bounded gap per eligible sender across active enrolled campaigns, counting future scheduled and ready-to-schedule rows across those campaigns, then choose the best same-sender campaign to fill each sender gap: prefer recent/future scheduler-owned sends for that sender, then strongest recent result evidence, then source health. Do not stop after filling only one sender when the request was sender-scoped. Use the refill workflow to decide whether to enrich/prep more rows in that same recent-send/best-result campaign, add/import more rows to that same campaign/source path, use a different existing campaign only when the selected same-sender lane is blocked/exhausted/already loaded while the sender still has a gap, or ask what to create. Do not create warm-post-engager side campaigns. Surface sender-health blockers separately from prepared/approved/scheduled counts. User-facing refill decisions must be campaign-name-first and sender-name-first, with ids as proof/execution targets only. For already-running regular campaigns that need Signal Discovery source replenishment, use the guarded currentStep clear with clearCurrentStepIfMatches:"running", campaign-scoped provider prompt/search/select, and import_leads with the existing sourceLeadListId when a newly approved selected-post scrape would otherwise return reusedExistingSourceList. After confirm_lead_list copies rows into an existing table, avoid fixed maxRowsToCheck:100; if confirm_lead_list returns USER_ADDED_ROWS_LIMIT_EXCEEDED, create a bounded same-source split from selectedLeadListId with get_rows_minimal/load_csv_linkedin_leads, confirm that smaller source list into the same campaign, and operate on the reviewBatch first; inspect reviewBatch/table selectors or use adaptive/wider bounded prep so appended rows are included. Do not interpret checkedRows as enriched rows; it is only the table cursor. Prepared, approved, and ready_to_schedule rows are intermediate states; never call them scheduled unless a re-read proves scheduler-owned scheduled cells with non-null scheduledFor. Before source import, prep, or approval, require exact visible approval and a fresh get_campaign_refill_state reread; stop if freshness.stateHash or exact ids changed. For "approve X messages", use approvalMode:approve only when explicitly requested, but still do not launch. For "schedule X sends" or "fill sender sends", approve only when explicitly requested, then re-read campaign/table scheduled counts; if scheduler-owned scheduledFor cells are not present, report prepared/approved/ready - awaiting scheduler instead of success. Do not call start_campaign as part of fill/schedule horizon. Do not call start_campaign as part of refill sends. Launch/start is a separate explicit human action after the operator intentionally wants sends to go out, and it must still verify that the bounded cohort is the only approved cohort and must not broad approve-all. campaignId is CampaignOffer.id. If the user asks to stop preparation, the target is wrong, or status shows the wrong campaign/table, use cancel_campaign_message_preparation only for the exact active job. Low-level selectors are diagnostics and recovery only for this lane. start_campaign remains forbidden until explicit launch/start approval outside refill sends.`,
376
+ prepareMessagesRule: `Default create-campaign stays on the existing reviewBatchLimit:15 first campaign-table execution slice. For plain post-mint fill/load/refill requests, load get_subskill_prompt({ subskillName: "refill-sends-workflow" }), then call resolve_campaign_fill_route({ intent:"plain" }), list_senders, and get_campaign_refill_state for enough candidate campaigns to identify the campaign that most recently had scheduler-owned sends for the relevant sender set before any mutation. Route outcomes are route:"evergreen_horizon", route:"active_campaigns", and route:"ask_create"; stay in the same campaignOfferId/campaignId context after minting. Plain fill is not an alias for fill_campaign_horizon or campaign creation. fill_campaign_horizon is evergreen-only and is not the generic regular-campaign fill path. Campaign creation is allowed only after route:"ask_create" and explicit user selection; it is never the default response to plain fill. Treat "fill up/load sends" as capacity-fill preparation, and treat "refill senders", "fill senders", "max out senders", and "load everyone up" as sender-scoped capacity-fill preparation. For sender-scoped requests with no named senders, --yolo means all eligible healthy senders enrolled in active campaign-backed sequence campaigns in the active workspace; without --yolo, ask which eligible enrolled senders to refill before choosing campaigns or mutating. In non-yolo interactive Codex or Claude Code sessions, the sender/campaign/action approval packet must be asked through request_user_input/AskUserQuestion with exactly Accept and Decline; do not use plain chat approval. The approval question body must show workspace, sender scope, a campaign-by-campaign table, exact ids/caps/dates, side effects, forbidden actions, and stop condition before mutation. Default --yolo horizon is a two-send-day horizon: two sender-local send days, skipping no-send-hour days. For campaign-scoped fill/refill, select the best recent-send campaign and calculate the bounded gap from healthy sender daily capacity minus future scheduler-owned scheduled sends and ready-to-schedule rows, then prepare only that gap. For sender-scoped fill/refill, calculate the bounded gap per eligible sender across active enrolled campaigns, counting future scheduled and ready-to-schedule rows across those campaigns, then choose the best same-sender campaign to fill each sender gap: prefer recent/future scheduler-owned sends for that sender, then strongest recent result evidence, then source health. Do not stop after filling only one sender when the request was sender-scoped. Use the refill workflow to decide whether to enrich/prep more rows in that same recent-send/best-result campaign, add/import more rows to that same campaign/source path, use a different existing campaign only when the selected same-sender lane is blocked/exhausted/already loaded while the sender still has a gap, or ask what to create. Do not create warm-post-engager side campaigns. Surface sender-health blockers separately from prepared/approved/scheduled counts. User-facing refill decisions must be campaign-name-first and sender-name-first, with ids as proof/execution targets only. For already-running regular campaigns that need Signal Discovery source replenishment, use the guarded currentStep clear with clearCurrentStepIfMatches:"running", campaign-scoped provider prompt/search/select, and import_leads with the existing sourceLeadListId when a newly approved selected-post scrape would otherwise return reusedExistingSourceList. After confirm_lead_list copies rows into an existing table, avoid fixed maxRowsToCheck:100; if confirm_lead_list returns USER_ADDED_ROWS_LIMIT_EXCEEDED, create a bounded same-source split from selectedLeadListId with get_rows_minimal/load_csv_linkedin_leads, confirm that smaller source list into the same campaign, and operate on the reviewBatch first; inspect reviewBatch/table selectors or use adaptive/wider bounded prep so appended rows are included. Do not interpret checkedRows as enriched rows; it is only the table cursor. Prepared, approved, and ready_to_schedule rows are intermediate states; never call them scheduled unless a re-read proves scheduler-owned scheduled cells with non-null scheduledFor. Before source import, prep, or approval, require exact visible approval and a fresh get_campaign_refill_state reread; stop if freshness.stateHash or exact ids changed. For "approve X messages", use approvalMode:approve only when explicitly requested, but still do not launch. For "schedule X sends" or "fill sender sends", approve only when explicitly requested, then re-read campaign/table scheduled counts; if scheduler-owned scheduledFor cells are not present, report prepared/approved/ready - awaiting scheduler instead of success. Do not call start_campaign as part of fill/schedule horizon. Do not call start_campaign as part of refill sends. Launch/start is a separate explicit human action after the operator intentionally wants sends to go out, and it must still verify that the bounded cohort is the only approved cohort and must not broad approve-all. campaignId is CampaignOffer.id. If the user asks to stop preparation, the target is wrong, or status shows the wrong campaign/table, use cancel_campaign_message_preparation only for the exact active job. Low-level selectors are diagnostics and recovery only for this lane. start_campaign remains forbidden until explicit launch/start approval outside refill sends.`,
377
377
  },
378
378
  };
379
379
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sellable/mcp",
3
- "version": "0.1.420",
3
+ "version": "0.1.423",
4
4
  "type": "module",
5
5
  "description": "Sellable MCP server for Claude Code and Codex campaign workflows",
6
6
  "main": "dist/index.js",
@@ -14,6 +14,29 @@ producing duplicates. This skill never launches campaigns; customer-visible
14
14
  completion stops at paused send review.
15
15
  </role>
16
16
 
17
+ ## Invocation Mode Gate
18
+
19
+ Before any setup plan, classify the invocation mode.
20
+
21
+ - **Interactive approval mode is the default.** A bare user invocation such as
22
+ `$sellable:create-evergreen-campaigns`, "create evergreen campaigns", or
23
+ "set up evergreen" is interactive unless the prompt explicitly says `--yolo`,
24
+ scheduled, cron, automation, fresh-thread, run without approval, no user input,
25
+ or the host injects an equivalent trusted automation marker.
26
+ - In interactive approval mode, first call
27
+ `setup_evergreen_campaigns({ mode:"plan", depth:"customer_visible", ... })`
28
+ **without** `yolo:true`, render the returned `approvalSummary`, and wait for
29
+ explicit operator approval before dispatching any lane worker or creating
30
+ product state.
31
+ - In automation or explicit `--yolo` mode, pass `yolo:true` in plan mode only
32
+ and proceed without intermediate approval only when the plan returns
33
+ `autoExecutable:true`.
34
+
35
+ Short form: no explicit automation/`--yolo` marker means plan-and-ask, not run.
36
+ Short form: never infer `yolo:true` from a bare command invocation.
37
+ Short form: command hosts that want Custer-style unattended execution must
38
+ inject explicit automation context before invoking this skill.
39
+
17
40
  ## Operational Fast Path
18
41
 
19
42
  In automation or `--yolo` customer-visible setup, use this path before the
@@ -51,6 +74,21 @@ directory setup or product mutation.
51
74
  <inputs>
52
75
  The invoking prompt names the senders ("create evergreen campaigns for csreyes92 and thomas"). Resolve each via `list_senders`.
53
76
 
77
+ If the invoking prompt is a normal user chat without an explicit automation or
78
+ `--yolo` marker, run **interactive approval mode**:
79
+
80
+ - Call `setup_evergreen_campaigns({ mode:"plan", depth:"customer_visible", ... })`
81
+ without `yolo:true`.
82
+ - Render `approvalSummary.approvalQuestion` and the approval buckets
83
+ (`campaignsToCreate`, `campaignsToUpdate`, `campaignsToVerifyOnly`,
84
+ `campaignsLeftUntouched`, sender scopes, selected action ids, allowed and
85
+ forbidden side effects, blockers, and stop conditions).
86
+ - Wait for an explicit approval that names or clearly accepts the rendered plan
87
+ before dispatching lane workers or mutating product state.
88
+ - After approval, execute only the current `planRevision` and
89
+ `selectedActionIds`; re-plan if the planRevision, action ids, sender scopes,
90
+ blockers, side-effect classes, or lane bindings drift.
91
+
54
92
  If the invoking prompt says scheduled, automation, cron, heartbeat, fresh-thread,
55
93
  run without user input, or similar, run in **automation mode**:
56
94
 
@@ -294,6 +332,8 @@ route-proof approval policy. Stop and re-plan if any fresh reread changes the
294
332
  workspace id, sender ids, source/list id, campaign/table id, new campaign/table
295
333
  id, planRevision, actionId, selectedActionIds, allowed side effects, caps,
296
334
  status, or blocker set outside the approved packet.
335
+ Do not ask a vague yes/no question; show the exact bounded approval packet and
336
+ require explicit approval for that packet before mutation.
297
337
  The short rule: one approval covers the current planRevision and selectedActionIds.
298
338
  The approval clarity rule: the operator approves named campaign create/update/verify/untouched buckets and attached senders, not a generic evergreen run.
299
339
  The execution rule: act on behalf of the operator; do not ask again for substep approvals while work stays within the lane packet, approved caps, approved side-effect classes, and route-proof approval policy.
@@ -461,10 +501,22 @@ Do not paste the entire wrapper skill into worker prompts. If the parent cannot
461
501
  launch at least one visible/durable worker immediately after directory setup,
462
502
  stop with `blocked: worker_dispatch_stalled` before any mutation.
463
503
  After launching workers, poll only the expected current-run receipt paths from
464
- `workerDispatch.receiptArtifactHint`. If no worker receipt file appears within a
465
- bounded wait, for example 10 minutes in automation, stop with
466
- `blocked: worker_receipt_timeout`, include the worker ids/final-file paths/stderr
467
- if available, and do not create or repair campaign shells in the parent.
504
+ `workerDispatch.receiptArtifactHint`. Polling is progress-aware, not a blind
505
+ wall-clock cutoff: while a visible/durable worker is still active and recent
506
+ readbacks show forward progress, keep polling the expected path and do not
507
+ report `worker_receipt_timeout`. If a worker is active but the receipt is still
508
+ missing after the product postconditions appear complete, send exactly one
509
+ receipt-write recovery prompt that says the worker must write either a success
510
+ receipt or a blocked receipt at the exact path before ending. If no worker
511
+ receipt file appears after a bounded **no-progress** window, for example 10
512
+ minutes without new tool calls, final-file updates, receipt mtime changes, or
513
+ thread progress, stop with `blocked: worker_receipt_timeout`, include the worker
514
+ ids/final-file paths/stderr if available, and do not create or repair campaign
515
+ shells in the parent.
516
+ If a worker produced partial product state but no receipt, recover through the
517
+ same visible/durable worker or a compact repair worker against the existing
518
+ campaign/table ids; never create a duplicate lane campaign to recover a missing
519
+ receipt.
468
520
  Use only the repo-local `workerDispatch.receiptArtifactHint` returned by the
469
521
  current lane packet for receipt paths.
470
522
  Do not hardcode machine-specific paths, workspace ids, campaign ids, sender ids, or UAT/phase artifact paths in this MCP prompt or any worker prompt.
@@ -490,6 +542,10 @@ Lane key: <laneKey>
490
542
  Allowed side effects: <allowedSideEffects JSON>
491
543
  Write the durable receipt to this exact path: <receiptArtifactPath>
492
544
 
545
+ If your runtime exposes a goal tool, create or maintain this exact goal:
546
+ complete action <actionId> only when <receiptArtifactPath> exists as valid JSON
547
+ for this lane or when a blocked receipt has been written at that same path.
548
+
493
549
  Worker lane packet JSON:
494
550
  <lane packet JSON>
495
551
 
@@ -498,7 +554,14 @@ Use mcp__sellable only. Do not use admin tools, direct DB, Prisma, SQL, web sear
498
554
  Do not launch, start, schedule, send, or use paid InMail.
499
555
  For filter proof, durable receipt status must be `filterDecisionReceipt.status:"applied"` only; never `completed`, `confirmed`, `done`, or aliases.
500
556
  For generated messages, `update_cell` is allowed only for the semantic Approved checkbox. Never use `update_cell` for generated message text/body/sample copy. Bad copy requires `revise_message_template_and_rerun` or brief/template revision plus Generate Message rerun. Any generated-message cell override is `blocked: generated_message_cell_override`.
501
- Complete only this lane, write the required durable receipt JSON, and stop.
557
+ Complete only this lane. Do not end with narration only. Before your final
558
+ response, run a local file-existence and JSON self-check for
559
+ <receiptArtifactPath>. If the lane succeeded, write the canonical success
560
+ receipt there; if any postcondition, quality gate, verifier schema, or file
561
+ write blocks, write a canonical blocked receipt there with `status:"blocked"`,
562
+ `blocker`, `campaignId`, `tableId`, `actionId`, `laneKey`, `planRevision`, and
563
+ the latest product readback. The terminal condition is filesystem proof that
564
+ <receiptArtifactPath> exists.
502
565
  WORKER_PROMPT
503
566
 
504
567
  # Multi-worker launcher equivalent:
@@ -1028,6 +1091,13 @@ every canonical verifier object under `createCampaignStepReceipt`, including
1028
1091
  `campaignBriefReceipt`, `sourceDecisionReceipt`, `filterDecisionReceipt`,
1029
1092
  `messageDraftingReceipt`, `reviewBatchReceipt`, `sequenceReceipt`,
1030
1093
  `finalPausedSendProof`, and `verifyCall`.
1094
+ Write the compact verifier payload to a temporary JSON file and load that object
1095
+ directly into the MCP verify call. Do not manually paste, retype, truncate, or
1096
+ merge full receipt JSON into the tool call. If a verify attempt fails because
1097
+ the payload was malformed, overlong, manually edited, missing a receipt, or used
1098
+ a later reuse planRevision/actionId set, rebuild the compact payload from the
1099
+ durable receipt files and retry once with the original execution
1100
+ `planRevision`/`selectedActionIds` before reporting a verifier blocker.
1031
1101
  When compacting, normalize `finalPausedSendProof.currentStep` and
1032
1102
  `finalPausedSendProof.campaignStatus` from nested pause/navigation readbacks if
1033
1103
  the worker receipt stored those values under `pauseCampaignResult`,
@@ -1042,6 +1112,7 @@ Short form: Do not use complex inline `jq` for receipt compaction; use Node.
1042
1112
  Short form: Compact receipts must preserve `researchSenderReceipt`.
1043
1113
  Short form: Compact receipts must preserve direct `finalPausedSendProof.currentStep` and `campaignStatus`.
1044
1114
  Short form: Never change planRevision/actionId/laneKey while compacting receipts.
1115
+ Short form: Do not hand-build giant receipt tool-call payloads; load compact JSON from disk.
1045
1116
 
1046
1117
  Parent repair dispatch uses the same compact discipline. If the parent sample
1047
1118
  quality gate fails after a worker receipt, do not launch a repair worker by
@@ -1121,10 +1192,12 @@ Message, and verify current-revision sample messages before final completion.
1121
1192
  `list_tables` and the managed waterfall as authoritative for orientation
1122
1193
  before the command-backed plan; `get_campaigns` is a recent campaign page and
1123
1194
  may miss canonical evergreen lanes. Match existing campaigns/tables/waterfall
1195
+ Short form: Treat `list_tables` and the managed waterfall as authoritative.
1124
1196
  slots to the plan by name (case-insensitive, ignore suffixes like "(Copy)")
1125
1197
  and stored slot identity. `list_tables.campaignStatus` and
1126
1198
  `list_tables.dashboardBucket` are part of the identity check: a matching
1127
1199
  `ARCHIVED` table/campaign is not a plain `REUSE`. If it is the canonical prod
1200
+ Short form: a matching `ARCHIVED` table/campaign is not a plain `REUSE`.
1128
1201
  slot and the invocation explicitly allows dashboard visibility repair, repair
1129
1202
  it to `PAUSED`; otherwise treat it as archived inventory only. After
1130
1203
  `setup_evergreen_campaigns({ mode:"plan" })` returns, that plan is