@sellable/mcp 0.1.134 → 0.1.136

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.
@@ -168,10 +168,7 @@
168
168
  {
169
169
  "action": "confirm_ambiguous_campaign_focus_with_structured_gate",
170
170
  "when": "after_identity_before_founder_strategy_source_packet",
171
- "allowedTools": [
172
- "AskUserQuestion",
173
- "request_user_input"
174
- ],
171
+ "allowedTools": ["AskUserQuestion", "request_user_input"],
175
172
  "questionType": "single-choice only; plain chat only for free-text custom detail",
176
173
  "multiSelect": false,
177
174
  "maxOptions": 4,
@@ -273,11 +270,7 @@
273
270
  "Risks",
274
271
  "What happens next"
275
272
  ],
276
- "mustRunBefore": [
277
- "optional_debug_artifact_writer",
278
- "Bash",
279
- "mkdir"
280
- ],
273
+ "mustRunBefore": ["optional_debug_artifact_writer", "Bash", "mkdir"],
281
274
  "chatRenderRule": "After synthesizing the brief, render the approval-ready brief in chat before any optional local folder creation, file write, or artifact copy. Normal customer runs should not create local draft files here. The user should see useful brief content and live campaign state, not local persistence chrome. Do not ask the user to approve a hidden artifact."
282
275
  },
283
276
  {
@@ -308,10 +301,7 @@
308
301
  "watchNarration.stage": "brief-review"
309
302
  },
310
303
  "briefMode": "v1 campaign brief shown to the user; not an empty placeholder",
311
- "capture": [
312
- "campaignId",
313
- "watchUrl"
314
- ],
304
+ "capture": ["campaignId", "watchUrl"],
315
305
  "surfaceWatchLink": true,
316
306
  "watchLinkRule": "Show the link only after the brief is on the campaign, so the user lands on a useful brief instead of an empty campaign.",
317
307
  "onFailure": "stop_and_surface_campaign_shell_creation_error; no no-shell mint fallback in shell-first flow",
@@ -322,9 +312,7 @@
322
312
  "enrich_with_prospeo",
323
313
  "bulk_enrich_with_prospeo"
324
314
  ],
325
- "forbiddenUntilSenderAndSequenceReady": [
326
- "start_campaign"
327
- ],
315
+ "forbiddenUntilSenderAndSequenceReady": ["start_campaign"],
328
316
  "canonicalStateWrites": [
329
317
  "campaignId",
330
318
  "watchUrl",
@@ -336,11 +324,7 @@
336
324
  {
337
325
  "action": "surface_campaign_shell_watch_link",
338
326
  "reference": "references/watch-link-handoff.md",
339
- "requires": [
340
- "campaignId",
341
- "watchUrl",
342
- "campaignBrief"
343
- ],
327
+ "requires": ["campaignId", "watchUrl", "campaignBrief"],
344
328
  "requiredVisibleContent": [
345
329
  "draft campaign shell",
346
330
  "brief is already there",
@@ -348,11 +332,7 @@
348
332
  "no leads import and nothing sends yet",
349
333
  "Watch link:"
350
334
  ],
351
- "requiresCampaignState": [
352
- "campaignId",
353
- "watchUrl",
354
- "campaignBrief"
355
- ],
335
+ "requiresCampaignState": ["campaignId", "watchUrl", "campaignBrief"],
356
336
  "watchUrlSource": "create_campaign.watchUrl",
357
337
  "requiredWatchUrlShape": "direct /campaign-builder/{campaignId}?mode={claude|codex} watch URL with token auto-login and workspace routing",
358
338
  "codexBrowserHandoff": {
@@ -395,23 +375,14 @@
395
375
  "bulk_enrich_with_prospeo"
396
376
  ],
397
377
  "watchRequired": false,
398
- "waitFor": [
399
- "brief_ready",
400
- "campaign_shell_created",
401
- "confirm_with_user"
402
- ],
378
+ "waitFor": ["brief_ready", "campaign_shell_created", "confirm_with_user"],
403
379
  "transitions": {
404
380
  "brief_ready": "brief-review",
405
381
  "confirm_with_user": "brief-review",
406
382
  "campaign_shell_created": "brief-review"
407
383
  },
408
- "optionalProducesArtifacts": [
409
- "lead-source-intake.json"
410
- ],
411
- "debugProducesArtifacts": [
412
- "brief.md",
413
- "campaign-shell.json"
414
- ]
384
+ "optionalProducesArtifacts": ["lead-source-intake.json"],
385
+ "debugProducesArtifacts": ["brief.md", "campaign-shell.json"]
415
386
  },
416
387
  {
417
388
  "id": "brief-review",
@@ -439,9 +410,8 @@
439
410
  "then I will find good-fit leads"
440
411
  ],
441
412
  "minimumVisibleBriefDetail": "full_readable_brief_before_question",
442
- "requiredCampaignLinks": [
443
- "watchUrl"
444
- ],
413
+ "forbiddenSubstituteApprovalSurface": "Do not replace the visible brief with a summary like `the brief is visible there`; the user must see the actual campaign brief content in chat before the approval question.",
414
+ "requiredCampaignLinks": ["watchUrl"],
445
415
  "campaignLinkTiming": "before_approval_question",
446
416
  "questionToolGate": "Do not call AskUserQuestion or request_user_input for brief approval until the immediately preceding customer-visible chat text includes `Watch link:` with the create_campaign.watchUrl for this campaign shell.",
447
417
  "missingWatchLinkRecovery": "If the brief approval question would appear before the watch link, stop and send the rendered brief checkpoint plus `Watch link: {watchUrl}` first; then ask the approve/revise question.",
@@ -462,19 +432,14 @@
462
432
  "mustNotCallQuestionToolUntil": "The watch link has already been shown in normal chat text after create_campaign returned watchUrl.",
463
433
  "autoContinueWhen": {
464
434
  "artifactStatus": "confirmed",
465
- "confidenceIn": [
466
- "high",
467
- "medium"
468
- ],
435
+ "confidenceIn": ["high", "medium"],
469
436
  "noOpenStrategicTradeoff": true,
470
437
  "noMissingRequiredInputs": true
471
438
  }
472
439
  },
473
440
  {
474
441
  "action": "optional_debug_artifact_sync",
475
- "artifacts": [
476
- "brief.md"
477
- ],
442
+ "artifacts": ["brief.md"],
478
443
  "debugOrUatOnly": true,
479
444
  "requiredBeforeTransition": false,
480
445
  "reconcileRule": "If debug/UAT artifacts are enabled, verify the sidecar persisted the same brief shown in chat. Normal customer Find Leads work reads CampaignOffer.campaignBrief, not brief.md.",
@@ -483,10 +448,7 @@
483
448
  {
484
449
  "action": "sync_approved_brief_to_campaign_shell",
485
450
  "tool": "update_campaign",
486
- "requires": [
487
- "campaignId",
488
- "campaignBrief"
489
- ],
451
+ "requires": ["campaignId", "campaignBrief"],
490
452
  "when": "after_user_brief_confirmed_or_auto_continue",
491
453
  "fields": [
492
454
  "campaignBrief",
@@ -495,10 +457,7 @@
495
457
  ],
496
458
  "fallback": "If campaignId is missing from CampaignOffer state, stop; shell-first flow requires an existing campaign before sourcing leads.",
497
459
  "readsCampaignStateFirst": true,
498
- "debugArtifacts": [
499
- "campaign-shell.json",
500
- "brief.md"
501
- ]
460
+ "debugArtifacts": ["campaign-shell.json", "brief.md"]
502
461
  }
503
462
  ],
504
463
  "requiredArtifacts": [],
