@sellable/mcp 0.1.128 → 0.1.130

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.
@@ -1,144 +1,87 @@
1
1
  You are Message Draft Builder for Sellable create-campaign-v2.
2
2
 
3
- Your job starts only after find-leads has produced `lead-review.md` and
4
- `lead-sample.json`, and the lead source has been approved or auto-confirmed.
5
- Work only on the message generation branch. Do not source new leads, create lead
6
- filters, import leads, create campaigns, ask the user questions, or mutate live
7
- campaign state. Do not call `update_campaign_brief`; the main thread owns the
8
- approval write.
9
-
10
- Required inputs:
11
-
12
- - `brief.md`
13
- - `lead-review.md`
14
- - `lead-sample.json`
15
- - campaign state from the parent thread
16
- - campaign table sample from the parent thread
17
-
18
- Required first steps:
19
-
20
- 1. Read the three required inputs.
21
- 2. Treat campaign state and the campaign table sample as the input of record.
22
- Disk files are context/debug aids, not durable state.
23
- 3. Use the embedded Message Review Safety Gate below. Do not load the full
24
- long-form `generate-messages` subskill in this `create-campaign-v2` path. If
25
- a needed rule is missing or the draft fails quality gates, return
26
- `revise-messaging` with the exact failure instead of pulling the long prompt
27
- into this worker.
28
-
29
- Owned outputs:
30
-
31
- - proposed template using supported `{{...}}` tokens
32
- - one sample message rendered from the proposed template
33
- - `message-validation.md` debug artifact
34
- - optional `message-prep.md` debug artifact
35
- - optional `message-candidate-drafts.md` debug artifact
36
-
37
- Do not write or modify:
38
-
39
- - `lead-filter.md`
40
- - `rubric.json`
41
- - `message-review.md`
42
- - `approval-packet.md`
43
- - `brief.md`
44
- - `lead-review.md`
45
- - `lead-sample.json`
46
-
47
- Process:
48
-
49
- 1. Run the embedded message-review safety-gate workflow in dry mode from the
50
- approved brief, lead-review source decision, and `lead-sample.json`.
51
- 2. Use `lead-sample.json` as the only lead sample source. Do not fetch new
52
- prospects or invent richer row signals.
53
- 3. Build proof inventory, token fill rules, token adherence, angle drafts,
54
- kill/combine review, skeptical-prospect review, winner gate, and a raw
55
- sendable selected winner.
56
- 4. If `lead-filter.md` already exists, cite only basis rows that pass it. If it
57
- does not exist yet, choose probable good-fit rows from `lead-sample.json` and
58
- mark the final reconciliation as pending.
59
- 5. Write `message-validation.md`. Do not write `message-review.md`; the parent
60
- thread owns the joined review after both builders finish.
61
- 6. Return the proposed template and one sample message for approval. Do not
62
- mutate campaignBrief. The main thread must show readable filters with
63
- reasons, show one sample message, ask the user to approve or edit, then write
64
- the approved template with `update_campaign_brief` only after approval.
65
- 7. If the user edits the proposal, the main thread updates the proposal and asks
66
- again. Do not queue enrichment, validation rows, or Generate Message work
67
- until the approved template write succeeds.
68
-
69
- Return a concise final status with:
70
-
71
- - artifacts written
72
- - whether the embedded message-review safety-gate rules passed or returned
73
- `revise-messaging`
74
- - lead sample basis used
75
- - proposed template and one sample message
76
- - selected winner summary
77
- - whether final reconciliation with `lead-filter.md` is complete or pending
78
-
79
- Quality bar:
80
-
81
- - Do not synthesize a lightweight message from general knowledge. The artifact
82
- must prove the embedded message-review safety-gate workflow ran.
83
- - There is no generate-message fast mode in this path. The embedded gate is the
84
- same campaign-launch quality contract used by the parent-thread compatibility
85
- path, not a lower-quality shortcut.
86
- - Message generation can start before `lead-filter.md`, but message review
87
- cannot start until the parent verifies the selected basis rows still pass the
88
- final filter.
89
-
90
- ## Embedded Message Review Safety Gate
91
-
92
- Use this campaign-launch subset to produce a truthful first-send message,
93
- rendered token examples, and a decision without loading the full long-form
94
- message prompt.
95
-
96
- Required `message-validation.md` sections:
97
-
98
- - `Status`
99
- - `Mode`
100
- - `Lead Sample Basis`
101
- - `Strongest Reply Reason`
102
- - `Campaign Element Pool`
103
- - `Gold Standard Strategy Map`
104
- - `Current Campaign Translation`
105
- - `Token Fill Rules`
106
- - `Token Adherence Table`
107
- - `Angle Drafts`
108
- - `Kill / Combine Review`
109
- - `Finalizer Pass`
110
- - `Gold-Standard Quality Gate`
111
- - `Skeptical Prospect Review`
112
- - `Winner Gate`
113
- - `Selected Winner`
114
- - `Findings`
115
- - `Recommendation`
116
-
117
- Quality gates:
3
+ Your job starts only after the source is approved and the bounded review batch
4
+ exists in the campaign table. Work only on the message-generation branch. Do not
5
+ source leads, create lead filters, import leads, confirm lead lists, queue cells,
6
+ attach sequences, start campaigns, ask the user questions, or mutate live
7
+ campaign state. The main thread owns approval and campaign writes.
118
8
 
119
- - The selected winner is one first outbound send only. No post-accept DM,
120
- follow-up, cadence branch, or sequence copy.
121
- - Explain what the product is and what it does in plain language before asking
122
- for a call.
123
- - Use only proof from the brief, source review, sample, campaign state, or
124
- explicit user answers. Unsupported reply-rate, meeting-rate, ROI, revenue,
125
- and customer-logo claims are blocked.
126
- - Include at least one supported `{{token}}` when templating, plus a complete
127
- rendered good-fill example and a complete rendered omit/fallback example.
9
+ ## Source Of Truth
10
+
11
+ Use the live campaign inputs supplied by the parent thread:
12
+
13
+ - `campaignId`
14
+ - `campaignBrief` / campaign brief content model
15
+ - selected source decision and provider state
16
+ - `selectedLeadListId` or selected source list context
17
+ - `workflowTableId`
18
+ - imported review-batch rows from that selected list
19
+ - any already-saved fit/rubric result summaries supplied by the parent
20
+
21
+ Do not require or hunt for `brief.md`, `lead-review.md`, or `lead-sample.json`.
22
+ Those files are optional debug context only when the parent explicitly provides
23
+ them. Never inspect the product database directly, never run `psql`, and never
24
+ read stale local markdown files to reconstruct campaign state.
25
+
26
+ ## Required First Steps
27
+
28
+ 1. Load the full long-form generate-messages prompt:
29
+
30
+ `get_subskill_prompt({ subskillName: "generate-messages", offset, limit })`
31
+ until `hasMore` is false.
32
+
33
+ 2. Use that prompt as the drafting contract. The create-campaign safety gate may
34
+ be used as an approval checklist, but it does not replace the long prompt.
35
+ 3. Draft only from the campaign brief, selected source context, and imported
36
+ review-batch rows supplied by the parent.
37
+ 4. Keep the work provisional until the user chooses `Use Template` in Messages.
38
+
39
+ ## Owned Output
40
+
41
+ Return the following to the parent thread:
42
+
43
+ - proposed first-message template using supported `{{...}}` tokens
44
+ - token fill rules and fallbacks
45
+ - one rendered good-fill sample for a plausible passing review-batch row
46
+ - one omit/fallback sample when the row signal is not safe
47
+ - pass/fail notes against the generate-messages quality gates
48
+
49
+ Write `message-validation.md`, `message-prep.md`, or
50
+ `message-candidate-drafts.md` only when the parent explicitly asks for debug
51
+ artifacts. Normal live campaign runs can return the same content directly.
52
+
53
+ ## Hard Rules
54
+
55
+ - Do not call product Generate Message cells. This worker drafts the template
56
+ recommendation only.
57
+ - Do not call `update_campaign_brief`; the main thread writes the approved
58
+ template after user approval.
59
+ - Do not overwrite an existing approved message/template.
60
+ - Do not use unsupported reply-rate, meeting-rate, ROI, revenue, or
61
+ customer-logo claims.
128
62
  - Do not use internal tokens such as `{{profile_signal}}` in customer-facing
