@sellable/mcp 0.1.131 → 0.1.133

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.
@@ -190,7 +190,7 @@ export const campaignToolDefinitions = [
190
190
  },
191
191
  watchNarration: {
192
192
  type: "object",
193
- description: "Single structured watch-guide narration object to send with currentStep changes. Include stage, headline, visibleState, agentIntent, and nextAction so the browser guide mirrors Codex progress.",
193
+ description: "Single structured watch-guide narration object to send with currentStep changes. Include what just happened, what the browser is showing now, what Codex is doing next, and nextAction so the browser guide mirrors Codex progress.",
194
194
  properties: {
195
195
  stage: {
196
196
  type: "string",
@@ -211,7 +211,13 @@ export const campaignToolDefinitions = [
211
211
  blockedReason: { type: ["string", "null"] },
212
212
  workerStatuses: { type: "object" },
213
213
  },
214
- required: ["stage", "headline", "visibleState", "agentIntent"],
214
+ required: [
215
+ "stage",
216
+ "headline",
217
+ "visibleState",
218
+ "agentIntent",
219
+ "nextAction",
220
+ ],
215
221
  },
216
222
  leadSourceType: {
217
223
  type: ["string", "null"],
@@ -283,7 +289,7 @@ export const campaignToolDefinitions = [
283
289
  },
284
290
  watchNarration: {
285
291
  type: "object",
286
- description: "Single structured watch-guide narration object to send with currentStep changes. Include stage, headline, visibleState, agentIntent, and nextAction so the browser guide mirrors Codex progress. Do not send separate guideHeadline/guideBody fields.",
292
+ description: "Single structured watch-guide narration object to send with currentStep changes. Include what just happened, what the browser is showing now, what Codex is doing next, and nextAction so the browser guide mirrors Codex progress. Do not send separate guideHeadline/guideBody fields.",
287
293
  properties: {
288
294
  stage: {
289
295
  type: "string",
@@ -304,7 +310,13 @@ export const campaignToolDefinitions = [
304
310
  blockedReason: { type: ["string", "null"] },
305
311
  workerStatuses: { type: "object" },
306
312
  },
307
- required: ["stage", "headline", "visibleState", "agentIntent"],
313
+ required: [
314
+ "stage",
315
+ "headline",
316
+ "visibleState",
317
+ "agentIntent",
318
+ "nextAction",
319
+ ],
308
320
  },
309
321
  interactionMode: {
310
322
  type: "string",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sellable/mcp",
3
- "version": "0.1.131",
3
+ "version": "0.1.133",
4
4
  "type": "module",
5
5
  "description": "Sellable MCP server for Claude Code and Codex campaign workflows",
6
6
  "main": "dist/index.js",
@@ -93,11 +93,15 @@ Optional debug/UAT draft directory, disabled in normal customer runs:
93
93
  campaign. If shell creation fails, stop and surface the error. There is no
94
94
  no-shell mint fallback in the active shell-first flow.
95
95
  - The main thread owns watch navigation. Before expensive work in each major
96
- stage, call `update_campaign({ campaignId, currentStep })` with the visible UI
97
- step the user should be watching, then continue the work from MCP/product
98
- state. Use `create-offer` for the brief, `pick-provider` or the selected
99
- provider step while sourcing, `filter-choice` after the 15-row review batch is
100
- imported, `messages`/`auto-execute-messaging` for message work,
96
+ stage, call `update_campaign({ campaignId, currentStep, watchNarration })`
97
+ with the visible UI step the user should be watching, then continue the work
98
+ from MCP/product state. Every currentStep switch in a watched run must refresh
99
+ `watchNarration` in the same visible beat. The copy must say what just
100
+ happened, what the browser is showing now, what Codex is doing next, and what
101
+ the user should do next. Use `create-offer` for the brief, `pick-provider` or
102
+ the selected provider step while sourcing, `filter-choice` after the 15-row
103
+ review batch is imported, `apply-icp-rubric` while template approval is
104
+ pending on Filter Leads, `auto-execute-messaging` for approved message work,
101
105
  `awaiting-user-greenlight` for Settings, and `validate-sample` only for
102
106
  recovery/legacy validation if reached. Do not advance `currentStep` backward.
103
107
  - After the shell is minted, normal resume paths read the CampaignOffer first.
@@ -531,12 +535,13 @@ until the filter choice is resolved and the template is ready for approval.`
531
535
  `approval-packet.md`.
532
536
  - Run the dependency chain as a DAG: `create-campaign-brief` -> `find leads` ->
533
537
  bounded import/confirm. Once the source decision, lead sample, and campaign
534
- table `workflowTableId` exist, the normal path is the `filter-choice` user
535
- gate. If the user chooses filters, `post-lead-workstreams` launches filter
536
- leads and can let the Message Draft Builder run from the same basis (live
537
- campaign brief, source decision, lead sample, and the imported review-batch
538
- rows) as separate workstreams when the host exposes the named Sellable
539
- post-find-leads agents or real background work.
538
+ table `workflowTableId` exist, immediately start the Message Draft Builder
539
+ from the live campaign brief, approved source, source list, and imported
540
+ review-batch rows before telling the user it is in progress. The normal path
541
+ then reaches the `filter-choice` user gate. If the user chooses filters,
542
+ `post-lead-workstreams` launches filter leads and reuses the in-flight Message
543
+ Draft Builder work from the same basis as separate workstreams when the host
544
+ exposes the named Sellable post-find-leads agents or real background work.
540
545
  First call `get_post_find_leads_scout_registry` and use the returned
541
546
  canonical `name` values only when those names are available in the current
542
547
  runtime. In Claude Code, use both returned Task/Agent subagents in the same
@@ -548,12 +553,14 @@ until the filter choice is resolved and the template is ready for approval.`
548
553
  `filter-rubric` and `message-generation` steps remain focused retry and
549
554
  resume targets.
550
555
  - Message generation does not need `lead-filter.md` to start, but it is only
551
- background template drafting until the user approves the template. The moment the
552
- bounded review batch is imported, `workflowTableId` is ready, and the user is
553
- on/after filter choice, the Message Draft Builder may start from the live
554
- campaign brief, source decision, lead sample, and imported campaign table
555
- sample. It can prepare proof inventory, token strategy, and candidate angles
556
- while filter-leads tightens keep/exclude rules. Template review cannot start
556
+ background template drafting until the user approves the template. The moment
557
+ the bounded review batch is imported, `workflowTableId` is ready, and
558
+ `get_rows_minimal` proves rows exist, the Message Draft Builder must start
559
+ from the live campaign brief, source decision, lead sample, and imported
560
+ campaign table sample. Do not show `Message Draft Builder: In Progress` unless
561
+ the worker or parent-thread fallback has actually started. It can prepare
562
+ proof inventory, token strategy, and candidate angles while filter-leads
563
+ tightens keep/exclude rules. Template review cannot start
557
564
  until filter choice is answered and rubrics are saved. If filters are enabled,
558
565
  the watched browser stays on Filter Leads while the template is approved in
559
566
  chat. After approval is saved to the campaign brief, queue the bounded
@@ -590,13 +597,18 @@ until the filter choice is resolved and the template is ready for approval.`
590
597
  `currentStep` before waiting on source-scout results, so relevant search tabs
591
598
  appear in real time.
592
599
  - For post-lead workstreams, first call
593
- `get_post_find_leads_scout_registry` and launch exactly the returned
594
- `filter-leads` and `message-generation` scouts when real subagents are
595
- available. This is the two Task/Agent subagents path for the
596
- `post-lead-workstreams` step. This is the same registry pattern as source scouting, but the
597
- trigger is source approval and the join gate is both `lead-filter.md` and
598
- `message-validation.md` existing from the same live campaign brief/source
599
- decision/lead-sample basis.
600
+ `get_post_find_leads_scout_registry` after the review batch import confirms
601
+ rows. Launch the returned `message-generation` scout immediately, before the
602
+ filter-choice/watch copy says message work is running. When the user chooses
603
+ filters, launch the returned `filter-leads` scout and reuse the in-flight
604
+ message branch. This is the Task/Agent subagents path for the
605
+ `post-lead-workstreams` step. This is the same registry pattern as source
606
+ scouting, but the trigger is bounded review-batch import and the join gate is
607
+ both `lead-filter.md` and `message-validation.md` existing from the same live
608
+ campaign brief/source decision/imported review-batch basis. When the host can
609
+ launch both branches together, this is still the two Task/Agent subagents
610
+ path; otherwise message-generation starts first and filter-leads joins after
611
+ the filter choice.
600
612
  - Never run a downstream stage until the active `flow.v2.json` step's required
601
613
  campaign state and required normal inputs exist. In normal customer runs, do
602
614
  not create optional debug artifacts just to satisfy a file gate.
@@ -8,6 +8,30 @@
8
8
  "requiredArtifactsApplyWhen": "legacy resume, fixture validation, or explicit debug/UAT mode only",
9
9
  "customerFacingAccess": "Watch link, live campaign currentStep, and concise watchNarration"
10
10
  },
11
+ "watchNarrationTransitionContract": {
12
+ "rule": "Every watched currentStep switch must update watchNarration in the same visible beat. The narration must clearly say what just happened, what page or state the browser is showing now, what Codex is doing next, and what the user should do next. Do not persist optimistic future-step copy; if the step cannot be shown yet, keep the previous currentStep and narrate the current work instead.",
13
+ "requiredFields": [
14
+ "stage",
15
+ "headline",
16
+ "visibleState",
17
+ "agentIntent",
18
+ "nextAction"
19
+ ],
20
+ "copyMustInclude": [
21
+ "what just happened",
22
+ "current visible page/state",
23
+ "next user action",
24
+ "safety boundary when work is gated"
25
+ ],
26
+ "appliesToTools": [
27
+ "create_campaign",
28
+ "update_campaign",
29
+ "update_campaign_brief when it unlocks a step",
30
+ "attach_selected_sender",
31
+ "attach_recommended_sequence",
32
+ "start_campaign"
33
+ ]
34
+ },
11
35
  "commitGateChoices": [
12
36
  "approve",
13
37
  "revise-brief",
@@ -1031,6 +1055,44 @@
1031
1055
  ],
1032
1056
  "rule": "If confirm_lead_list succeeds but get_rows_minimal returns zero imported review-batch rows, do not advance to filter-choice. Keep the user in source/import recovery and ask for retry import, import more from the approved source, or revise source."
1033
1057
  },
1058
+ {
1059
+ "tool": "get_post_find_leads_scout_registry",
1060
+ "purpose": "load_post_import_message_draft_builder_registry",
1061
+ "requiredBeforeMessageDraftLaunch": true
1062
+ },
1063
+ {
1064
+ "action": "launch_message_draft_builder_after_review_batch_import",
1065
+ "mode": "background_required_when_host_supports_subagents",
1066
+ "registryRequired": "get_post_find_leads_scout_registry",
1067
+ "branch": {
1068
+ "name": "message-generation",
1069
+ "target": "generate-messages",
1070
+ "workerName": "post-find-leads-message-scout",
1071
+ "inputs": [
1072
+ "campaignBrief",
1073
+ "selected source decision",
1074
+ "selectedLeadListId source list",
1075
+ "workflowTableId imported 15-row review batch"
1076
+ ],
1077
+ "toolCallRequiredBeforeArtifacts": [
1078
+ "get_subskill_prompt({ subskillName: \"generate-messages\" }) until hasMore=false"
1079
+ ],
1080
+ "producesArtifacts": [
1081
+ "template recommendation",
1082
+ "token fill rules",
1083
+ "rendered sample"
1084
+ ],
1085
+ "optionalProducesArtifacts": [
1086
+ "message-validation.md",
1087
+ "message-prep.md",
1088
+ "message-candidate-drafts.md"
1089
+ ],
1090
+ "ownership": "proof inventory, token strategy, angle drafting, skeptical-prospect review, and selected winner only"
1091
+ },
1092
+ "requiredBeforeFilterChoiceWatchCopy": true,
1093
+ "rule": "Immediately after get_rows_minimal proves the bounded review batch is non-empty, start the Message Draft Builder from the live campaign brief, approved source, selectedLeadListId, and imported workflowTableId rows. Do this before the watched campaign moves to filter-choice or Filter Rules so any guide copy saying Message Draft Builder is In Progress is true.",
1094
+ "fallback": "If the host cannot launch the post-find-leads-message-scout in the background, load the full generate-messages prompt in the parent thread and start the message-generation work before reporting Message Draft Builder as In Progress. If message work cannot start, do not show running copy; surface pending/blocked template-start copy instead."
1095
+ },
1034
1096
  {
1035
1097
  "tool": "update_campaign",
1036
1098
  "requiredFields": [
@@ -1041,9 +1103,10 @@
1041
1103
  "requiredValues": {
1042
1104
  "currentStep": "filter-choice",
1043
1105
  "watchNarration.stage": "fit-message",
1044
- "watchNarration.headline": "Added to Campaign"
1106
+ "watchNarration.headline": "Added to Campaign",
1107
+ "watchNarration.workerStatuses.messageDraftBuilder": "running_after_launch_message_draft_builder_after_review_batch_import_only"
1045
1108
  },
1046
- "watchNarrationRule": "After the bounded review batch is present and non-empty, update the watched guide to `Added to Campaign` and ask the user to Choose filters or skip. This is filter intent only: do not say filtering the batch before rubrics and message approval are saved. The Message Draft Builder may start in the background, but no enrichment, filtering, Generate Message cells, sequence, or sending starts until the message template is approved and saved to the campaign brief. Do not set enableICPFilters=true until save_rubrics succeeds with active criteria."
1109
+ "watchNarrationRule": "After the bounded review batch is present and non-empty, and after launch_message_draft_builder_after_review_batch_import has actually started or resumed message-generation, update the watched guide to `Added to Campaign` and ask the user to Choose filters or skip. This is filter intent only: do not say filtering the batch before rubrics and message approval are saved. The Message Draft Builder should be reported as In Progress only if the worker or parent-thread fallback has actually started. No enrichment, filtering, Generate Message cells, sequence, or sending starts until the message template is approved and saved to the campaign brief. Do not set enableICPFilters=true until save_rubrics succeeds with active criteria."
1047
1110
  }
1048
1111
  ],
1049
1112
  "allowedTools": [
@@ -1059,7 +1122,11 @@
1059
1122
  "load_csv_linkedin_leads",
1060
1123
  "load_csv_domains",
1061
1124
  "get_provider_prompt",
1062
- "get_campaign"
1125
+ "get_campaign",
1126
+ "get_post_find_leads_scout_registry",
1127
+ "get_subskill_prompt",
1128
+ "Task",
1129
+ "spawn_agent"
1063
1130
  ],
1064
1131
  "doNotAllow": [
1065
1132
  "queue_cells",
@@ -1187,7 +1254,6 @@
1187
1254
  {
1188
1255
  "name": "message-generation",
1189
1256
  "target": "generate-messages",
1190
- "mode": "DRY MODE",
1191
1257
  "inputs": [
1192
1258
  "campaignBrief",
1193
1259
  "selected source decision",
@@ -1204,15 +1270,18 @@
1204
1270
  "message-prep.md",
1205
1271
  "message-candidate-drafts.md"
1206
1272
  ],
1207
- "ownership": "proof inventory, token strategy, angle drafting, skeptical-prospect review, and selected winner only"
1273
+ "ownership": "proof inventory, token strategy, angle drafting, skeptical-prospect review, and selected winner only",
1274
+ "mode": "background_template_builder_reuse_or_launch_if_missing",
1275
+ "alreadyStartedBy": "auto-execute-leads.launch_message_draft_builder_after_review_batch_import"
1208
1276
  }
1209
1277
  ],
1210
- "earlyMessageStartRule": "After auto-execute-leads confirms the 15-row review batch, workflowTableId is ready, and the user has reached filter choice, the Message Draft Builder may start from campaignBrief, selected source state, selectedLeadListId, and imported campaign table rows, including while the user is on filter choice. This background template draft is provisional speed work only. The message worker must load the full generate-messages prompt; parent-thread fallback loads the same prompt and may use references/message-review-safety-gate.md only as a supplemental approval checklist. It may run beside filter-leads, but no enrichment, ICP scoring, filtering, or product Generate Message cells can queue until save_rubrics and update_campaign_brief both succeed.",
1278
+ "earlyMessageStartRule": "After auto-execute-leads confirms the 15-row review batch, workflowTableId is ready, and get_rows_minimal proves rows exist, the Message Draft Builder must start immediately from campaignBrief, selected source state, selectedLeadListId, and imported campaign table rows. post-lead-workstreams must reuse that in-flight message branch; if it is missing, launch it before saving rubrics or claiming message progress. This background template draft is provisional speed work only. The message worker must load the full generate-messages prompt; parent-thread fallback loads the same prompt and may use references/message-review-safety-gate.md only as a supplemental approval checklist. It may run beside filter-leads, but no enrichment, ICP scoring, filtering, or product Generate Message cells can queue until save_rubrics and update_campaign_brief both succeed.",
1279
+ "workerStatusContract": "Only show Lead Fit Builder or Message Draft Builder as In Progress when a Task/spawn_agent branch actually started and returned a live branch id, or when the parent-thread fallback is actively doing that branch before the next user-visible step. If a branch is completed in the parent thread, show it as Ready. After update_campaign_brief saves the approved template, Message Draft Builder is Ready even if row-level Generate Message cells have not completed.",
1211
1280
  "finalMessageReconcileRule": "The message recommendation may start before lead-filter.md exists, but before message-review it must cite only imported review-batch rows that still pass the saved rubric/filter results. If the selected winner depends on a row later excluded by saved filters, revise message-generation before message review.",
1212
1281
  "finalTemplateReviewLockRule": "template review does not require a Use Template vs AI Generated mode choice. After filters are enabled, lock template review until saved rubrics exist and the draft reconciles to the approved brief, selected source, and imported review batch. Keep the watched browser on Filter Leads while approval is pending. After approval writes the Approved Message Template into campaignBrief, queue enrichment/ICP scoring for the bounded review batch; prefer at least 2 usable passing/probably passing rows before moving the watched browser to Messages, with weak-sample escalation if only 1 passes.",
1213
- "claudeRule": "In Claude Code, launch both returned post-find-leads scouts with Task/Agent subagents in the same assistant message only when the current session lists those agent names. Do not run filter first and then message generation unless subagents/background work are unavailable.",
1214
- "codexRule": "In Codex, launch both returned post-find-leads scout names as disjoint subagents in the same assistant turn only when the host exposes those custom agents for this run. If the host cannot spawn them, run the same branches sequentially and say so.",
1215
- "fallback": "If real parallel branches are unavailable or the named agents are absent, run filter-leads and then message-generation in the parent thread with product MCP tools/assets. Do not customer-surface agent install status, and do not claim background or parallel work in that fallback."
1282
+ "claudeRule": "In Claude Code, launch the returned message-generation scout immediately after post-import rows exist, then launch/reuse the filter-leads scout when the user chooses filters. When both named agents are available and both branches are ready in the same assistant message, use Task/Agent subagents in parallel; otherwise never claim background message progress until message-generation actually started.",
1283
+ "codexRule": "In Codex, launch the returned message-generation scout immediately after post-import rows exist, then launch/reuse filter-leads when filters are chosen. Use disjoint subagents only when the host exposes those custom agents; otherwise start the same message-generation branch in the parent thread before reporting it as running.",
1284
+ "fallback": "If real parallel branches are unavailable or the named agents are absent, start message-generation in the parent thread immediately after post-import, then run filter-leads from the same imported review batch. Do not customer-surface agent install status, and do not claim background or parallel work unless that work actually started."
1216
1285
  },
1217
1286
  {
1218
1287
  "action": "wait_for_lead_filter_rules",
@@ -1307,7 +1376,7 @@
1307
1376
  ],
1308
1377
  "toolRules": [
1309
1378
  "The post-lead workstreams are disjoint: filter-leads owns lead-filter.md/rubric.json; message-generation owns message-validation.md/message-prep.md/message-candidate-drafts.md.",
1310
- "message-generation can start before saved filters, but message-review cannot start until saved filters and the message recommendation reconcile against the same imported review-batch rows.",
1379
+ "message-generation must start immediately after the post-import review batch exists, before saved filters; message-review cannot start until saved filters and the message recommendation reconcile against the same imported review-batch rows.",
1311
1380
  "Do not let filter-leads create a new message sample. Do not let message-generation fetch new prospects.",
1312
1381
  "Before writing message-validation.md or returning a template recommendation, message-generation must load the full get_subskill_prompt({ subskillName: \"generate-messages\" }) prompt and use campaignBrief, selected source state, selectedLeadListId, and imported review-batch rows as the source of truth. Optional debug markdown files are not durable state.",
1313
1382
  "Do not queue enrichment, ICP scoring, filtering, or Generate Message cells after saved rubrics alone. Wait until the approved message template is synced into campaignBrief, then queue only bounded review-batch enrichCellId values.",
@@ -1522,7 +1591,7 @@
1522
1591
  {
1523
1592
  "action": "run_or_reconcile_subskill",
1524
1593
  "target": "generate-messages",
1525
- "mode": "DRY MODE",
1594
+ "mode": "approved_template_builder",
1526
1595
  "sampleSource": "imported review-batch rows from selectedLeadListId",
1527
1596
  "toolCallRequiredBeforeArtifacts": [
1528
1597
  "get_subskill_prompt({ subskillName: \"generate-messages\", offset, limit }) until hasMore=false",
@@ -1743,6 +1812,25 @@
1743
1812
  "requiredBeforeCascade": true,
1744
1813
  "writesCampaignState": "campaignBrief.Approved Message Template"
1745
1814
  },
1815
+ {
1816
+ "action": "advance_watch_after_template_saved_before_cascade",
1817
+ "tool": "update_campaign",
1818
+ "requires": [
1819
+ "campaignId",
1820
+ "campaignBrief.Approved Message Template",
1821
+ "leadScoringRubrics"
1822
+ ],
1823
+ "requiredValues": {
1824
+ "currentStep": "apply-icp-rubric",
1825
+ "enableICPFilters": true,
1826
+ "watchNarration.stage": "fit-message",
1827
+ "watchNarration.headline": "Template saved",
1828
+ "watchNarration.workerStatuses.leadFitBuilder": "ready",
1829
+ "watchNarration.workerStatuses.messageDraftBuilder": "ready"
1830
+ },
1831
+ "when": "immediately_after_update_campaign_brief_succeeds_before_get_rows_or_queue_cells",
1832
+ "watchNarrationRule": "Say the approved message template was saved into the campaign brief, the browser is staying on Filter Leads, and Codex is now queueing only the bounded review-batch enrichment/ICP scoring cascade. The Message Draft Builder must be shown as Ready, not In Progress, after this point."
1833
+ },
1746
1834
  {
1747
1835
  "tool": "get_rows_minimal",
1748
1836
  "purpose": "read_bounded_review_batch_cell_ids_after_message_approval",
@@ -2436,7 +2524,8 @@
2436
2524
  "requiredValues": {
2437
2525
  "currentStep": "awaiting-user-greenlight",
2438
2526
  "watchNarration.stage": "review-ready"
2439
- }
2527
+ },
2528
+ "watchNarrationRule": "Say the approved-template cascade has produced reviewable messages and the browser is moving to Settings. Include what just happened, that Settings is now visible for sender selection, and that nothing launches until the user greenlights the final send."
2440
2529
  }
2441
2530
  ],
2442
2531
  "allowedTools": [
@@ -2492,9 +2581,11 @@
2492
2581
  {
2493
2582
  "tool": "update_campaign",
2494
2583
  "requiredValues": {
2495
- "currentStep": "settings"
2584
+ "currentStep": "settings",
2585
+ "watchNarration.stage": "review-ready"
2496
2586
  },
2497
- "purpose": "park the watched UI on Settings before sender selection"
2587
+ "purpose": "park the watched UI on Settings before sender selection",
2588
+ "watchNarrationRule": "Say message review is complete and the browser is now on Settings so the user can connect or choose a sender. Include the safety boundary that no launch happens from Settings."
2498
2589
  },
2499
2590
  {
2500
2591
  "tool": "get_campaign_navigation_state",
@@ -2532,8 +2623,10 @@
2532
2623
  "senderIds": [
2533
2624
  "{selectedSenderId}"
2534
2625
  ],
2535
- "currentStep": "sequence"
2536
- }
2626
+ "currentStep": "sequence",
2627
+ "watchNarration.stage": "review-ready"
2628
+ },
2629
+ "watchNarrationRule": "Say the sender was attached and the browser is moving to Sequence. Explain that Codex is binding the recommended sequence next and that launch still needs explicit greenlight."
2537
2630
  },
2538
2631
  {
2539
2632
  "tool": "get_campaign_navigation_state",
@@ -2547,15 +2640,18 @@
2547
2640
  "campaignId": "{campaignId}",
2548
2641
  "currentStep": "send"
2549
2642
  },
2550
- "purpose": "bind the tier-aware recommended sequence before the launch decision"
2643
+ "purpose": "bind the tier-aware recommended sequence before the launch decision",
2644
+ "watchNarrationRule": "The sequence tool may persist currentStep. If it does, the same visible beat must still explain that the recommended sequence is attached and Send is next; otherwise follow with update_campaign and watchNarration before asking for launch greenlight."
2551
2645
  },
2552
2646
  {
2553
2647
  "tool": "update_campaign",
2554
- "when": "sequence attach response does not persist currentStep",
2648
+ "when": "after recommended sequence attach; required if sequence attach did not persist watchNarration with currentStep",
2555
2649
  "requiredValues": {
2556
- "currentStep": "send"
2650
+ "currentStep": "send",
2651
+ "watchNarration.stage": "review-ready"
2557
2652
  },
2558
- "purpose": "show the Send tab once sender and sequence are ready"
2653
+ "purpose": "show the Send tab once sender and sequence are ready",
2654
+ "watchNarrationRule": "Say the sender and sequence are ready and the browser is now on Send. The next action is the final launch greenlight; do not imply anything has sent yet."
2559
2655
  },
2560
2656
  {
2561
2657
  "tool": "get_campaign_navigation_state",
@@ -2676,7 +2772,8 @@
2676
2772
  "requiredFields": [
2677
2773
  "campaignId"
2678
2774
  ],
2679
- "persistsCurrentStep": "running"
2775
+ "persistsCurrentStep": "running",
2776
+ "watchNarrationRule": "After start_campaign succeeds, the running state must say the final greenlight was accepted, the campaign is now live/running, and the user can watch progress from the campaign."
2680
2777
  },
2681
2778
  {
2682
2779
  "action": "surface_watch_link",
@@ -28,6 +28,9 @@ stage labels. The driver label comes from the watch URL mode (`mode=claude` or
28
28
  - One primary user action per frame.
29
29
  - Headline under 8 words.
30
30
  - Body is 1-2 short sentences total.
31
+ - Every watched step switch must refresh `watchNarration` with current-tense
32
+ copy that says what just happened, what the browser is showing now, what
33
+ Codex is doing next, and what the user should do next.
31
34
  - Say where the user acts: usually `in Claude Code`, `in Codex`, or `in chat`,
32
35
  matching the host that created the watch URL.
33
36
  - `Find Leads` is only the static progress label. The copy must name the active