@@ -520,22 +479,14 @@
520
479
  "bulk_enrich_with_prospeo"
521
480
  ],
522
481
  "watchRequired": false,
523
- "waitFor": [
524
- "user_brief_confirmed",
525
- "revise_brief",
526
- "auto_continue"
527
- ],
482
+ "waitFor": ["user_brief_confirmed", "revise_brief", "auto_continue"],
528
483
  "transitions": {
529
484
  "user_brief_confirmed": "find-leads",
530
485
  "revise_brief": "brief-interview",
531
486
  "auto_continue": "find-leads"
532
487
  },
533
488
  "interruptOnly": true,
534
- "requiredCampaignState": [
535
- "campaignId",
536
- "campaignBrief",
537
- "currentStep"
538
- ]
489
+ "requiredCampaignState": ["campaignId", "campaignBrief", "currentStep"]
539
490
  },
540
491
  {
541
492
  "id": "find-leads",
@@ -581,11 +532,7 @@
581
532
  {
582
533
  "action": "advance_watch_to_source_selection",
583
534
  "tool": "update_campaign",
584
- "requiredFields": [
585
- "campaignId",
586
- "currentStep",
587
- "watchNarration"
588
- ],
535
+ "requiredFields": ["campaignId", "currentStep", "watchNarration"],
589
536
  "requiredValues": {
590
537
  "currentStep": "pick-provider",
591
538
  "watchNarration.stage": "find-leads"
@@ -599,11 +546,7 @@
599
546
  {
600
547
  "action": "advance_watch_to_initial_source_lane",
601
548
  "tool": "update_campaign",
602
- "requiredFields": [
603
- "campaignId",
604
- "currentStep",
605
- "watchNarration"
606
- ],
549
+ "requiredFields": ["campaignId", "currentStep", "watchNarration"],
607
550
  "currentStepByPrimaryLane": {
608
551
  "signals": "signal-discovery",
609
552
  "salesNav": "sales-nav",
@@ -618,10 +561,7 @@
618
561
  },
619
562
  {
620
563
  "action": "run_first_campaign_attached_source_search",
621
- "requiredFields": [
622
- "campaignOfferId",
623
- "currentStep"
624
- ],
564
+ "requiredFields": ["campaignOfferId", "currentStep"],
625
565
  "before": "evaluating_source_viability_gate",
626
566
  "rule": "The parent thread must run the campaign-attached provider prompt + provider search for the current visible lane with campaignOfferId and currentStep (signal-discovery, sales-nav, prospeo, or the user-directed lane) so the campaign UI creates the corresponding live search/tab. Do not wait on background scouts before the user sees the active lane. Run only enough evidence to pass/fail this lane, then either recommend it or update the visible lane before trying the next fallback."
627
567
  },
@@ -667,10 +607,7 @@
667
607
  },
668
608
  {
669
609
  "action": "optional_debug_artifacts",
670
- "artifacts": [
671
- "lead-review.md",
672
- "lead-sample.json"
673
- ],
610
+ "artifacts": ["lead-review.md", "lead-sample.json"],
674
611
  "debugOrUatOnly": true,
675
612
  "shouldNotRunInNormalCustomerPath": true,
676
613
  "chatRenderRule": "Do not create or surface local lead-review artifacts in normal customer runs. The main thread renders the source decision in chat and persists provider/source state to the campaign."
@@ -707,19 +644,13 @@
707
644
  "bulk_enrich_with_prospeo"
708
645
  ],
709
646
  "watchRequired": false,
710
- "waitFor": [
711
- "lead_review_ready",
712
- "revise_brief",
713
- "confirm_with_user"
714
- ],
647
+ "waitFor": ["lead_review_ready", "revise_brief", "confirm_with_user"],
715
648
  "transitions": {
716
649
  "lead_review_ready": "lead-review",
717
650
  "revise_brief": "brief-interview",
718
651
  "confirm_with_user": "lead-review"
719
652
  },
720
- "optionalRequiredArtifacts": [
721
- "lead-source-intake.json"
722
- ],
653
+ "optionalRequiredArtifacts": ["lead-source-intake.json"],
723
654
  "toolRules": {
724
655
  "suppliedLinkedinProfiles": "Preview only before the bounded import step. campaignOfferId may attach the preview/list choice to campaign state, but do not import into the campaign table until auto-execute-leads confirms the source list.",
725
656
  "suppliedDomains": "May confirm load_csv_domains to produce a domainFilterId. Campaign-associated Prospeo preview searches must use campaignOfferId so the user can watch source state in the campaign, but no import/enrichment happens until rubrics and message set are ready.",
@@ -742,15 +673,8 @@
742
673
  "selected source state"
743
674
  ]
744
675
  },
745
- "requiredCampaignState": [
746
- "campaignId",
747
- "campaignBrief",
748
- "currentStep"
749
- ],
750
- "debugProducesArtifacts": [
751
- "lead-review.md",
752
- "lead-sample.json"
753
- ]
676
+ "requiredCampaignState": ["campaignId", "campaignBrief", "currentStep"],
677
+ "debugProducesArtifacts": ["lead-review.md", "lead-sample.json"]
754
678
  },
755
679
  {
756
680
  "id": "lead-review",
@@ -845,9 +769,7 @@
845
769
  "use or discard decision"
846
770
  ],
847
771
  "forbidPercentOnlyFitRates": true,
848
- "requiredArtifactLinks": [
849
- "lead-review.md"
850
- ],
772
+ "requiredArtifactLinks": ["lead-review.md"],
851
773
  "artifactLinkTiming": "before_next_step_or_revision_question",
852
774
  "doNotCompressToSummaryOnly": false,
853
775
  "doNotRenderArtifactLinksOnly": true,
@@ -877,10 +799,7 @@
877
799
  "approvalMathRule": "Immediately above the approval question, render the compact Math block: given this many eligible relevant posts, this many reachable engagers, this sampled ICP fit as n/N plus percentage/range, this many good-fit prospects per 100 engagers, this many required engagers to scrape, this average reachable engagers per post, this expected good-fit prospects per post after cleanup, this source target good-fit lead count defaulting to 300, and this many posts needed, we expect this usable lead range from the selected posts; also state that approval only imports a 15-row campaign review batch first after the source list is materialized. If the user wants more volume, name the Sales Nav or Prospeo alternative, its preview/sample math, any Sales Nav loosening used to avoid an over-tight pool, and the colder/weaker-context tradeoff. Do not ask the approval question until this math is visible.",
878
800
  "autoContinueWhen": {
879
801
  "status": "confirmed",
880
- "confidenceIn": [
881
- "high",
882
- "medium"
883
- ],
802
+ "confidenceIn": ["high", "medium"],
884
803
  "projectedGoodFitsAtLeast": 150,
885
804
  "samplePassRateAtLeast": 0.7,
886
805
  "noProviderMismatch": true,
@@ -890,28 +809,21 @@
890
809
  {
891
810
  "action": "sync_selected_source_to_campaign_shell",
892
811
  "tool": "update_campaign",
893
- "requires": [
894
- "campaignId",
895
- "lead-review.md"
896
- ],
812
+ "requires": ["campaignId", "lead-review.md"],
897
813
  "when": "after_source_confirmed_or_auto_continue",
898
814
  "fields": [
899
815
  "leadSourceType",
900
816
  "leadSourceProvider",
901
817
  "providerSearchAssociation",
902
- "currentStep: primary provider step (signal-discovery, sales-nav, prospeo/contact-search, saved-lists, or leads)",
903
- "watchNarration: find-leads source recommendation ready, or review-batch import starting when source approval already happened",
818
+ "watchNarration only when paired to an already-visible source recommendation; do not create a separate source-approved navigation beat before import",
904
819
  "selectedLeadListId as source list id only for existing-list or supplied-list preview"
905
820
  ],
906
- "sourceApprovalWatchRule": "Immediately after the user approves a source, and before lookup_sales_nav_filter, get_provider_prompt, search_sales_nav, search_prospeo, import_leads, or confirm_lead_list, persist the approved source as the active provider on the CampaignOffer. For Sales Nav approval, call update_campaign with leadSourceType `new`, leadSourceProvider `sales-nav`, currentStep `sales-nav`, and current-tense watchNarration so the browser switches off the previous Signal Discovery lane and the user can continue watching the approved provider setup. Apply the same rule for signal-discovery and prospeo approvals.",
821
+ "sourceApprovalWatchRule": "Immediately after the user approves a source, persist the approved source as the active provider on the CampaignOffer without a separate source-approved currentStep fix. If the recommendation already switched the watched source lane before approval, leave it there; the next visible navigation after approval is owned by import_leads after it has a leadListId/jobId. Apply the same rule for signal-discovery, sales-nav, and prospeo approvals.",
907
822
  "fallback": "Stop if campaignId is missing; the source must be attached to the existing CampaignOffer before import.",
908
823
  "readsCampaignStateFirst": true
909
824
  }
910
825
  ],
911
- "requiredArtifacts": [
912
- "lead-review.md",
913
- "lead-sample.json"
914
- ],
826
+ "requiredArtifacts": ["lead-review.md", "lead-sample.json"],
915
827
  "producesArtifacts": [],
916
828
  "allowedTools": [
917
829
  "AskUserQuestion",
@@ -984,16 +896,14 @@
984
896
  },
985
897
  {
986
898
  "action": "watch_mode_orient",
987
- "watchNarrationRule": "Before import_leads starts, align the guide with chat by setting review-batch watchNarration to current-tense source materialization copy while keeping currentStep on the approved source lane. Also persist the approved source provider/currentStep first: Sales Nav approval must set leadSourceProvider `sales-nav` and currentStep `sales-nav`, Signals approval must set `signal-discovery`, and Prospeo approval must set `prospeo`. Do not set currentStep to `confirm-lead-list` before import_leads returns a leadListId/jobId; import_leads owns that move after the provider import API call so the import page has progress state when it appears. Use a headline like `Preparing the source list`; explain that the browser is on the approved source lane while Codex materializes only the approved source-capacity plan, then imports only the bounded 15-row review batch into the campaign table; include a no-launch safety note."
899
+ "watchNarrationRule": "Before import_leads starts, keep the guide aligned with the already-approved source without creating a separate source-approved step fix. Persist the approved source provider if needed, but do not set currentStep to `confirm-lead-list` before import_leads returns a leadListId/jobId; import_leads owns that move after the provider import API call so the import page has progress state when it appears. Use import progress copy only once the owning tool has a table/job to show; include a no-launch safety note."
988
900
  },
989
901
  {
990
902
  "tool": "import_leads",
991
903
  "currentStepTiming": "Omit currentStep or pass `confirm-lead-list`; either way the tool must start the provider import API first, then update CampaignOffer.selectedLeadListId/currentStep after it has a leadListId/jobId. Never navigate the watched UI to confirm-lead-list with no import job/table to show.",
904
+ "watchNarrationAfterJob": "When import_leads moves currentStep to confirm-lead-list, it must also persist current-tense source import copy such as `Scraping source leads from posts` or `Importing source leads`. Do not leave stale source-approval narration like `Review the source in Codex` while the import progress page is visible.",
992
905
  "sameTurnPreflight": "Immediately before import_leads, call get_provider_prompt({ provider, campaignOfferId }) in the same resumed turn, even if source discovery loaded the provider prompt earlier.",
993
- "requiredFields": [
994
- "campaignOfferId",
995
- "targetLeadCount"
996
- ],
906
+ "requiredFields": ["campaignOfferId", "targetLeadCount"],
997
907
  "targetLeadCountFromConfig": "sourceTargetGoodFitLeadCount (default 300) for provider source-list materialization, not import.importLimit",
998
908
  "signalDiscoveryRequiredFields": [
999
909
  "targetEngagerCount from approved source math",
@@ -1017,10 +927,7 @@
1017
927
  },
1018
928
  {
1019
929
  "tool": "wait_for_lead_list_ready",
1020
- "sourceBranches": [
1021
- "normal-discovery",
1022
- "supplied-domains"
1023
- ],
930
+ "sourceBranches": ["normal-discovery", "supplied-domains"],
1024
931
  "skipWhen": "resolved source branch is supplied-linkedin-profiles or existing-lead-list; direct lead-list branches have no provider import job to wait on and this probe can poison the campaign import status"
1025
932
  },
1026
933
  {
@@ -1032,13 +939,9 @@
1032
939
  "supplied-linkedin-profiles",
1033
940
  "existing-lead-list"
1034
941
  ],
1035
- "requiredBeforeCall": [
1036
- "campaign_attached_source_state"
1037
- ],
1038
- "persists": [
1039
- "selectedLeadListId",
1040
- "workflowTableId"
1041
- ],
942
+ "requiredBeforeCall": ["campaign_attached_source_state"],
943
+ "persists": ["selectedLeadListId", "workflowTableId"],
944
+ "watchNarrationAfterImport": "When confirm_lead_list imports a non-empty review batch and moves currentStep to filter-choice, it must persist fit-message watch copy in the same beat: `I recommend adding filters`, explain that the visible 15-row sample is ready, and ask the user to choose filters or skip. Do not leave stale source-review narration while the filter-choice screen is visible.",
1042
945
  "identityBoundary": "selectedLeadListId remains the source list; workflowTableId is the campaign table returned by confirm/import."
1043
946
  },
1044
947
  {
@@ -1098,23 +1001,36 @@
1098
1001
  "ownership": "proof inventory, token strategy, angle drafting, skeptical-prospect review, and selected winner only"
1099
1002
  },
1100
1003
  "requiredBeforeFilterChoiceWatchCopy": true,
1101
- "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.",
1102
- "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."
1004
+ "runtimeProofTransport": "watchNarration.workerDetails.messageDraftBuilder",
1005
+ "runtimeProofRequiredBeforeRunning": [
1006
+ "runId or fallbackId",
1007
+ "statusSource",
1008
+ "status",
1009
+ "startedAt",
1010
+ "updatedAt",
1011
+ "basisToken",
1012
+ "basis.selectedLeadListId",
1013
+ "basis.workflowTableId",
1014
+ "basis.reviewBatchRowHash or basis.reviewBatchRowIds"
1015
+ ],
1016
+ "basisFields": [
1017
+ "campaign revision or updatedAt",
1018
+ "brief hash",
1019
+ "selectedLeadListId",
1020
+ "workflowTableId",
1021
+ "bounded review-batch row ids/hash",
1022
+ "filter choice at branch start",
1023
+ "filter/rubric basis when present"
1024
+ ],
1025
+ "chatAnnouncementRule": "After a live branch or explicit parent-thread fallback starts, tell the user the background Message Draft Builder is running from the full generate-messages prompt and the real review-batch contacts while they decide filters. Do not say it is running before runtime proof exists.",
1026
+ "racePrecedence": "ready output from the current basis wins; branch-superseded and fallback-superseded are ignored for display; spawn-failed, blocked, retry-needed, or stale show blocked/retry copy; fallback-active shows running only until a current branch-ready output supersedes it.",
1027
+ "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 after confirm_lead_list succeeds and before any guide copy saying Message Draft Builder is In Progress. confirm_lead_list remains the first allowed start point; do not start from source recommendation, provider import job alone, or a zero-row review batch.",
1028
+ "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 with a fallbackId 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."
1103
1029
  },