129
63
  copy.
130
64
  - Do not put bracketed instructions in the message body, such as `[ROW_BRIDGE]`,
131
65
  `[insert]`, or `[generated]`.
132
- - Optional row-specific personalization must be grounded in a row field or
133
- omitted entirely.
134
66
  - Engagement-source personalization is a special case, not the default opener.
135
67
  Do not write `saw you {{engagement_context}} on {{post_context}}`, `saw you
136
68
  reacted to`, `saw you engaging with`, or equivalent source-citation copy as a
137
- default hook. Only refer to the prospect's engagement when the line is
138
- self-aware and low-certainty, for example `not sure if this is too specific,
139
- but the [topic] thread felt close enough to send`. Otherwise omit the
140
- engagement signal and use role/company/problem context.
141
- - Subjects should be short, buyer-relevant, and specific. Avoid `quick
142
- question`, `demo`, `founder call`, sender names, and generic `outbound`.
143
- - If the message is plausible but not ready to send, set `Recommendation:
144
- revise-messaging`.
69
+ default hook. Only refer to engagement when the line is self-aware and
70
+ low-certainty, for example: `not sure if this is too specific, but the
71
+ [topic] thread felt close enough to send`. Otherwise omit the engagement
72
+ signal and use role/company/problem context.
73
+ - The selected winner is one first outbound send only. No post-accept DM,
74
+ follow-up, cadence branch, sequence copy, or launch copy.
75
+
76
+ ## Final Response
77
+
78
+ Return a concise status with:
79
+
80
+ - prompt basis loaded: `generate-messages`
81
+ - live campaign basis used
82
+ - proposed template
83
+ - token fill rules/fallbacks
84
+ - one rendered passing-row sample
85
+ - one rendered omit/fallback sample
86
+ - quality-gate pass/fail summary
87
+ - whether final template review is ready or needs revision
@@ -46,6 +46,7 @@
46
46
  "Glob",
47
47
  "mcp__sellable__get_provider_prompt",
48
48
  "mcp__sellable__search_signals",
49
+ "mcp__sellable__select_promising_posts",
49
50
  "mcp__sellable__fetch_post_engagers"
50
51
  ]
51
52
  }
@@ -205,14 +206,20 @@
205
206
  "displayName": "Message Draft Builder",
206
207
  "target": "generate-messages",
207
208
  "inputs": [
208
- "brief.md",
209
- "lead-review.md",
210
- "lead-sample.json"
209
+ "campaignId",
210
+ "campaignBrief",
211
+ "selected source state",
212
+ "selectedLeadListId",
213
+ "workflowTableId",
214
+ "imported review-batch rows"
211
215
  ],
212
216
  "producesArtifacts": [
213
- "message-validation.md"
217
+ "template recommendation",
218
+ "token fill rules",
219
+ "rendered sample"
214
220
  ],
215
221
  "optionalProducesArtifacts": [
222
+ "message-validation.md",
216
223
  "message-prep.md",
217
224
  "message-candidate-drafts.md"
218
225
  ],
@@ -20,7 +20,7 @@ export const cellToolDefinitions = [
20
20
  },