1104
1030
  {
1105
- "tool": "update_campaign",
1106
- "requiredFields": [
1107
- "campaignId",
1108
- "currentStep",
1109
- "watchNarration"
1110
- ],
1111
- "requiredValues": {
1112
- "currentStep": "filter-choice",
1113
- "watchNarration.stage": "fit-message",
1114
- "watchNarration.headline": "Added to Campaign",
1115
- "watchNarration.workerStatuses.messageDraftBuilder": "running_after_launch_message_draft_builder_after_review_batch_import_only"
1116
- },
1117
- "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."
1031
+ "action": "confirm_tool_owned_filter_choice_beat",
1032
+ "requiredAfter": ["confirm_lead_list", "non_empty_review_batch"],
1033
+ "rule": "confirm_lead_list owns the move to filter-choice after the bounded review batch is present and non-empty. Its watch copy must say `I recommend adding filters`, include sample assessment, ask the user to Choose filters or skip, and avoid claiming enrichment, filtering, Generate Message cells, sequence, or sending has started. Do not call update_campaign as a post-confirm step fix."
1118
1034
  }
1119
1035
  ],
1120
1036
  "allowedTools": [
@@ -1209,11 +1125,7 @@
1209
1125
  "bulk_enrich_with_prospeo"
1210
1126
  ],