21
21
  {
22
22
  name: "queue_cells",
23
- description: "Queue cells for processing (enrichment, ICP scoring, etc). Use with cell IDs from get_rows_minimal (enrichCellId, icpCellId).",
23
+ description: "Queue explicit cells for processing. In create-campaign-v2 Filter Leads, pass only bounded review-batch enrichCellId values from get_rows_minimal; do not pass icpCellId values or full-table cells because the workflow cascade handles downstream ICP scoring.",
24
24
  inputSchema: {
25
25
  type: "object",
26
26
  properties: {
@@ -99,7 +99,9 @@ export interface SourceScoutRegistryResponse {
99
99
  export interface PostFindLeadsScoutRegistryResponse {
100
100
  version: number;
101
101
  trigger: "find_leads_source_approved";
102
- requiredArtifacts: string[];
102
+ requiredArtifacts?: string[];
103
+ requiredInputs?: string[];
104
+ optionalDebugArtifacts?: string[];
103
105
  scouts: Array<{
104
106
  id: string;
105
107
  name: string;
@@ -123,7 +125,9 @@ export interface PostFindLeadsScoutRegistryResponse {
123
125
  }>;
124
126
  joinGate: {
125
127
  afterAllComplete: boolean;
126
- requiredArtifacts: string[];
128
+ requiredArtifacts?: string[];
129
+ requiredState?: string[];
130
+ optionalDebugArtifacts?: string[];
127
131
  show: string[];
128
132
  nextStage: string;
129
133
  };
@@ -242,9 +242,9 @@ export function getSourceScoutRegistry() {
242
242
  legacy: agent.legacy,
243
243
  })),
244
244
  usage: {
245
- codex: "Spawn credible scouts by the returned `name` values in one assistant turn only when the current Codex host exposes those custom agents.",
246
- claude: "Invoke Claude Code Task/Agent subagents with subagent_type equal to the returned `name` values only when the current Claude session lists those agents.",
247
- parentThreadRule: "Named agents are optional acceleration. If they are absent, do not customer-surface install status; run the same provider probes with MCP tools from the parent thread. Do not preload every provider prompt before spawning agents; each scout loads only the provider prompt for its lane.",
245
+ codex: "Source scouting is sequential by default. Launch multiple returned `name` values in one assistant turn only when the user asked for comparison, a prior lane failed, or the active flow marks the first lane borderline.",
246
+ claude: "Source scouting is sequential by default. Invoke multiple Task/Agent subagents with subagent_type equal to returned `name` values only when comparison, fallback, or borderline-source conditions apply.",
247
+ parentThreadRule: "Named agents are optional acceleration. If they are absent, do not customer-surface install status; run the same provider probes with MCP tools from the parent thread. Default fallback order: Signal Discovery / LinkedIn engagement, then Sales Nav recent activity, then broader Sales Nav title search, then Prospeo account/contact search. Do not preload every provider prompt before spawning agents; each scout loads only the provider prompt for its lane.",
248
248
  },
249
249
  };
250
250
  }
@@ -254,7 +254,14 @@ export function getPostFindLeadsScoutRegistry() {
254
254
  return {
255
255
  version: registry.version,
256
256
  trigger: "find_leads_source_approved",
257
- requiredArtifacts: ["brief.md", "lead-review.md", "lead-sample.json"],
257
+ requiredInputs: [
258
+ "campaignId",
259
+ "campaignBrief",
260
+ "source decision and selectedLeadList/source state",
261
+ "workflowTableId",
262
+ "imported review-batch rows from selectedLeadList",
263
+ ],
264
+ optionalDebugArtifacts: ["brief.md", "lead-review.md", "lead-sample.json"],
258
265
  scouts: scouts.map((agent) => ({
259
266
  id: String(agent.id || ""),
260
267
  name: String(agent.name || ""),
@@ -282,14 +289,15 @@ export function getPostFindLeadsScoutRegistry() {
282
289
  })),
283
290
  joinGate: {
284
291
  afterAllComplete: true,
285
- requiredArtifacts: ["lead-filter.md", "message-validation.md"],
292
+ requiredState: ["leadScoringRubrics", "messageDraftRecommendation"],
293
+ optionalDebugArtifacts: ["lead-filter.md", "message-validation.md"],
286
294
  show: ["readable_filters_with_reasons", "sample_message_for_approval"],
287
295
  nextStage: "message-review",
288
296
  },
289
297
  usage: {
290
298
  codex: "After the user approves or auto-confirms the lead source, spawn both returned scout `name` values in one assistant turn only when the current Codex host exposes those custom agents.",
291
299
  claude: "After lead source approval, invoke both returned Task/Agent subagents in one assistant message only when the current Claude session lists those agents, so filter-leads and message generation run concurrently.",
292
- parentThreadRule: "Named agents are optional acceleration. If they are absent, do not customer-surface install status; the main thread still orchestrates filter and message branches from brief.md, lead-review.md, and lead-sample.json. The message branch should use the baked-in message-scout prompt or the create-campaign-v2 message-review safety gate asset, not the full long generate-messages prompt in the normal path. Join before message review.",
300
+ parentThreadRule: "Named agents are optional acceleration. If they are absent, do not customer-surface install status; the main thread still orchestrates filter and message branches from CampaignOffer state, selected source state, workflowTableId, and imported review-batch rows. Debug markdown/json artifacts are optional only. The message branch must load the full generate-messages prompt and may use the create-campaign-v2 message-review safety gate as a supplemental approval check. Join before message review.",
293
301
  },
294
302
  };
295
303
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sellable/mcp",
3
- "version": "0.1.128",
3
+ "version": "0.1.130",
4
4
  "type": "module",
5
5
  "description": "Sellable MCP server for Claude Code and Codex campaign workflows",
6
6
  "main": "dist/index.js",
@@ -13,7 +13,6 @@ allowed-tools:
13
13
  - mcp__sellable__get_provider_prompt
14
14
  - mcp__sellable__get_source_scout_registry
15
15
  - mcp__sellable__get_post_find_leads_scout_registry
16
- - mcp__sellable__get_message_prompt
17
16
  - mcp__sellable__get_active_workspace
18
17
  - mcp__sellable__list_senders
19
18
  - mcp__sellable__get_sender
@@ -71,10 +70,11 @@ Disk artifacts are optional debug/UAT diagnostics; normal customer runs should
71
70
  not create, link, or surface local draft files unless the user explicitly asks
72
71
  for them. Resume, gating, and handoff read campaign state first. The
73
72
  watchable campaign exists after the short brief; lead import is bounded to the
74
- first review batch. After that, the user chooses whether to use filters or skip,
75
- then Messages first shows Use Template and AI Generated. The Message Draft
76
- Builder may work in the background, but template review waits for the filter
77
- path and an explicit Use Template choice.
73
+ first review batch. After that, the user chooses whether to use filters or skip.
74
+ When filters are chosen, save rubrics, then pause before queueing enrichment or
75
+ filtering: tell the user the fit rules are saved, the message template needs
76
+ approval next, and enrichment/filtering will start after that approval. Use
77
+ Template is the default message path; AI Generated is only an explicit opt-out.
78
78
 
79
79
  ## Opening Turn Contract
80
80
 
@@ -148,12 +148,13 @@ calls `get_post_find_leads_scout_registry`, then launches the returned
148
148
  filter-leads scout and message-generation scout together when real subagents are
149
149
  available and the current session exposes the returned names. Message
150
150
  generation is the provisional Message Draft Builder: it may start after the
151
- review batch exists, including while the user is on filter choice, but template
152
- review cannot start until the user answers filter choice and chooses Use
153
- Template in Messages. AI Generated is an explicit opt-out that cancels or
154
- ignores the background template draft. If the post-lead agents are absent, the
155
- main thread still orchestrates the same branches from the compact context with
156
- MCP tools/assets.
151
+ review batch exists, including while the user is on filter choice, but workflow
152
+ cell execution must wait. After rubrics are saved, do not queue enrichment,
153
+ filtering, or Generate Message cells until the user approves the message
154
+ template. AI Generated is an explicit opt-out that cancels or ignores the
155
+ background template draft. If the post-lead agents are absent, the main thread
156
+ still orchestrates the same branches from the compact context with MCP
157
+ tools/assets.
157
158
 
158
159
  Use rendered Markdown for user review surfaces, not fenced code blocks. Keep
159
160
  lines short, use indexed section labels and bullets, and translate internal
@@ -561,24 +562,26 @@ updates.
561
562
  asset loader so they share the same config.
562
563
  3. Follow that prompt and workflow config exactly.
563
564
  4. For message generation, use the `post-find-leads-message-scout` agent when
564
- available; its prompt carries the campaign-launch message rules. In the
565
- parent-thread fallback, load
566
- `mcp__sellable__get_subskill_asset({ subskillName: "create-campaign-v2", assetPath: "references/message-review-safety-gate.md" })`.
567
- Do not load the full long-form `generate-messages` subskill in this
568
- create-campaign path; if the safety gate cannot safely approve the draft,
569
- route to revise messaging with the concrete failure.
570
- This compatibility path is not a generate-message fast mode.
571
- Do not synthesize `message-validation.md` from the brief, lead review, or
565
+ available. The worker and parent-thread fallback must load the full
566
+ long-form `generate-messages` prompt with
567
+ `mcp__sellable__get_subskill_prompt({ subskillName: "generate-messages", offset, limit })`
568
+ until `hasMore=false`. The create-campaign message-review safety gate is a
569
+ supplemental approval checklist, not a replacement for the long prompt. Use
570
+ campaign state, campaign brief content, selected source state, and imported
571
+ review-batch rows as the source of truth; do not read stale local markdown,
572
+ inspect the database directly, or synthesize `message-validation.md` from
572
573
  general knowledge.
573
574
  5. Create the campaign shell early with the v1 brief so the user can open the
574
575
  watch link and see useful setup state immediately. Import only the first
575
576
  bounded review batch after the source is attached to the campaign; do not
576
577
  queue workflow cells, attach a sequence, or start until the filter choice is
577
- resolved, Messages has shown Use Template / AI Generated, template/token
578
- rules are approved when Use Template is chosen, rubrics are saved when
579
- filters are enabled, and the approved message set is synced into the campaign
580
- brief. Product Generate Message cells must not run from the background
581
- template path before that template/token approval.
578
+ resolved, rubrics are saved when filters are enabled, template/token rules
579
+ are approved on the default Use Template path, and the approved message set
580
+ is synced into the campaign brief. After rubrics save, pause and say the fit
581
+ rules are saved; approve the message template next; after approval, queue the
582
+ bounded review-batch `enrichCellId` cells to kick off enrichment/filtering.
583
+ Product Generate Message cells must not run from the background template
584
+ path before that template/token approval.
582
585
  Do not ask the user to approve the brief before shell creation unless they
583
586
  explicitly requested a no-write draft; the shell itself is the review surface.
584
587
  6. The main thread owns watch navigation. Call
@@ -571,29 +571,19 @@ until the filter choice is resolved and you choose Use Template.`
571
571
  provisional candidate work from probable good-fit rows in `lead-sample.json`,
572
572
  but the final `message-validation.md` winner must cite basis rows from
573
573
  `lead-sample.json` that still pass `lead-filter.md`.
574
- - Parallel means real parallel execution, not optimistic progress copy. For the
575
- lead-source scout, first call `get_source_scout_registry` and use the
576
- returned canonical `name` values. In Codex, explicitly spawn one named custom scout per
577
- credible source lane when those subagents are visible in the current runtime:
578
- `source-scout-linkedin-engagement`
579
- (display: LinkedIn Engagement Scout, powered by the `signal-discovery`
580
- provider prompt), `source-scout-sales-nav` (Sales Nav Scout), and
581
- `source-scout-prospeo-contact` (Prospeo Contact Scout). For Claude Code,
582
- invoke the generated `source-scout-*` Task/Agent subagents for all credible
583
- lanes in one assistant message only when the session exposes those names.
584
- They are installed from the same canonical Sellable agent registry and carry
585
- explicit Sellable MCP tool allowlists when `@sellable/install` is active. The
586
- parent thread should not preload every provider prompt before spawning scouts;
587
- each scout loads only its own provider prompt. If host subagents are
588
- unavailable or the names are not visible, use the same provider probes via
589
- independent MCP/tool calls in the parent thread or dedicated Sellable MCP
590
- tools that perform server-side `Promise.all` fan-out. Do not write "source
591
- scout agents are not installed" or similar host status into customer-facing
592
- output. If real parallel execution is not available or not allowed, run the
593
- same DAG sequentially and use honest copy: `I’ll tighten the filter first,
594
- then draft the message from the same sample.` Never say `kicking off two
595
- workstreams`, `in parallel`, or `background` unless parallel branches were
596
- actually launched.
574
+ - Parallel means real parallel execution, not optimistic progress copy. Source
575
+ scouting is sequential by default: start with the first recommended lane from
576
+ `flow.v2.json`, usually Signal Discovery / LinkedIn engagement. Only launch
577
+ multiple named source scouts when the user explicitly requested comparison, a
578
+ prior lane failed, or the active flow marks the first lane borderline. The
579
+ fallback order is plain: use Sales Nav to find ICP people who are actively
580
+ posting on LinkedIn; if that is not enough, search Sales Nav by titles; if
581
+ that is still not enough, use Prospeo for a broader account/contact path.
582
+ Before any source scout dispatch, call `get_source_scout_registry` and use the
583
+ returned canonical `name` values. The parent thread should not preload every
584
+ provider prompt; each scout loads only its own provider prompt. Do not write
585
+ "source scout agents are not installed" or similar host status into
586
+ customer-facing output.
597
587
  - Source scout parallelism must not hide all provider work from the watch UI.
598
588
  The parent thread owns the first visible provider search: call the chosen
599
589
  provider prompt/search with the minted CampaignOffer id and provider
@@ -633,22 +623,25 @@ batch`, and a no-launch safety note. Do not leave the guide saying
633
623
  recovery and ask whether to retry import, import more from the approved
634
624
  source, or revise source. Active filtering starts only after rubrics are
635
625
  saved; do not say filtering the batch before rubrics are saved.
636
- - After Lead Fit Builder saves rubrics, move the watched browser to Filter
637
- Leads before waiting for message work to finish. Persist
638
- `enableICPFilters: true`, `currentStep: "apply-icp-rubric"`, and
639
- `watchNarration.stage: "fit-message"` so the user can see fit filtering
640
- happen while the Message Draft Builder finishes. The next user-facing Messages
641
- screen must show mode choice first: Use Template and AI Generated. Use
642
- Template is recommended. AI Generated is an explicit opt-out that cancels or
626
+ - When the user chooses filters, immediately persist `enableICPFilters: true`
627
+ and `currentStep: "create-icp-rubric"` so the watched app leaves the filter
628
+ choice screen and shows that Codex is defining the rules in chat. After Lead
629
+ Fit Builder saves rubrics, move the watched browser to Filter Leads before
630
+ waiting for message work to finish. Persist `currentStep:
631
+ "apply-icp-rubric"` and `watchNarration.stage: "fit-message"`, then read the
632
+ bounded review batch with `get_rows_minimal`, but do not queue workflow cells
633
+ yet. Tell the user the fit rules are saved, the message template needs
634
+ approval next, and enrichment/filtering will start after approval. Do not call
635
+ `check_rubric` in the normal create-campaign-v2 path. Use Template is the
636
+ default message path. AI Generated is an explicit opt-out that cancels or
643
637
  ignores the background template draft and must not let late template output
644
638
  overwrite campaign state. Product Generate Message cells should not run from
645
639
  the background template path until template/token rules are approved. After
646
640
  message approval, update the campaign brief with an Approved Message Template
647
- containing `{{...}}` tokens and keep `enableICPFilters: true`; Generate
648
- Message detects template mode from those tokens, not from
649
- `useMessagingTemplate`. Sample validation then runs the review-batch cascade,
650
- and the user should be walked through fit results, generated message results,
651
- and Settings/sender/sequence handoff.
641
+ containing `{{...}}` tokens and keep `enableICPFilters: true`; then queue only
642
+ pending/error `enrichCellId` values with `queue_cells` to start enrichment and
643
+ filtering. Generate Message detects template mode from those tokens, not from
644
+ `useMessagingTemplate`.
652
645
  - During pre-import validation, do not call `check_rubric`; use the lead-filter
653
646
  artifacts and only use campaign-backed scoring after Step 13 imports the
654
647
  15-lead test batch.
@@ -1283,21 +1276,22 @@ Do not:
1283
1276
 
1284
1277
  Step 3 is the Message Draft Builder path. It is orchestrated by the main thread
1285
1278
  and executed either by the `post-find-leads-message-scout` worker or by the
1286
- parent-thread fallback. The worker carries the campaign-launch message rules in
1287
- its agent prompt. The parent-thread fallback must load the small safety-gate
1288
- reference before writing message artifacts:
1279
+ parent-thread fallback. The worker and fallback must load the full
1280
+ `generate-messages` prompt with `get_subskill_prompt` and use it as the drafting
1281
+ contract. The small safety-gate reference is only a supplemental approval
1282
+ check, not a replacement for the long message workflow:
1289
1283
 
1290
1284
  ```
1285
+ mcp__sellable__get_subskill_prompt({ subskillName: "generate-messages", offset, limit }) until hasMore=false
1291
1286
  mcp__sellable__get_subskill_asset({ subskillName: "create-campaign-v2", assetPath: "references/message-review-safety-gate.md" })
1292
1287
  ```
1293
1288
 
1294
- Do not load the full long-form `generate-messages` subskill in
1295
- `create-campaign-v2`. The safety-gate asset is a parent-thread compatibility
1296
- path for hosts that cannot launch `post-find-leads-message-scout`, not a
1297
- generate-message fast-mode prompt and not the preferred route. If the safety gate
1298
- is missing a needed campaign-specific rule or the draft fails quality gates, stop
1299
- at `revise-messaging` with the exact failure instead of pulling the long prompt
1300
- into the main thread.
1289
+ Use campaign state, campaign brief content, selected source state, and the
1290
+ selected/imported review-batch rows as the source of truth. Do not read
1291
+ `brief.md`, `lead-review.md`, or `lead-sample.json` as required state in the
1292
+ normal live campaign path; those files are optional debug context only. If the
1293
+ long prompt plus safety gate cannot safely approve the draft, stop at
1294
+ `revise-messaging` with the exact failure.
1301
1295
 
1302
1296
  Do NOT proceed to Step 4 (message review gate) unless the user has answered
1303
1297
  filter choice, the Messages step has shown the mode choice, the user chose Use
@@ -230,31 +230,24 @@ setting: `~/.codex/config.toml` with
230
230
 
231
231
  ## Parallelism + Naming
232
232
 
233
- Source-angle comparison should be real, not implied. When the source is not
234
- supplied and multiple source angles are viable, scout Signals, Sales Nav, and
235
- relevant domain/contact paths as independent branches when the host can do so.
236
- Call `get_source_scout_registry` first and use the returned canonical `name`
237
- values; the names below are the current registry entries. In Codex, explicitly
238
- spawn named custom scouts in the same turn when the current runtime exposes
239
- them:
240
- `source-scout-linkedin-engagement` (LinkedIn Engagement Scout, backed by the
241
- `signal-discovery` provider prompt), `source-scout-sales-nav` (Sales Nav Scout), and
242
- `source-scout-prospeo-contact` (Prospeo Contact Scout) for the credible lanes. Codex
243
- does not infer subagent fan-out from generic "compare paths" wording. In Claude
244
- Code, invoke the generated `source-scout-linkedin-engagement`, `source-scout-sales-nav`,
245
- and/or `source-scout-prospeo-contact` Task/Agent subagents in one assistant
246
- message only when the session lists those names, so Claude can run the source
247
- lanes concurrently/background. These agents come from the same canonical
248
- Sellable registry and must load their matching provider prompt before
249
- searching. The parent thread should not fetch every provider prompt first; that
250
- burns time and often degenerates into parallel tool calls instead of true
251
- subagents. When asking the user to approve a brief with Sellable finding leads,
252
- use an explicit label like `Approve brief + use background source scouts` only
253
- when the current runtime exposes the named agents; otherwise use `Approve brief
254
-
255
- - compare source paths`. If the host runs them sequentially or the named agents
256
- are absent, keep the output numeric but do not claim the source scout was
257
- parallel or surface install status to the customer.
233
+ Source selection is sequential by default. Start with the first recommended
234
+ source lane from `flow.v2.json`; for the current campaign shape that is usually
235
+ Signal Discovery / LinkedIn engagement. Only try the next fallback after the
236
+ current lane is not viable, the user asks for a comparison, or the first lane is
237
+ borderline and needs a second source to avoid a bad recommendation. The fallback
238
+ order must stay plain: use Sales Nav to find ICP people who are actively posting
239
+ on LinkedIn; if that is not enough, search Sales Nav by titles; if that is still
240
+ not enough, use Prospeo for a broader account/contact path.
241
+
242
+ Source-angle comparison should be real, not implied. Call
243
+ `get_source_scout_registry` before dispatching source scouts and use the
244
+ returned canonical `name` values. In Codex or Claude Code, launch multiple named
245
+ source scouts in the same turn only when the user explicitly requested source
246
+ comparison, a prior lane failed, or the active flow marks the first lane as
247
+ borderline. The parent thread should not fetch every provider prompt first; each
248
+ scout loads only its matching provider prompt. If source work is sequential,
249
+ keep the output numeric but do not claim the source scout was parallel or
250
+ surface install status to the customer.
258
251
 
259
252
  For post-lead work, call `get_post_find_leads_scout_registry` after source
260
253
  approval and use the returned canonical `name` values. Launch both returned
@@ -963,9 +963,10 @@
963
963
  "targetLeadCountFromConfig": "sourceTargetGoodFitLeadCount (default 300) for provider source-list materialization, not import.importLimit",
964
964
  "signalDiscoveryRequiredFields": [
965
965
  "targetEngagerCount from approved source math",
966
- "maxPostsToScrape or selected post plan when available"
966
+ "approved promoted post ids selected with select_promising_posts",
967
+ "maxPostsToScrape from approved source math"
967
968
  ],
968
- "signalDiscoveryRule": "For Signal Discovery, pass targetEngagerCount = ceil(sourceTargetGoodFitLeads / sampledFitRateAfterCleanup) and maxPostsToScrape = postsNeeded when known. This prevents import_leads from scraping every selected/promoted sample post by default. The first campaign review batch is still limited by confirm_lead_list targetLeadCount import.importLimit.",
969
+ "signalDiscoveryRule": "For Signal Discovery, promote/select the exact approved posts with select_promising_posts before import_leads. Then pass targetEngagerCount = ceil(sourceTargetGoodFitLeads / sampledFitRateAfterCleanup) and maxPostsToScrape = postsNeeded from the approved source math. If no approved promoted posts are attached to the campaign, stop and route back to find-leads; do not import from unpromoted sample/search results. The first campaign review batch is still limited by confirm_lead_list targetLeadCount import.importLimit.",
969
970
  "onZeroLeads": "escalate_hard_fail",
970
971
  "modeAddHandshake": {
971
972
  "firstCallReturns": "needsModeSelection=true + existingLeadListId",
@@ -976,7 +977,8 @@
976
977
  "defaultBranch": "normal-discovery",
977
978
  "skipWhen": "resolved source branch returns sourceLeadListId for confirm_lead_list",
978
979
  "requiredBeforeCall": [
979
- "campaign_attached_source_state"
980
+ "campaign_attached_source_state",
981
+ "for Signal Discovery: approved_promoted_posts_attached_to_campaign"
980
982
  ]
981
983
  },
982
984
  {
@@ -1069,11 +1071,7 @@
1069
1071
  "review_batch_imported": "filter-choice",
1070
1072
  "escalation_triggered": "escalation"
1071
1073
  },
1072
- "requiredArtifacts": [
1073
- "brief.md",
1074
- "lead-review.md",
1075
- "lead-sample.json"
1076
- ],
1074
+ "requiredArtifacts": [],
1077
1075
  "requiredCampaignState": [
1078
1076
  "campaignId",
1079
1077
  "providerSearchAssociation",
@@ -1102,7 +1100,18 @@
1102
1100
  "currentStep": "filter-choice",
1103
1101
  "watchNarration.stage": "fit-message"
1104
1102
  },
1105
- "watchNarrationRule": "Show Added to Campaign with a Choose filters or skip decision. Do not say filtering the batch before rubrics are saved."
1103
+ "watchNarrationRule": "Ask whether the user wants to further filter the imported leads. Say whether the selected lead list looks mixed/good/noisy based on the imported 15-row review batch, then ask Choose filters or skip. Do not say filtering the batch before rubrics are saved."
1104
+ },
1105
+ {
1106
+ "action": "persist_filters_enabled_choice",
1107
+ "tool": "update_campaign",
1108
+ "when": "immediately after the user chooses filters_enabled",
1109
+ "requiredValues": {
1110
+ "enableICPFilters": true,
1111
+ "currentStep": "create-icp-rubric",
1112
+ "watchNarration.stage": "fit-message"
1113
+ },
1114
+ "watchNarrationRule": "Move the watched app to Filter Rules and show passive copy that Codex is defining the filters in chat. Do not leave the user on filter-choice after they choose filters."
1106
1115
  }
1107
1116
  ],
1108
1117
  "allowedTools": [
@@ -1136,6 +1145,8 @@
1136
1145
  "interruptOnly": true,
1137
1146
  "requiredCampaignState": [
1138
1147
  "campaignId",
1148
+ "campaignBrief",
1149
+ "selectedLeadListId",
1139
1150
  "workflowTableId"
1140
1151
  ]
1141
1152
  },
@@ -1158,9 +1169,9 @@
1158
1169
  "name": "filter-leads",
1159
1170
  "target": "filter-leads",
1160
1171
  "inputs": [
1161
- "brief.md",
1162
- "lead-review.md",
1163
- "lead-sample.json",
1172
+ "campaignBrief",
1173
+ "selected source decision",
1174
+ "selectedLeadListId",
1164
1175
  "workflowTableId review-batch rows"
1165
1176
  ],
1166
1177
  "producesArtifacts": [
@@ -1176,30 +1187,33 @@
1176
1187
  "target": "generate-messages",
1177
1188
  "mode": "DRY MODE",
1178
1189
  "inputs": [
1179
- "brief.md",
1180
- "lead-review.md",
1181
- "lead-sample.json",
1190
+ "campaignBrief",
1191
+ "selected source decision",
1192
+ "selectedLeadListId",
1182
1193
  "workflowTableId review-batch rows"
1183
1194
  ],
1184
1195
  "producesArtifacts": [
1185
- "message-validation.md"
1196
+ "template recommendation",
1197
+ "token fill rules",
1198
+ "rendered sample"
1186
1199
  ],
1187
1200
  "optionalProducesArtifacts": [
1201
+ "message-validation.md",
1188
1202
  "message-prep.md",
1189
1203
  "message-candidate-drafts.md"
1190
1204
  ],
1191
1205
  "ownership": "proof inventory, token strategy, angle drafting, skeptical-prospect review, and selected winner only"
1192
1206
  }
1193
1207
  ],
1194
- "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 brief.md, lead-review.md, lead-sample.json, and the imported campaign table sample, including while the user is on filter choice. This background template draft is provisional speed work only. The message worker carries the campaign-launch message rules in its agent prompt; parent-thread fallback loads references/message-review-safety-gate.md. Legacy/resume preview prep still requires at least 3 probable good-fit rows. It may run beside filter-leads, but the cascade cannot queue until save_rubrics and update_campaign_brief both succeed.",
1195
- "finalMessageReconcileRule": "message-validation.md may start before lead-filter.md exists, but before message-review it must cite only imported review-batch rows that still pass lead-filter.md. If the selected winner depends on a row later excluded by lead-filter.md, revise message-generation before message review.",
1208
+ "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. Legacy/resume preview prep still requires at least 3 probable good-fit rows. It may run beside filter-leads, but product Generate Message cells cannot queue until save_rubrics and update_campaign_brief both succeed.",
1209
+ "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.",
1196
1210
  "finalTemplateReviewLockRule": "cannot start template review until filter choice is answered and the user chooses Use Template in Messages. If filters are enabled, lock template review until saved rubrics exist, a bounded filter run has completed, and there are usable passing/probably passing rows. prefer at least 2 usable passing rows for template review; if only 1 passes, show weak-sample copy and ask whether to continue, revise, import more, or skip filters. If filters are skipped, route to Messages mode choice once the review batch is ready. AI Generated is an explicit opt-out and must cancel or ignore the background template draft.",
1197
1211
  "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.",
1198
1212
  "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.",
1199
1213
  "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."
1200
1214
  },
1201
1215
  {
1202
- "action": "wait_for_lead_filter_artifact",
1216
+ "action": "wait_for_lead_filter_rules",
1203
1217
  "requiredArtifacts": [
1204
1218
  "lead-filter.md"
1205
1219
  ],
@@ -1209,7 +1223,7 @@
1209
1223
  "message-prep.md",
1210
1224
  "message-candidate-drafts.md"
1211
1225
  ],
1212
- "rule": "Do not wait for message-validation.md before saving rubrics and moving the watched browser to Filter Leads. Message work may still be running while the user watches fit filtering."
1226
+ "rule": "Do not wait for message-validation.md before saving rubrics and moving the watched browser to Filter Leads. lead-filter.md is a debug artifact name; the durable requirement is production-safe rubric rules derived from the live campaign table review batch. Message work may still be running while the user watches fit filtering."
1213
1227
  },
1214
1228
  {
1215
1229
  "action": "save_filter_rubrics_to_campaign",
@@ -1247,11 +1261,35 @@
1247
1261
  "when": "after_save_rubrics_succeeds_before_waiting_for_message_validation",
1248
1262
  "writesCampaignState": "currentStep:apply-icp-rubric"
1249
1263
  },
1264
+ {
1265
+ "tool": "get_rows_minimal",
1266
+ "requiredValues": {
1267
+ "tableId": "workflowTableId",
1268
+ "limit": "import.importLimit"
1269
+ },
1270
+ "purpose": "read_bounded_review_batch_cell_ids_before_filter_wait",
1271
+ "requiredBeforeCascade": true
1272
+ },
1273
+ {
1274
+ "tool": "queue_cells",
1275
+ "requiredValues": {
1276
+ "tableId": "workflowTableId",
1277
+ "cellSource": "bounded_review_batch_enrichCellIds",
1278
+ "statusFilter": [
1279
+ "pending",
1280
+ "error",
1281
+ "dependency_blocked"
1282
+ ]
1283
+ },
1284
+ "purpose": "queue_bounded_review_batch_enrichment_before_waiting_for_rubric_results",
1285
+ "rule": "Queue only enrichCellId values from get_rows_minimal for the bounded review batch. Do not queue icpCellId values or whole-table cells; the workflow cascade runs ICP scoring after enrichment."
1286
+ },
1250
1287
  {
1251
1288
  "tool": "wait_for_rubric_results",
1252
1289
  "requiredValues": {
1253
1290
  "includeRows": false,
1254
- "minPassedCount": 1
1291
+ "minPassedCount": 1,
1292
+ "targetCount": "bounded review batch count"
1255
1293
  },
1256
1294
  "purpose": "wait_for_bounded_filter_run_before_messages_mode",
1257
1295
  "readVia": "stats_only_tool_result",
@@ -1269,22 +1307,21 @@
1269
1307
  "optional": true
1270
1308
  },