1211
1127
  "watchRequired": true,
1212
- "waitFor": [
1213
- "filters_enabled",
1214
- "filters_skipped",
1215
- "revise_leads"
1216
- ],
1128
+ "waitFor": ["filters_enabled", "filters_skipped", "revise_leads"],
1217
1129
  "transitions": {
1218
1130
  "filters_enabled": "post-lead-workstreams",
1219
1131
  "filters_skipped": "message-generation",
@@ -1251,12 +1163,8 @@
1251
1163
  "selectedLeadListId",
1252
1164
  "workflowTableId review-batch rows"
1253
1165
  ],
1254
- "producesArtifacts": [
1255
- "lead-filter.md"
1256
- ],
1257
- "optionalProducesArtifacts": [
1258
- "rubric.json"
1259
- ],
1166
+ "producesArtifacts": ["lead-filter.md"],
1167
+ "optionalProducesArtifacts": ["rubric.json"],
1260
1168
  "ownership": "lead quality, false-positive patterns, keep/exclude rules, and production rubric translation only"
1261
1169
  },
1262
1170
  {
@@ -1284,7 +1192,7 @@
1284
1192
  }
1285
1193
  ],
1286
1194
  "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.",
1287
- "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.",
1195
+ "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 with fallbackId before the next user-visible step. Message Draft Builder runtime proof must be stored under watchNarration.workerDetails.messageDraftBuilder with statusSource, status, startedAt, updatedAt, basisToken, and current selectedLeadListId/workflowTableId/review-batch basis. 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.",
1288
1196
  "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.",
1289
1197
  "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.",
1290
1198
  "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.",
@@ -1293,9 +1201,7 @@
1293
1201
  },
1294
1202
  {
1295
1203
  "action": "wait_for_lead_filter_rules",
1296
- "requiredArtifacts": [
1297
- "lead-filter.md"
1298
- ],
1204
+ "requiredArtifacts": ["lead-filter.md"],
1299
1205
  "optionalArtifacts": [
1300
1206
  "rubric.json",
1301
1207
  "message-validation.md",
@@ -1307,30 +1213,19 @@
1307
1213
  {
1308
1214
  "action": "save_filter_rubrics_to_campaign",
1309
1215
  "tool": "save_rubrics",
1310
- "requires": [
1311
- "campaignId",
1312
- "workflowTableId",
1313
- "lead-filter.md"
1314
- ],
1216
+ "requires": ["campaignId", "workflowTableId", "lead-filter.md"],
1315
1217
  "optionalInput": "rubric.json",
1316
1218
  "when": "after_imported_campaign_table_sample_and_lead_filter_ready_before_message_review",
1317
1219
  "fallback": "Stop if campaignId/workflowTableId is missing or save_rubrics fails; do not queue the review-batch cascade without saved rubrics.",
1318
1220
  "requiredBeforeImport": false,
1319
- "requiredFields": [
1320
- "campaignOfferId",
1321
- "leadScoringRubrics"
1322
- ],
1221
+ "requiredFields": ["campaignOfferId", "leadScoringRubrics"],
1323
1222
  "writesCampaignState": "leadScoringRubrics",
1324
1223
  "requiredBeforeCascade": true
1325
1224
  },
1326
1225
  {
1327
1226
  "action": "advance_watch_to_filter_leads_after_rubrics_saved",
1328
1227
  "tool": "update_campaign",
1329
- "requires": [
1330
- "campaignId",
1331
- "workflowTableId",
1332
- "leadScoringRubrics"
1333
- ],
1228
+ "requires": ["campaignId", "workflowTableId", "leadScoringRubrics"],
1334
1229
  "requiredValues": {
1335
1230
  "currentStep": "apply-icp-rubric",
1336
1231
  "enableICPFilters": true,
@@ -1361,10 +1256,7 @@
1361
1256
  }
1362
1257
  ],
1363
1258
  "requiredArtifacts": [],
1364
- "producesArtifacts": [
1365
- "lead-filter.md",
1366
- "message-validation.md"
1367
- ],
1259
+ "producesArtifacts": ["lead-filter.md", "message-validation.md"],
1368
1260
  "optionalProducesArtifacts": [
1369
1261
  "rubric.json",
1370
1262
  "message-prep.md",
@@ -1457,9 +1349,7 @@
1457
1349
  "bulk_enrich_with_prospeo"
1458
1350
  ],
1459
1351
  "watchRequired": true,
1460
- "waitFor": [
1461
- "legacy_resume_only"
1462
- ],
1352
+ "waitFor": ["legacy_resume_only"],
1463
1353
  "transitions": {
1464
1354
  "legacy_resume_only": "message-generation"
1465
1355
  },
@@ -1481,20 +1371,12 @@
1481
1371
  {
1482
1372
  "action": "run_stage",
1483
1373
  "target": "filter-leads",
1484
- "inputs": [
1485
- "brief.md",
1486
- "lead-review.md",
1487
- "lead-sample.json"
1488
- ]
1374
+ "inputs": ["brief.md", "lead-review.md", "lead-sample.json"]
1489
1375
  },
1490
1376
  {
1491
1377
  "action": "write_artifacts",
1492
- "artifacts": [
1493
- "lead-filter.md"
1494
- ],
1495
- "optionalArtifacts": [
1496
- "rubric.json"
1497
- ]
1378
+ "artifacts": ["lead-filter.md"],
1379
+ "optionalArtifacts": ["rubric.json"]
1498
1380
  },
1499
1381
  {
1500
1382
  "action": "show_lead_filter_summary",
@@ -1506,27 +1388,18 @@
1506
1388
  "Why",
1507
1389
  "Expected impact"
1508
1390
  ],
1509
- "requiredArtifactLinks": [
1510
- "lead-filter.md"
1511
- ],
1391
+ "requiredArtifactLinks": ["lead-filter.md"],
1512
1392
  "chatRenderRule": "Show a slim rendered-Markdown filter summary before message review, never a fenced code block. Use ## Lead filter plus indexed sections and short bullets for who we keep, who we exclude, sample-backed reason, and expected impact. Keep production rubric fields and rubric.json details in lead-filter.md/rubric.json. Do not show plain filesystem paths unless links cannot be created."
1513
1393
  },
1514
1394
  {
1515
1395
  "action": "save_filter_rubrics_to_campaign",
1516
1396
  "tool": "save_rubrics",
1517
- "requires": [
1518
- "campaignId",
1519
- "workflowTableId",
1520
- "lead-filter.md"
1521
- ],
1397
+ "requires": ["campaignId", "workflowTableId", "lead-filter.md"],
1522
1398
  "optionalInput": "rubric.json",
1523
1399
  "when": "after_filter_confirmed_or_auto_continue",
1524
1400
  "fallback": "Stop if campaignId/workflowTableId is missing or save_rubrics fails; do not queue the review-batch cascade without saved rubrics.",
1525
1401
  "requiredBeforeImport": false,
1526
- "requiredFields": [
1527
- "campaignOfferId",
1528
- "leadScoringRubrics"
1529
- ],
1402
+ "requiredFields": ["campaignOfferId", "leadScoringRubrics"],
1530
1403
  "writesCampaignState": "leadScoringRubrics",
1531
1404
  "requiredBeforeCascade": true
1532
1405
  },
@@ -1534,27 +1407,16 @@
1534
1407
  "action": "ask_revise_only_if_filter_is_not_confirmed",
1535
1408
  "autoContinueWhen": {
1536
1409
  "status": "confirmed",
1537
- "decisionIn": [
1538
- "continue",
1539
- "confirmed"
1540
- ],
1410
+ "decisionIn": ["continue", "confirmed"],
1541
1411
  "expectedUsableLeadsAtLeast": 150,
1542
1412
  "noHeavyFilteringWarning": true,
1543
1413
  "noCampaignMissRisk": true
1544
1414
  }
1545
1415
  }