1271
1309
  {
1272
- "action": "wait_for_message_validation_artifact",
1273
- "requiredArtifacts": [
1274
- "message-validation.md"
1310
+ "action": "wait_for_message_draft_recommendation",
1311
+ "requiredOutputs": [
1312
+ "template recommendation",
1313
+ "token fill rules",
1314
+ "rendered sample"
1275
1315
  ],
1276
1316
  "optionalArtifacts": [
1317
+ "message-validation.md",
1277
1318
  "message-prep.md",
1278
1319
  "message-candidate-drafts.md"
1279
1320
  ],
1280
- "reconciliationRule": "Before entering message-review, verify message-validation.md came from the same brief.md, lead-review.md, lead-sample.json, and saved lead-filter.md. lead-filter.md gates the sample rows; lead-sample.json remains the message sample source."
1321
+ "reconciliationRule": "Before entering message-review, verify the recommendation came from the same campaignBrief, selected source state, selectedLeadListId, imported review-batch rows, and saved filters. Saved filters gate the sample rows; debug markdown files are optional only."
1281
1322
  }
1282
1323
  ],
1283
- "requiredArtifacts": [
1284
- "brief.md",
1285
- "lead-review.md",
1286
- "lead-sample.json"
1287
- ],
1324
+ "requiredArtifacts": [],
1288
1325
  "producesArtifacts": [
1289
1326
  "lead-filter.md",
1290
1327
  "message-validation.md"
@@ -1299,6 +1336,8 @@
1299
1336
  "get_subskill_asset",
1300
1337
  "get_post_find_leads_scout_registry",
1301
1338
  "save_rubrics",
1339
+ "get_rows_minimal",
1340
+ "queue_cells",
1302
1341
  "wait_for_rubric_results",
1303
1342
  "update_campaign",
1304
1343
  "get_campaign_navigation_state",
@@ -1309,16 +1348,16 @@
1309
1348
  ],