1546
1416
  ],
1547
- "requiredArtifacts": [
1548
- "brief.md",
1549
- "lead-review.md",
1550
- "lead-sample.json"
1551
- ],
1552
- "producesArtifacts": [
1553
- "lead-filter.md"
1554
- ],
1555
- "optionalProducesArtifacts": [
1556
- "rubric.json"
1557
- ],
1417
+ "requiredArtifacts": ["brief.md", "lead-review.md", "lead-sample.json"],
1418
+ "producesArtifacts": ["lead-filter.md"],
1419
+ "optionalProducesArtifacts": ["rubric.json"],
1558
1420
  "allowedTools": [
1559
1421
  "get_subskill_prompt",
1560
1422
  "get_subskill_asset",
@@ -1587,10 +1449,7 @@
1587
1449
  "auto_continue": "message-generation"
1588
1450
  },
1589
1451
  "interruptOnly": true,
1590
- "requiredCampaignState": [
1591
- "campaignId",
1592
- "workflowTableId"
1593
- ]
1452
+ "requiredCampaignState": ["campaignId", "workflowTableId"]
1594
1453
  },
1595
1454
  {
1596
1455
  "id": "message-generation",
@@ -1614,9 +1473,7 @@
1614
1473
  }
1615
1474
  ],
1616
1475
  "requiredArtifacts": [],
1617
- "producesArtifacts": [
1618
- "message-validation.md"
1619
- ],
1476
+ "producesArtifacts": ["message-validation.md"],
1620
1477
  "allowedTools": [
1621
1478
  "get_subskill_prompt",
1622
1479
  "get_subskill_asset",
@@ -1628,6 +1485,8 @@
1628
1485
  "Do not use brief.md, lead-review.md, or lead-sample.json as required live state; those files are optional debug context only.",
1629
1486
  "Do not hand-write message-validation.md from message-prep.md, message-candidate-drafts.md, stale markdown files, database reads, or general campaign knowledge.",
1630
1487
  "message-validation.md or the returned recommendation must prove the generate-messages workflow ran and include Token Fill Rules, good-fill and omit/fallback examples, a raw sendable selected winner, and quality gate notes before message-review can recommend approve-message.",
1488
+ "The compact messageDraftRecommendation must return only templateRecommendation, tokenFillRules, renderedSample, concerns, status, basisToken, outputAt, outputHash, and error/retry detail. Its basis must include campaign revision or updatedAt, brief hash, selectedLeadListId, workflowTableId, bounded review-batch row ids/hash, filter choice, and filter/rubric basis when present.",
1489
+ "If current campaign state, selectedLeadListId, workflowTableId, or review-batch row ids/hash do not match the branch basis, classify the output stale or blocked; do not write message cells, enrich rows, attach sequence, or imply send readiness.",
1631
1490
  "If the generate-messages output is plausible but weaker than the gold-standard rules, stop at message-review with revise-messaging. Do not continue to approval or mint just because the mechanical flow worked.",
1632
1491
  "The approved message set must be written back into campaignBrief before auto-execute-messaging observes or queues product Generate Message cells. Normal create-campaign runs use the approved-template path; legacy AI-generated mode is not part of the normal flow."
1633
1492
  ],
@@ -1708,9 +1567,7 @@
1708
1567
  ],
1709
1568
  "mustRenderInlineBeforeQuestion": true,
1710
1569
  "minimumVisibleMessageDetail": "filled_sample_message_only",
1711
- "requiredArtifactLinks": [
1712
- "message-review.md"
1713
- ],
1570
+ "requiredArtifactLinks": ["message-review.md"],
1714
1571
  "artifactLinkTiming": "before_approval_question",
1715
1572
  "requiredFileLabels": [
1716
1573
  "Subject:",
@@ -1739,10 +1596,7 @@
1739
1596
  "Recommendation:"
1740
1597
  ],
1741
1598
  "chatRenderRule": "Show a slim rendered-Markdown message review, never a fenced code block. Use ## Message review plus indexed sections and short bullets. Show one fully filled sample message with no {{tokens}} before asking for approval. Include subject, sample message, why it should work, one concern or None, recommendation, and Watch link when a campaign shell exists. Keep tokenized template, token fill basis, rendered examples, good/bad token fill examples, validation notes, and message-validation.md details in campaign state or optional debug artifacts. Do not show plain filesystem paths unless the user asks.",
1742
- "allowedRecommendations": [
1743
- "approve-message",
1744
- "revise-messaging"
1745
- ],
1599
+ "allowedRecommendations": ["approve-message", "revise-messaging"],
1746
1600
  "judgeAgainst": [
1747
1601
  "generate-messages rules",
1748
1602
  "gold-standard-* references",
@@ -1770,10 +1624,7 @@
1770
1624
  },