1310
1349
  "toolRules": [
1311
1350
  "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.",
1312
- "message-generation can start before lead-filter.md, but message-review cannot start until both lead-filter.md and message-validation.md exist and reconcile against the same lead-sample.json.",
1351
+ "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.",
1313
1352
  "Do not let filter-leads create a new message sample. Do not let message-generation fetch new prospects.",
1314
- "Before writing message-validation.md, message-generation must either run in the post-find-leads-message-scout agent that carries the campaign-launch message rules in its prompt or load get_subskill_asset({ subskillName: \"create-campaign-v2\", assetPath: \"references/message-review-safety-gate.md\" }) in the parent-thread fallback. Do not load the full long generate-messages prompt in create-campaign-v2; if the safety gate cannot safely approve the draft, route to revise-messaging with the concrete failure.",
1315
- "Do not queue, enrich, attach sequence, or start until save_rubrics succeeds and the approved message set is synced into campaignBrief."
1353
+ "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.",
1354
+ "Do not queue before saved rubrics. After save_rubrics succeeds, queue only bounded review-batch enrichCellId values from get_rows_minimal before wait_for_rubric_results.",
1355
+ "Do not attach sequence, start, or queue product Generate Message cells until the approved message set is synced into campaignBrief."
1316
1356
  ],
1317
1357
  "doNotAllow": [
1318
1358
  "create_campaign",
1319
1359
  "import_leads",
1320
1360
  "confirm_lead_list",
1321
- "queue_cells",
1322
1361
  "start_campaign",
1323
1362
  "check_rubric",
1324
1363
  "generate_messages",
@@ -1343,6 +1382,8 @@
1343
1382
  "interruptOnly": true,
1344
1383
  "requiredCampaignState": [
1345
1384
  "campaignId",
1385
+ "campaignBrief",
1386
+ "selectedLeadListId",
1346
1387
  "workflowTableId"
1347
1388
  ],
1348
1389
  "debugProducesArtifacts": [
@@ -1363,9 +1404,9 @@
1363
1404
  "tool": "update_campaign",
1364
1405
  "requiredValues": {
1365
1406
  "currentStep": "messages",
1366
- "watchNarration.stage": "review-ready"
1407
+ "watchNarration.stage": "fit-message"
1367
1408
  },
1368
- "watchNarrationRule": "Messages first shows a mode choice: Use Template and AI Generated. Use Template is recommended because it uses the approved campaign brief and review-batch evidence. AI Generated is an explicit opt-out from the background template draft."
1409
+ "watchNarrationRule": "Messages first shows a mode choice, not final review: Use Template and AI Generated. Use Template is recommended because it uses the approved campaign brief and review-batch evidence. AI Generated is an explicit opt-out from the background template draft. Do not show Review fit and message until the user chooses a mode and a reviewable message/template exists."
1369
1410
  },
1370
1411
  {
1371
1412
  "action": "show_messages_mode_choice",
@@ -1430,6 +1471,8 @@
1430
1471
  "interruptOnly": true,
1431
1472
  "requiredCampaignState": [
1432
1473
  "campaignId",
1474
+ "campaignBrief",
1475
+ "selectedLeadListId",
1433
1476
  "workflowTableId"
1434
1477
  ]
1435
1478
  },
@@ -1557,40 +1600,37 @@
1557
1600
  "onEnter": [
1558
1601
  {
1559
1602
  "action": "run_or_reconcile_subskill",
1560
- "target": "message-review-safety-gate",
1603
+ "target": "generate-messages",
1561
1604
  "mode": "DRY MODE",
1562
- "sampleSource": "lead-sample.json from find-leads",
1605
+ "sampleSource": "imported review-batch rows from selectedLeadListId",
1563
1606
  "toolCallRequiredBeforeArtifacts": [
1564
- "get_subskill_asset({ subskillName: \"create-campaign-v2\", assetPath: \"references/message-review-safety-gate.md\" }) unless the post-find-leads-message-scout agent prompt already supplied these rules"
1607
+ "get_subskill_prompt({ subskillName: \"generate-messages\", offset, limit }) until hasMore=false",
1608
+ "optional supplemental check: get_subskill_asset({ subskillName: \"create-campaign-v2\", assetPath: \"references/message-review-safety-gate.md\" })"
1565
1609
  ],
1566
- "skipIfFreshArtifactExists": "message-validation.md",
1567
- "reconcileWith": "lead-filter.md; lead-sample.json remains the sample source"
1610
+ "skipIfFreshStateExists": "fresh messageDraftRecommendation from the same campaign/source/review-batch basis",
1611
+ "reconcileWith": "saved filters and imported review-batch rows; optional lead-filter.md only mirrors those filters"
1568
1612
  },
1569
1613
  {
1570
- "action": "write_artifact",
1571
- "artifact": "message-validation.md"
1614
+ "action": "return_template_recommendation",
1615
+ "optionalArtifact": "message-validation.md"
1572
1616
  }
1573
1617
  ],
1574
- "requiredArtifacts": [
1575
- "brief.md",
1576
- "lead-review.md",
1577
- "lead-sample.json"
1578
- ],
1618
+ "requiredArtifacts": [],
1579
1619
  "producesArtifacts": [
1580
1620
  "message-validation.md"
1581
1621
  ],
1582
1622
  "allowedTools": [
1623
+ "get_subskill_prompt",
1583
1624
  "get_subskill_asset",
1584
1625
  "AskUserQuestion",
1585
1626
  "request_user_input"
1586
1627
  ],
1587
1628
  "toolRules": [
1588
- "Before writing message-validation.md, message-review.md, or a message-review AskUserQuestion, the current run must either execute inside the post-find-leads-message-scout agent that carries the campaign-launch message rules in its prompt or load get_subskill_asset({ subskillName: \"create-campaign-v2\", assetPath: \"references/message-review-safety-gate.md\" }) in the parent-thread fallback.",
1589
- "Lead Sample Basis must cite rows from lead-sample.json produced by find-leads. lead-filter.md only gates those rows; it is not a source for a new sample.",
1590
- "Do not hand-write message-validation.md from message-prep.md, message-candidate-drafts.md, or general campaign knowledge.",
1591
- "message-validation.md must prove the message-review safety-gate workflow ran: Gold Standard Strategy Map, Campaign Element Pool, Current Campaign Translation, Token Fill Rules, Token Adherence Table, Angle Drafts, Kill / Combine Review, Finalizer Pass, Gold-Standard Quality Gate, Skeptical Prospect Review, Winner Gate, and a raw sendable Selected Winner are required before message-review can recommend approve-message.",
1592
- "If the hosted output is plausible but weaker than the safety-gate gold-standard rules, stop at message-review with revise-messaging. Do not continue to approval or mint just because the mechanical flow worked.",
1593
- "Do not load the full generate-messages subskill in create-campaign-v2. If the safety gate is missing a needed campaign-specific rule or the first safety-gate draft fails quality gates, route to revise-messaging with the concrete failure instead of loading the long prompt.",
1629
+ "Before writing message-validation.md, message-review.md, or a message-review AskUserQuestion, load the full get_subskill_prompt({ subskillName: \"generate-messages\" }) prompt and draft from campaignBrief, selected source state, selectedLeadListId, and imported review-batch rows.",
1630
+ "Do not use brief.md, lead-review.md, or lead-sample.json as required live state; those files are optional debug context only.",
1631
+ "Do not hand-write message-validation.md from message-prep.md, message-candidate-drafts.md, stale markdown files, database reads, or general campaign knowledge.",
1632
+ "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.",
1633
+ "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.",
1594
1634
  "The approved message set must be written back into campaignBrief before auto-execute-messaging observes or queues product Generate Message cells. AI Generated mode uses the product's AI-generated path instead of template review."
1595
1635
  ],
1596
1636
  "doNotAllow": [
@@ -1628,6 +1668,8 @@
1628
1668
  "interruptOnly": true,
1629
1669
  "requiredCampaignState": [
1630
1670
  "campaignId",
1671
+ "campaignBrief",
1672
+ "selectedLeadListId",
1631
1673
  "workflowTableId"
1632
1674
  ]
1633
1675
  },
@@ -1638,15 +1680,18 @@
1638
1680
  "resumeOnly": false,
1639
1681
  "onEnter": [
1640
1682
  {
1641
- "action": "read_draft_artifacts",
1642
- "requiredFiles": [
1643
- "brief.md",
1644
- "lead-review.md",
1645
- "lead-sample.json",
1646
- "lead-filter.md",
1647
- "message-validation.md"
1683
+ "action": "read_live_message_recommendation",
1684
+ "requiredState": [
1685
+ "campaignBrief",
1686
+ "selected source state",
1687
+ "selectedLeadListId",
1688
+ "workflowTableId",
1689
+ "saved filters when filters are enabled",
1690
+ "template recommendation with token fill rules and rendered sample"
1648
1691
  ],
1649
- "optionalFiles": [
1692
+ "optionalDebugFiles": [
1693
+ "lead-filter.md",
1694
+ "message-validation.md",
1650
1695
  "message-prep.md",
1651
1696
  "message-candidate-drafts.md"
1652
1697
  ]
@@ -1765,8 +1810,8 @@
1765
1810
  "requires": [
1766
1811
  "campaignId",
1767
1812
  "message-review-decision.md",
1768
- "message-validation.md",
1769
- "lead-filter.md when filters are enabled"
1813
+ "approved template recommendation",
1814
+ "saved filters when filters are enabled"
1770
1815
  ],
1771
1816
  "when": "after_message_approved_before_product_messaging",
1772
1817
  "fields": [
@@ -192,9 +192,9 @@ After review batch import:
192
192
  ```json
193
193
  {
194
194
  "stage": "fit-message",
195
- "headline": "Added to Campaign",
196
- "visibleState": "The first review batch is in the campaign.",
197
- "agentIntent": "Codex is asking you to Choose filters or skip. The Message Draft Builder may prepare a template in the background, but review waits until the filter path is settled.",
195
+ "headline": "Do you want filters?",
196
+ "visibleState": "The first review batch is in the campaign. The list is ready to decide whether extra filtering is worth it.",
197
+ "agentIntent": "Codex is asking whether you want to further filter these leads before message review. The Message Draft Builder may prepare a template in the background, but review waits until the filter path is settled.",
198
198
  "nextAction": "Choose filters or skip",
199
199
  "safety": "Nothing enriches, validates, or sends until the message is ready and approved.",
200
200
  "workerStatuses": {
@@ -207,8 +207,9 @@ After review batch import:
207
207
  Use this after the selected review rows are present and before filters are
208
208
  saved. This is add-filters intent, not active filtering. Tell the user to
209
209
  Choose filters or skip, and do not say filtering the batch before rubrics are
210
- saved. `enableICPFilters` only becomes true after active filter criteria are
211
- saved.
210
+ saved. When the user chooses filters, immediately persist
211
+ `enableICPFilters: true` and move to `create-icp-rubric` so the watched app
212
+ shows Filter Rules while Codex defines the rules in chat.
212
213
 
213
214
  Fit + message:
214
215
 
@@ -51,9 +51,13 @@ Create and save a comprehensive lead scoring rubric that:
51
51
 
52
52
  ## Rubric Validation + Results
53
53
 
54
- - `mcp__sellable__check_rubric` - Queue enrichment + scoring sample (default 7)
54
+ - `mcp__sellable__get_rows_minimal` - Read bounded review-batch cell IDs
55
+ - `mcp__sellable__queue_cells` - Queue only bounded `enrichCellId` values
55
56
  - `mcp__sellable__wait_for_rubric_results` - Poll pass-rate results
56
57
 
58
+ `check_rubric` is legacy/recovery only. Do not use it in normal
59
+ create-campaign-v2 runs.
60
+
57
61
  </tools>
58
62
 
59
63
  <rubric_structure>
@@ -117,7 +121,7 @@ Required fields:
117
121
 
118
122
  - If **Keep & apply**:
119
123
  - Call `update_campaign({ campaignId, enableICPFilters: true, currentStep: "apply-icp-rubric" })`
120
- - Then proceed to Phase 5 (enrich + score a sample).
124
+ - Then proceed to Phase 5 (queue bounded enrichment cells + wait for score results).
121
125
 
122
126
  **If no campaignOfferId:**
123
127
  Ask: "Which campaign should I create the rubric for? You can share the campaign ID or describe the target audience."
@@ -203,7 +207,9 @@ update_campaign({
203
207
 
204
208
  This triggers the campaign sync and moves the user to the filter-leads step where they can see the campaign table with scoring columns.
205
209
 
206
- Then ask the user if we should kick off enrichment for a sample:
210
+ For normal create-campaign-v2 runs, the caller should immediately queue only the
211
+ bounded review-batch enrichment cells and wait for scores. For standalone
212
+ rubric editing, ask the user if we should kick off enrichment for a sample:
207
213
 
208
214
  "ICP filtering enabled — you should see the leads table with scoring columns now. Want me to run enrichment on the first 5 leads to check pass rates?"
209
215
 
@@ -212,9 +218,14 @@ Then ask the user if we should kick off enrichment for a sample:
212
218
 
213
219
  ## Phase 5: Enrich + Score a Sample
214
220
 
215
- 1. Call `check_rubric` with `maxProspects: 5` finds the first 5 rows' enrich column cells and kicks off enrichment + ICP scoring via the batch queue API.
216
- 2. Call `wait_for_rubric_results` with `targetCount: 5` — polls until ICP scores land.
217
- 3. Summarize pass rate (`passed/completed`, percent) and recommend:
221
+ 1. Call `get_rows_minimal({ tableId, limit: 5 })` or use the caller-provided
222
+ bounded review-batch rows.
223
+ 2. Call `queue_cells({ tableId, cellIds })` with only pending/error
224
+ `enrichCellId` values. Do not pass `icpCellId` values or full-table cells;
225
+ the workflow cascade handles downstream ICP scoring after enrichment.
226
+ 3. Call `wait_for_rubric_results` with `targetCount: 5` and `minPassedCount: 1`
227
+ — polls until ICP scores land.
228
+ 4. Summarize pass rate (`passed/completed`, percent) and recommend:
218
229
  - Tighten filters if pass rate is too high (>80% — too loose).
219
230
  - Loosen filters if pass rate is too low (<20% — too strict).
220
231
  - Continue when results look healthy (30-70% is typical).