1771
1625
  {
1772
1626
  "action": "ask_message_review_choice",
1773
- "choices": [
1774
- "approve-message",
1775
- "revise-messaging"
1776
- ],
1627
+ "choices": ["approve-message", "revise-messaging"],
1777
1628
  "questionPrerequisiteVisibleLabels": [
1778
1629
  "Status: message-review",
1779
1630
  "Subject:",
@@ -1791,10 +1642,7 @@
1791
1642
  {
1792
1643
  "action": "write_message_review_decision",
1793
1644
  "artifact": "message-review-decision.md",
1794
- "allowedChoices": [
1795
- "approve-message",
1796
- "revise-messaging"
1797
- ],
1645
+ "allowedChoices": ["approve-message", "revise-messaging"],
1798
1646
  "authorizesReviewBatchImportWhenApproved": true
1799
1647
  },
1800
1648
  {
@@ -1849,11 +1697,7 @@
1849
1697
  "requiredValues": {
1850
1698
  "tableId": "workflowTableId",
1851
1699
  "cellSource": "bounded_review_batch_enrichCellIds",
1852
- "statusFilter": [
1853
- "pending",
1854
- "error",
1855
- "dependency_blocked"
1856
- ]
1700
+ "statusFilter": ["pending", "error", "dependency_blocked"]
1857
1701
  },
1858
1702
  "purpose": "queue_bounded_review_batch_enrichment_after_approved_template",
1859
1703
  "rule": "Queue only enrichCellId values from get_rows_minimal for the bounded review batch after update_campaign_brief has saved the Approved Message Template. Do not queue Generate Message cells directly here; the workflow cascade runs enrichment, ICP scoring, then Generate Message for passing rows."
@@ -1894,13 +1738,8 @@
1894
1738
  "lead-sample.json",
1895
1739
  "message-validation.md"
1896
1740
  ],
1897
- "optionalRequiredArtifacts": [
1898
- "lead-filter.md"
1899
- ],
1900
- "producesArtifacts": [
1901
- "message-review.md",
1902
- "message-review-decision.md"
1903
- ],
1741
+ "optionalRequiredArtifacts": ["lead-filter.md"],
1742
+ "producesArtifacts": ["message-review.md", "message-review-decision.md"],
1904
1743
  "allowedTools": [
1905
1744
  "AskUserQuestion",
1906
1745
  "request_user_input",
@@ -1921,19 +1760,13 @@
1921
1760
  "bulk_enrich_with_prospeo"
1922
1761
  ],
1923
1762
  "watchRequired": false,
1924
- "waitFor": [
1925
- "message_approved",
1926
- "revise_messaging"
1927
- ],
1763
+ "waitFor": ["message_approved", "revise_messaging"],
1928
1764
  "transitions": {
1929
1765
  "message_approved": "auto-execute-messaging",
1930
1766
  "revise_messaging": "message-generation"
1931
1767
  },
1932
1768
  "interruptOnly": true,
1933
- "requiredCampaignState": [
1934
- "campaignId",
1935
- "workflowTableId"
1936
- ]
1769
+ "requiredCampaignState": ["campaignId", "workflowTableId"]
1937
1770
  },
1938
1771
  {
1939
1772
  "id": "approval-packet",
@@ -1952,9 +1785,7 @@
1952
1785
  "message-review.md",
1953
1786
  "message-review-decision.md"
1954
1787
  ],
1955
- "optionalFiles": [
1956
- "rubric.json"
1957
- ]
1788
+ "optionalFiles": ["rubric.json"]
1958
1789
  },
1959
1790
  {
1960
1791
  "action": "require_message_review_decision",
@@ -1975,17 +1806,11 @@
1975
1806
  "what approval creates"
1976
1807
  ],
1977
1808
  "minimumVisibleDetail": "slim_final_approval_summary",
1978
- "requiredArtifactLinks": [
1979
- "approval-packet.md"
1980
- ],
1809
+ "requiredArtifactLinks": ["approval-packet.md"],
1981
1810
  "artifactLinkTiming": "before_commit_gate_question",
1982
1811
  "chatRenderRule": "Show a slim rendered-Markdown final approval packet only, never a fenced code block. Use ## Ready to create campaign plus indexed sections and short bullets. Include target with concrete role/title names, lead source with one key metric, filter summary, one fully filled sample message with no {{tokens}}, what approval creates, and safety note that nothing sends live until the review/greenlight step. Keep tokenized templates, evidence tables, rubrics, and validation details in approval-packet.md. Do not show sequence/settings in final approval; that comes after campaign creation. Do not show plain filesystem paths unless links cannot be created.",
1983
1812
  "userFacing": true,
1984
- "doNotUseWords": [
1985
- "anchor",
1986
- "validation anchor",
1987
- "lane"
1988
- ]
1813
+ "doNotUseWords": ["anchor", "validation anchor", "lane"]
1989
1814
  },
1990
1815
  {
1991
1816
  "action": "optional_customer_roleplay",
@@ -2003,12 +1828,8 @@
2003
1828
  "message-review.md",
2004
1829
  "message-review-decision.md"
2005
1830
  ],
2006
- "producesArtifacts": [
2007
- "approval-packet.md"
2008
- ],
2009
- "optionalProducesArtifacts": [
2010
- "customer-roleplay.md"
2011
- ],
1831
+ "producesArtifacts": ["approval-packet.md"],
1832
+ "optionalProducesArtifacts": ["customer-roleplay.md"],
2012
1833
  "allowedTools": [
2013
1834
  "AskUserQuestion",
2014
1835
  "request_user_input",
@@ -2028,9 +1849,7 @@
2028
1849
  "watchRequired": false,
2029
1850
  "waitFor": "approval_packet_ready",
2030
1851
  "transitions": {},
2031
- "optionalRequiredArtifacts": [
2032
- "lead-source-intake.json"
2033
- ],
1852
+ "optionalRequiredArtifacts": ["lead-source-intake.json"],
2034
1853
  "deprecated": true,
2035
1854
  "deprecatedReason": "Shell-first flow has no final approval gate or mint. Message approval syncs the final template into the existing campaign and transitions directly to Step 13."
2036
1855
  },
@@ -2047,9 +1866,7 @@
2047
1866
  "lead-filter.md",
2048
1867
  "message-validation.md"
2049
1868
  ],
2050
- "optionalFiles": [
2051
- "rubric.json"
2052
- ]
1869
+ "optionalFiles": ["rubric.json"]
2053
1870
  },
2054
1871
  {
2055
1872
  "action": "validate_anchors",
@@ -2062,16 +1879,11 @@
2062
1879
  "doesNotAuthorizeMint": true
2063
1880
  }
2064
1881
  ],
2065
- "allowedTools": [
2066
- "AskUserQuestion",
2067
- "request_user_input"
2068
- ],
1882
+ "allowedTools": ["AskUserQuestion", "request_user_input"],
2069
1883
  "watchRequired": false,
2070
1884
  "waitFor": "artifacts_validated",
2071
1885
  "transitions": {},
2072
- "optionalRequiredArtifacts": [
2073
- "lead-source-intake.json"
2074
- ],
1886
+ "optionalRequiredArtifacts": ["lead-source-intake.json"],
2075
1887
  "deprecated": true,
2076
1888
  "deprecatedReason": "Shell-first flow has no final approval gate or mint. Message approval syncs the final template into the existing campaign and transitions directly to Step 13.",
2077
1889
  "resumeOnly": true,
@@ -2098,9 +1910,7 @@
2098
1910
  "sample message",
2099
1911
  "what approval creates"
2100
1912
  ],
2101
- "requiredArtifactLinks": [
2102
- "approval-packet.md"
2103
- ],
1913
+ "requiredArtifactLinks": ["approval-packet.md"],
2104
1914
  "chatRenderRule": "Same slim rendered-Markdown approval packet as approval-packet, never a fenced code block. Use indexed sections and short bullets. Include target with concrete role/title names and one fully filled sample message without tokens. Do not include sequence/settings. Do not repeat tokenized template or long evidence tables in chat.",
2105
1915
  "artifactLinkTiming": "same_turn_before_commit_question"
2106
1916
  },
@@ -2129,10 +1939,7 @@
2129
1939
  "onlyApproveAuthorizesMint": true
2130
1940
  }
2131
1941
  ],
2132
- "allowedTools": [
2133
- "AskUserQuestion",
2134
- "request_user_input"
2135
- ],
1942
+ "allowedTools": ["AskUserQuestion", "request_user_input"],
2136
1943
  "watchRequired": false,
2137
1944
  "waitFor": [
2138
1945
  "user_approved",
@@ -2143,9 +1950,7 @@
2143
1950
  "user_abort"
2144
1951
  ],
2145
1952
  "transitions": {},
2146
- "optionalRequiredArtifacts": [
2147
- "lead-source-intake.json"
2148
- ],
1953
+ "optionalRequiredArtifacts": ["lead-source-intake.json"],
2149
1954
  "deprecated": true,
2150
1955
  "deprecatedReason": "Shell-first flow has no final approval gate or mint. Message approval syncs the final template into the existing campaign and transitions directly to Step 13.",
2151
1956
  "resumeOnly": true,
@@ -2166,10 +1971,7 @@
2166
1971
  "tool": "create_campaign",
2167
1972
  "mode": "create_new_or_resume_draft_shell",
2168
1973
  "shellArtifact": "campaign-shell.json",
2169
- "requiredFields": [
2170
- "campaignBrief",
2171
- "currentStep"
2172
- ],
1974
+ "requiredFields": ["campaignBrief", "currentStep"],
2173
1975
  "requiredValues": {
2174
1976
  "currentStep": "auto-execute-leads"
2175
1977
  },
@@ -2191,35 +1993,22 @@
2191
1993
  "Fallback if missing:",
2192
1994
  "Token fill basis:"
2193
1995
  ],
2194
- "capture": [
2195
- "campaignId",
2196
- "watchUrl"
2197
- ],
1996
+ "capture": ["campaignId", "watchUrl"],
2198
1997
  "onMissingWatchUrl": "recoverable_failure_abort_before_rubric_save"
2199
1998
  },
2200
1999
  {
2201
2000
  "tool": "update_campaign",
2202
2001
  "when": "campaign-shell.json exists",
2203
- "requiredFields": [
2204
- "campaignId",
2205
- "campaignBrief",
2206
- "currentStep"
2207
- ],
2002
+ "requiredFields": ["campaignId", "campaignBrief", "currentStep"],
2208
2003
  "requiredValues": {
2209
2004
  "currentStep": "auto-execute-leads"
2210
2005
  },
2211
- "fields": [
2212
- "campaignBrief",
2213
- "leadSourceType",
2214
- "leadSourceProvider"
2215
- ],
2006
+ "fields": ["campaignBrief", "leadSourceType", "leadSourceProvider"],
2216
2007
  "skipWhen": "create_campaign minted a new campaign with the final approved brief and currentStep already set"
2217
2008
  },
2218
2009
  {
2219
2010
  "tool": "save_rubrics",
2220
- "requiredFields": [
2221
- "rubric"
2222
- ],
2011
+ "requiredFields": ["rubric"],
2223
2012
  "idempotentWhenAlreadySavedToDraftShell": true,
2224
2013
  "onFailure": "rollback_or_recoverable_stop"
2225
2014
  },
@@ -2231,10 +2020,7 @@
2231
2020
  },
2232
2021
  {
2233
2022
  "action": "surface_watch_link",
2234
- "requires": [
2235
- "create_campaign_succeeded",
2236
- "save_rubrics_succeeded"
2237
- ],
2023
+ "requires": ["create_campaign_succeeded", "save_rubrics_succeeded"],
2238
2024
  "reference": "references/watch-link-handoff.md",
2239
2025
  "requiredVisibleContent": [
2240
2026
  "Campaign created",
@@ -2256,9 +2042,7 @@
2256
2042
  "AskUserQuestion",
2257
2043
  "request_user_input"
2258
2044
  ],
2259
- "doNotAllow": [
2260
- "import_leads"
2261
- ],
2045
+ "doNotAllow": ["import_leads"],
2262
2046
  "watchRequired": true,
2263
2047
  "waitFor": "autonomous_tail_started",
2264
2048
  "transitions": {},
@@ -2347,10 +2131,7 @@
2347
2131
  },
2348
2132
  {
2349
2133
  "tool": "wait_for_rubric_results",
2350
- "requiredFields": [
2351
- "targetCount",
2352
- "minPassedCount"
2353
- ],
2134
+ "requiredFields": ["targetCount", "minPassedCount"],
2354
2135
  "targetCountSource": "stats.totalRows_or_imported_batch_count",
2355
2136
  "minPassedCountSource": "firstPassingRowForMessageStart (1)",
2356
2137
  "requiredValues": {
@@ -2430,10 +2211,7 @@
2430
2211
  "generate_message_cells_do_not_run_from_background_template_until_template_token_rules_approved"
2431
2212
  ],
2432
2213
  "watchRequired": true,
2433
- "waitFor": [
2434
- "sample_validated",
2435
- "sample_revision_required"
2436
- ],
2214
+ "waitFor": ["sample_validated", "sample_revision_required"],
2437
2215
  "transitions": {
2438
2216
  "sample_validated": "auto-execute-messaging",
2439
2217
  "sample_revision_required": "lead-review",
@@ -2524,11 +2302,7 @@
2524
2302
  },
2525
2303
  {
2526
2304
  "tool": "update_campaign",
2527
- "requiredFields": [
2528
- "campaignId",
2529
- "currentStep",
2530
- "watchNarration"
2531
- ],
2305
+ "requiredFields": ["campaignId", "currentStep", "watchNarration"],
2532
2306
  "requiredValues": {
2533
2307
  "currentStep": "awaiting-user-greenlight",
2534
2308
  "watchNarration.stage": "review-ready"
@@ -2546,11 +2320,7 @@
2546
2320
  "AskUserQuestion",
2547
2321
  "request_user_input"
2548
2322
  ],
2549
- "doNotAllow": [
2550
- "import_leads",
2551
- "start_campaign",
2552
- "generate_messages"
2553
- ],
2323
+ "doNotAllow": ["import_leads", "start_campaign", "generate_messages"],
2554
2324
  "hardRules": [
2555
2325
  "critique_failure_never_escalates",
2556
2326
  "critique_sample_size_bounded_by_config",
@@ -2563,10 +2333,7 @@
2563
2333
  "generate_message_cells_do_not_run_from_background_template_until_template_token_rules_approved"
2564
2334
  ],
2565
2335
  "watchRequired": true,
2566
- "waitFor": [
2567
- "review_batch_ready",
2568
- "sample_revision_required"
2569
- ],
2336
+ "waitFor": ["review_batch_ready", "sample_revision_required"],
2570
2337
  "transitions": {
2571
2338
  "review_batch_ready": "awaiting-user-greenlight",
2572
2339
  "escalation_triggered": "escalation"
@@ -2628,9 +2395,7 @@
2628
2395
  "tool": "update_campaign",
2629
2396
  "when": "user selected an available connected sender",
2630
2397
  "requiredValues": {
2631
- "senderIds": [
2632
- "{selectedSenderId}"
2633
- ],
2398
+ "senderIds": ["{selectedSenderId}"],
2634
2399
  "currentStep": "sequence",
2635
2400
  "watchNarration.stage": "review-ready"
2636
2401
  },
@@ -2646,20 +2411,11 @@
2646
2411
  "when": "after senderIds are attached",
2647
2412
  "requiredValues": {
2648
2413
  "campaignId": "{campaignId}",
2649
- "currentStep": "send"
2650
- },
2651
- "purpose": "bind the tier-aware recommended sequence before the launch decision",
2652
- "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."
2653
- },
2654
- {
2655
- "tool": "update_campaign",
2656
- "when": "after recommended sequence attach; required if sequence attach did not persist watchNarration with currentStep",
2657
- "requiredValues": {
2658
2414
  "currentStep": "send",
2659
2415
  "watchNarration.stage": "review-ready"
2660
2416
  },
2661
- "purpose": "show the Send tab once sender and sequence are ready",
2662
- "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."
2417
+ "purpose": "bind the tier-aware recommended sequence before the launch decision",
2418
+ "watchNarrationRule": "The sequence tool owns the visible beat: say the recommended sequence is attached, the browser is now on Send, and the next action is final launch greenlight. Do not follow with a separate step-only update_campaign fixup; do not imply anything has sent yet."
2663
2419
  },
2664
2420
  {
2665
2421
  "tool": "get_campaign_navigation_state",
@@ -2692,11 +2448,7 @@
2692
2448
  "action": "ask_final_launch_greenlight",
2693
2449
  "uses": "request_user_input",
2694
2450
  "singleChoice": true,
2695
- "choices": [
2696
- "Start campaign",
2697
- "Review campaign first",
2698
- "Pause here"
2699
- ],
2451
+ "choices": ["Start campaign", "Review campaign first", "Pause here"],
2700
2452
  "onUserStart": "claude-greenlight",
2701
2453
  "onReviewFirst": "awaiting-user-greenlight",
2702
2454
  "onPause": "awaiting-user-greenlight"
@@ -2712,10 +2464,7 @@
2712
2464
  "AskUserQuestion",
2713
2465
  "request_user_input"
2714
2466
  ],
2715
- "doNotAllow": [
2716
- "start_campaign",
2717
- "import_leads"
2718
- ],
2467
+ "doNotAllow": ["start_campaign", "import_leads"],
2719
2468
  "autoStart": false,
2720
2469
  "watchRequired": true,
2721
2470
  "waitFor": [
@@ -2734,9 +2483,7 @@
2734
2483
  "user_greenlight": "claude-greenlight",
2735
2484
  "ui_start_detected": "running"
2736
2485
  },
2737
- "optionalRequiredArtifacts": [
2738
- "lead-source-intake.json"
2739
- ]
2486
+ "optionalRequiredArtifacts": ["lead-source-intake.json"]
2740
2487
  },
2741
2488
  {
2742
2489
  "id": "claude-greenlight",
@@ -2777,9 +2524,7 @@
2777
2524
  },
2778
2525
  {
2779
2526
  "tool": "start_campaign",
2780
- "requiredFields": [
2781
- "campaignId"
2782
- ],
2527
+ "requiredFields": ["campaignId"],
2783
2528
  "persistsCurrentStep": "running",
2784
2529
  "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."
2785
2530
  },
@@ -2817,14 +2562,8 @@
2817
2562
  "action": "surface_campaign_live_confirmation"
2818
2563
  }
2819
2564
  ],
2820
- "allowedTools": [
2821
- "get_campaign",
2822
- "AskUserQuestion",
2823
- "request_user_input"
2824
- ],
2825
- "doNotAllow": [
2826
- "start_campaign"
2827
- ],
2565
+ "allowedTools": ["get_campaign", "AskUserQuestion", "request_user_input"],
2566
+ "doNotAllow": ["start_campaign"],
2828
2567
  "terminal": true
2829
2568
  },
2830
2569
  {
@@ -2852,14 +2591,8 @@
2852
2591
  ]
2853
2592
  }
2854
2593
  ],
2855
- "allowedTools": [
2856
- "AskUserQuestion",
2857
- "request_user_input"
2858
- ],
2859
- "doNotAllow": [
2860
- "start_campaign",
2861
- "import_leads"
2862
- ],
2594
+ "allowedTools": ["AskUserQuestion", "request_user_input"],
2595
+ "doNotAllow": ["start_campaign", "import_leads"],
2863
2596
  "terminal": true
2864
2597
  },
2865
2598
  {
@@ -2882,15 +2615,8 @@
2882
2615
  ]
2883
2616
  }
2884
2617
  ],
2885
- "allowedTools": [
2886
- "AskUserQuestion",
2887
- "request_user_input"
2888
- ],
2889
- "doNotAllow": [
2890
- "create_campaign",
2891
- "save_rubrics",
2892
- "import_leads"
2893
- ],
2618
+ "allowedTools": ["AskUserQuestion", "request_user_input"],
2619
+ "doNotAllow": ["create_campaign", "save_rubrics", "import_leads"],
2894
2620
  "terminal": true
2895
2621
  },
2896
2622
  {
@@ -2903,9 +2629,7 @@
2903
2629
  },
2904
2630
  {
2905
2631
  "action": "reset_draft_artifacts",
2906
- "preserve": [
2907
- "brief.md"
2908
- ],
2632
+ "preserve": ["brief.md"],
2909
2633
  "delete": [
2910
2634
  "lead-review.md",
2911
2635
  "lead-sample.json",
@@ -2915,15 +2639,8 @@
2915
2639
  ]
2916
2640
  }
2917
2641
  ],
2918
- "allowedTools": [
2919
- "AskUserQuestion",
2920
- "request_user_input"
2921
- ],
2922
- "doNotAllow": [
2923
- "create_campaign",
2924
- "save_rubrics",
2925
- "import_leads"
2926
- ],
2642
+ "allowedTools": ["AskUserQuestion", "request_user_input"],
2643
+ "doNotAllow": ["create_campaign", "save_rubrics", "import_leads"],
2927
2644
  "terminal": true
2928
2645
  },
2929
2646
  {
@@ -2936,27 +2653,12 @@
2936
2653
  },
2937
2654
  {
2938
2655
  "action": "reset_draft_artifacts",
2939
- "preserve": [
2940
- "brief.md",
2941
- "lead-review.md",
2942
- "lead-sample.json"
2943
- ],
2944
- "delete": [
2945
- "lead-filter.md",
2946
- "message-validation.md",
2947
- "rubric.json"
2948
- ]
2656
+ "preserve": ["brief.md", "lead-review.md", "lead-sample.json"],
2657
+ "delete": ["lead-filter.md", "message-validation.md", "rubric.json"]
2949
2658
  }
2950
2659
  ],
2951
- "allowedTools": [
2952
- "AskUserQuestion",
2953
- "request_user_input"
2954
- ],
2955
- "doNotAllow": [
2956
- "create_campaign",
2957
- "save_rubrics",
2958
- "import_leads"
2959
- ],
2660
+ "allowedTools": ["AskUserQuestion", "request_user_input"],
2661
+ "doNotAllow": ["create_campaign", "save_rubrics", "import_leads"],
2960
2662
  "terminal": true
2961
2663
  },
2962
2664
  {
@@ -2975,20 +2677,11 @@
2975
2677
  "lead-sample.json",
2976
2678
  "lead-filter.md"
2977
2679
  ],
2978
- "delete": [
2979
- "message-validation.md"
2980
- ]
2680
+ "delete": ["message-validation.md"]
2981
2681
  }
2982
2682
  ],
2983
- "allowedTools": [
2984
- "AskUserQuestion",
2985
- "request_user_input"
2986
- ],
2987
- "doNotAllow": [
2988
- "create_campaign",
2989
- "save_rubrics",
2990
- "import_leads"
2991
- ],
2683
+ "allowedTools": ["AskUserQuestion", "request_user_input"],
2684
+ "doNotAllow": ["create_campaign", "save_rubrics", "import_leads"],
2992
2685
  "terminal": true
2993
2686
  },
2994
2687
  {
@@ -3000,15 +2693,8 @@
3000
2693
  "target": ".aborted/"
3001
2694
  }
3002
2695
  ],
3003
- "allowedTools": [
3004
- "AskUserQuestion",
3005
- "request_user_input"
3006
- ],
3007
- "doNotAllow": [
3008
- "create_campaign",
3009
- "save_rubrics",
3010
- "import_leads"
3011
- ],
2696
+ "allowedTools": ["AskUserQuestion", "request_user_input"],
2697
+ "doNotAllow": ["create_campaign", "save_rubrics", "import_leads"],
3012
2698
  "terminal": true
3013
2699
  }
3014
2700
  ],