@sellable/mcp 0.1.142 → 0.1.144

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.
Files changed (30) hide show
  1. package/README.md +5 -3
  2. package/agents/post-find-leads-message-scout.md +47 -3
  3. package/agents/registry.json +3 -7
  4. package/dist/index-dev.js +0 -0
  5. package/dist/index.js +0 -0
  6. package/dist/server.js +59 -29
  7. package/dist/tools/auth.js +5 -5
  8. package/dist/tools/leads.js +107 -8
  9. package/dist/tools/processing.d.ts +1 -0
  10. package/dist/tools/prompts.js +3 -3
  11. package/dist/tools/rubrics.js +14 -9
  12. package/package.json +1 -1
  13. package/skills/create-campaign/SKILL.md +52 -32
  14. package/skills/create-campaign-brief/SKILL.md +4 -0
  15. package/skills/create-campaign-v2/SKILL.md +59 -9
  16. package/skills/create-campaign-v2/SOUL.md +21 -12
  17. package/skills/create-campaign-v2/core/flow.v2.json +54 -18
  18. package/skills/create-campaign-v2/core/policy.md +35 -39
  19. package/skills/create-campaign-v2/references/draft-lifecycle.md +20 -104
  20. package/skills/create-campaign-v2/references/filter-leads.md +199 -525
  21. package/skills/create-campaign-v2/references/final-handoff-contract.md +5 -5
  22. package/skills/create-campaign-v2/references/message-review-safety-gate.md +109 -28
  23. package/skills/create-campaign-v2/references/sample-validation-loop.md +5 -2
  24. package/skills/create-campaign-v2/references/step-15-re-cascade.md +2 -3
  25. package/skills/create-campaign-v2/references/watch-guide-narration.md +37 -22
  26. package/skills/create-campaign-v2/references/watch-link-handoff.md +1 -1
  27. package/skills/create-campaign-v2-tail/SKILL.md +26 -13
  28. package/skills/research/config.json +9 -0
  29. package/dist/tools/registry.d.ts +0 -4186
  30. package/dist/tools/registry.js +0 -59
@@ -33,8 +33,8 @@ order (all before waiting for the user):
33
33
  `get_campaign_navigation_state` when available so the watch link visibly
34
34
  lands on Settings before sender setup.
35
35
  4. Explain sender setup and Slack in normal customer language: Sellable needs a
36
- connected LinkedIn sender before anything can send, and Slack reply review
37
- matters because replies and approvals need a channel the team will monitor.
36
+ connected LinkedIn sender for launch, and Slack reply review matters because
37
+ replies and approvals need a channel the team will monitor.
38
38
  5. If no connected sender exists, surface the campaign Settings link
39
39
  `/campaign-builder/{campaignId}/settings?mode=claude`, tell the user to
40
40
  connect a sender there, and STOP. Do not attach sequence and do not start.
@@ -70,7 +70,7 @@ The final handoff must answer five customer questions in plain language:
70
70
  - why the selected LinkedIn sender and Slack reply review matter before launch
71
71
  - what clicking Start does (approves/starts the campaign send path)
72
72
  - how to revise (reply with what to change, or use the campaign UI)
73
- - whether anything has started yet (nothing sends until the user starts)
73
+ - current launch status and what the final Start action does
74
74
 
75
75
  When `lead-source-intake.json` exists, also answer:
76
76
 
@@ -84,8 +84,8 @@ When `lead-source-intake.json` exists, also answer:
84
84
 
85
85
  Use the exact state name `awaiting-user-greenlight` so logs and UX artifacts
86
86
  can verify the state-machine stop point, but explain it in normal words: the
87
- campaign is built, sender/sequence setup is being completed, and nothing sends
88
- until the user's final start decision. The watched UI may advance through
87
+ campaign is built, sender/sequence setup is being completed, and the final
88
+ start decision is still ahead. The watched UI may advance through
89
89
  Settings, Sequence, and Send while this state-machine step remains the final
90
90
  handoff.
91
91
 
@@ -17,20 +17,26 @@ instead of pulling the long prompt into the main thread.
17
17
 
18
18
  ## Required Workflow
19
19
 
20
- 1. Read `brief.md`, `lead-review.md`, `lead-sample.json`, and `lead-filter.md`
21
- when present.
22
- 2. Pick 2-3 sample rows from `lead-sample.json`, preferring rows that pass the
23
- filter or are likely to pass. Do not invent new rows.
24
- 3. Build the message from the approved brief and the sampled rows. Use only
25
- proof that appears in the brief, source review, sample, or explicit user
26
- answers. Unsupported reply-rate, meeting-rate, ROI, revenue, and customer-logo
27
- claims are blocked.
28
- 4. Produce `message-validation.md` with the required sections below.
29
- 5. Produce `message-review.md` as the customer-facing checkpoint.
20
+ 1. Read the live campaign basis: campaign id, campaign revision or
21
+ `campaignUpdatedAt`, campaign brief content, selected source decision,
22
+ selected lead list/source state, `workflowTableId`, imported review-batch
23
+ row ids/hash, and any saved rubric/filter summary supplied by the parent.
24
+ 2. Pick 2-3 sample rows from the imported review-batch rows, preferring rows
25
+ that pass the saved rubrics or are likely to pass. Do not invent new rows.
26
+ 3. Build the message from the approved campaign brief, selected source context,
27
+ sampled rows, and explicit user answers. Use only proof that appears in that
28
+ live basis. Unsupported reply-rate, meeting-rate, ROI, revenue, and
29
+ customer-logo claims are blocked.
30
+ 4. Return a compact message recommendation to the parent thread. Do not write
31
+ local markdown/json artifacts in normal customer runs; emit debug artifacts
32
+ only when the parent explicitly asks for debug/UAT output.
33
+ 5. Render the customer-facing message review in chat before asking for approval.
34
+ After approval, the parent persists template and token rules into campaign
35
+ state with `update_campaign_brief`.
30
36
  6. Ask exactly `approve-message` or `revise-messaging`. Do not import, queue,
31
37
  attach sequence, or start before `approve-message`.
32
38
 
33
- ## Required `message-validation.md` Sections
39
+ ## Required Message Recommendation Sections
34
40
 
35
41
  - `Status`
36
42
  - `Mode`
@@ -51,8 +57,8 @@ instead of pulling the long prompt into the main thread.
51
57
  - `Findings`
52
58
  - `Recommendation`
53
59
 
54
- Keep this artifact concise. It should prove the reasoning path, not reproduce
55
- the full framework.
60
+ Keep this recommendation concise. It should prove the reasoning path, not
61
+ reproduce the full framework.
56
62
 
57
63
  ## Message Quality Gates
58
64
 
@@ -83,21 +89,96 @@ but the [topic] thread felt close enough to send`. Otherwise omit the
83
89
  - If the message is plausible but not ready to send, set
84
90
  `Recommendation: revise-messaging`.
85
91
 
86
- ## Customer-Facing `message-review.md`
87
-
88
- Render this in chat before asking:
89
-
90
- - `Status: message-review`
91
- - `Subject:`
92
- - `Message:` with the tokenized template or approved AI-native message
93
- - `Rendered examples:`
94
- - `Good token fill:`
95
- - `Good omit:`
96
- - `Token notes:`
97
- - `My take:`
98
- - `Suggested adjustment:`
99
- - `Question: approve-message or revise-messaging?`
100
- - `Recommendation: approve-message` or `Recommendation: revise-messaging`
92
+ ## Customer-Facing Message Review
93
+
94
+ Render this in chat before asking. Use Markdown structure so the approval target
95
+ is visually scannable:
96
+
97
+ ````markdown
98
+ Status: message-review
99
+
100
+ ## Message Template
101
+
102
+ **Subject**
103
+
104
+ ```text
105
+ {{tokenized_subject}}
106
+ ```
107
+
108
+ **Body**
109
+
110
+ ```text
111
+ {{tokenized_message_body}}
112
+ ```
113
+
114
+ ## Rendered Examples
115
+
116
+ ### Good token fill
117
+
118
+ Use when the row has a clean, supported fill.
119
+
120
+ ```text
121
+ Subject: ...
122
+
123
+ Hey First,
124
+
125
+ ...
126
+ ```
127
+
128
+ ### Good omit / fallback
129
+
130
+ Use when a row signal or company context is missing, weak, or awkward.
131
+
132
+ ```text
133
+ Subject: ...
134
+
135
+ Hey First,
136
+
137
+ ...
138
+ ```
139
+
140
+ ### Bad fill to avoid
141
+
142
+ ```text
143
+ Subject: ...
144
+
145
+ Hey First,
146
+
147
+ ...
148
+ ```
149
+
150
+ **Why this is wrong:** one sentence naming the exact token-fill failure.
151
+
152
+ ## Token Notes
153
+
154
+ | Token | Use when | Fallback |
155
+ | ---------------- | ------------------------ | -------- |
156
+ | `{{first_name}}` | Clean first name exists. | `there` |
157
+
158
+ ## Recommendation
159
+
160
+ **My take:** ...
161
+
162
+ **Suggested adjustment:** ...
163
+
164
+ **Question:** approve-message or revise-messaging?
165
+
166
+ **Recommendation:** approve-message
167
+ ````
168
+
169
+ Formatting requirements:
170
+
171
+ - Put the tokenized template and every rendered example in fenced `text` blocks
172
+ so chat gives them a distinct background.
173
+ - `Good token fill` and `Good omit / fallback` must each contain a complete
174
+ rendered subject + body, not a bullet list of token names or a single
175
+ bridge-line fragment.
176
+ - Include `Bad fill to avoid` when the row sample has a tempting but unsafe
177
+ source-mechanics or raw-headline fill; otherwise include `Bad fill to avoid:
178
+ none found`.
179
+ - Use a token notes table, not paragraph-only token notes.
180
+ - Keep reasoning outside the code blocks so the blocks are easy to inspect and
181
+ approve.
101
182
 
102
183
  `My take` and `Suggested adjustment` must be specific. The adjustment can be
103
184
  "approve as-is, or revise once to X if you want Y"; it must not be empty.
@@ -20,7 +20,9 @@ auto-revise leads.
20
20
 
21
21
  ## Inputs
22
22
 
23
- - `CampaignOffer.currentStep === "validate-sample"` (set at end of Step 13)
23
+ - `CampaignOffer.currentStep === "apply-icp-rubric"` (the watched app is
24
+ already on Filter Leads after `save_rubrics`; `validate-sample` is the
25
+ logical loop name, not a visible route)
24
26
  - Imported review batch from Step 13 (size: `importLimit`, default 15)
25
27
  - Config from `auto-execute.yaml`: `sample.sampleSize`,
26
28
  `sample.minProjectedPass`, `sample.maxRevisionRounds`
@@ -48,7 +50,8 @@ auto-revise leads.
48
50
  the enrichment batch ids; NOT by calling wait_for_rubric_results
49
51
  before rubric was scheduled)
50
52
 
51
- 5. check_rubric(sample)
53
+ 5. do not call `check_rubric`; the product cascade runs enrichment and rubric
54
+ scoring from the queued Enrich Prospect cells
52
55
 
53
56
  6. wait_for_rubric_results(sample, targetCount = <cohortSize>, minPassedCount = 1)
54
57
  - cohortSize = stats.totalRows of the enrichment batch, or the
@@ -2,8 +2,7 @@
2
2
 
3
3
  This reference governs Step 15 (`auto-execute-messaging`) re-cascade
4
4
  behavior when Step 14's validate-sample loop graduates additional rows
5
- from pending → passed after the first `generate_messages` call has
6
- already run.
5
+ from pending → passed after the first Generate Message cells have already run.
7
6
 
8
7
  Load whenever Step 15 is about to transition to
9
8
  `awaiting-user-greenlight`, and on every resume into Step 15.
@@ -25,7 +24,7 @@ Re-cascade runs whenever ALL of the following are true:
25
24
 
26
25
  1. Step 15 has already observed the initial review-batch message cascade.
27
26
  2. A subsequent check of rubric state shows rows that were pending at
28
- first generate_messages time are now passed.
27
+ first message-generation pass are now passed.
29
28
  3. Those newly-passed rows do NOT yet have generated messages
30
29
  (`messagesCount` flat relative to pre-cascade).
31
30
  4. Step 15 has NOT yet transitioned to `awaiting-user-greenlight`.
@@ -14,14 +14,14 @@ Building campaign with {Claude Code or Codex} [Exit watch mode]
14
14
  {visibleState} {agentIntent}
15
15
 
16
16
  Next: {nextAction}
17
- {safety?}
18
17
  ```
19
18
 
20
19
  UI owns the header, driver label, button, progress bar, step count, and static
21
20
  stage labels. The driver label comes from the watch URL mode (`mode=claude` or
22
21
  `mode=codex`), so do not add separate guide args for it. You own `stage`,
23
22
  `headline`, `visibleState`, `agentIntent`, and any exceptional `nextAction`,
24
- `safety`, `workerStatuses`, or `blockedReason`.
23
+ `workerStatuses`, `blockedReason`, or concise `safety` note for blocked/recovery
24
+ states.
25
25
 
26
26
  ## Copy Rules
27
27
 
@@ -40,6 +40,11 @@ stage labels. The driver label comes from the watch URL mode (`mode=claude` or
40
40
  sample you are checking, and why that helps this campaign.
41
41
  - At filter choice, do not say filtering the batch before rubrics and message approval are saved.
42
42
  - Avoid internal terms: MCP, tool, currentStep, workflow table, scout, debug.
43
+ - Treat `safety` as optional internal context, not a visible disclaimer.
44
+ - Do not repeat long negative lists like "no leads import, no enrichment, no
45
+ messages, no sequence, no sending" in routine watch copy. Prefer one concise
46
+ gate phrase only when it matters: "Approval covers scouting/search only" or
47
+ "Next gate: selected-post scrape."
43
48
  - Do not invent time estimates.
44
49
 
45
50
  ## Stages
@@ -86,8 +91,7 @@ Default source funnel:
86
91
  "headline": "Choosing the lead source",
87
92
  "visibleState": "The browser is on Pick Provider while Codex explains the source path before opening a provider lane.",
88
93
  "agentIntent": "Codex will start with people engaging with relevant LinkedIn posts when that looks plausible, then switch to Sales Nav to find ICP people actively posting on LinkedIn, then search by titles, and use Prospeo for a broader account/contact path. Sales Nav with recent activity remains the first LinkedIn fallback.",
89
- "nextAction": "Review source",
90
- "safety": "No leads import until you approve the source."
94
+ "nextAction": "Review source"
91
95
  }
92
96
  ```
93
97
 
@@ -99,8 +103,7 @@ Signal Discovery viability handoff:
99
103
  "headline": "Checking Signal Discovery viability",
100
104
  "visibleState": "The browser is moving from Pick Provider into Signal Discovery so the exact posts and sample math are visible.",
101
105
  "agentIntent": "Codex is testing whether relevant posts have enough ICP-looking engagers before recommending this as the source.",
102
- "nextAction": "Review source math",
103
- "safety": "No leads import until you approve the source."
106
+ "nextAction": "Review source math"
104
107
  }
105
108
  ```
106
109
 
@@ -112,8 +115,7 @@ Source direction override:
112
115
  "headline": "Testing the requested source",
113
116
  "visibleState": "The campaign asked for hiring and account signals, so Codex is starting with Prospeo instead of the default LinkedIn engagement path.",
114
117
  "agentIntent": "It is checking whether this source has enough reachable ICP-looking people before importing a review batch.",
115
- "nextAction": "Review source",
116
- "safety": "No leads import until you approve the source."
118
+ "nextAction": "Review source"
117
119
  }
118
120
  ```
119
121
 
@@ -125,8 +127,7 @@ Source fallback:
125
127
  "headline": "Switching source lanes",
126
128
  "visibleState": "The Signal Discovery sample did not show enough ICP-looking engagers for a confident first batch.",
127
129
  "agentIntent": "Codex is trying Sales Nav with recent activity so the source can still preserve some buyer activity context.",
128
- "nextAction": "Review source",
129
- "safety": "No leads import until you approve the source."
130
+ "nextAction": "Review source"
130
131
  }
131
132
  ```
132
133
 
@@ -149,9 +150,8 @@ Source recommendation ready:
149
150
  "stage": "find-leads",
150
151
  "headline": "Review the source in Codex",
151
152
  "visibleState": "The browser is showing the evaluated Signal Discovery source with counts and sample quality.",
152
- "agentIntent": "Approve LinkedIn Engagement or ask for a source change in chat before any leads import.",
153
- "nextAction": "Approve in Codex",
154
- "safety": "No leads import until you approve the source."
153
+ "agentIntent": "Approve LinkedIn Engagement or ask for a source change in chat.",
154
+ "nextAction": "Approve in Codex"
155
155
  }
156
156
  ```
157
157
 
@@ -180,8 +180,7 @@ Source approved and import starting:
180
180
  "headline": "Importing the review batch",
181
181
  "visibleState": "The browser is still showing the approved LinkedIn Engagement source leads.",
182
182
  "agentIntent": "Codex is importing only the bounded 15-row review batch into the campaign now.",
183
- "nextAction": "Review batch ready",
184
- "safety": "This is still a review step; nothing launches or sends."
183
+ "nextAction": "Review batch ready"
185
184
  }
186
185
  ```
187
186
 
@@ -199,10 +198,9 @@ After review batch import:
199
198
  "visibleState": "The first review batch is in the campaign. The visible sample looks mixed enough that filters should be added before message review.",
200
199
  "agentIntent": "Codex is asking whether you want to further filter these leads before message review. Skip filters only if the visible rows already look clean.",
201
200
  "nextAction": "Choose filters or skip",
202
- "safety": "Nothing enriches, validates, or sends until the message is ready and approved.",
203
201
  "workerStatuses": {
204
202
  "leadFitBuilder": "idle",
205
- "messageDraftBuilder": "idle"
203
+ "messageDraftBuilder": "running"
206
204
  }
207
205
  }
208
206
  ```
@@ -211,7 +209,8 @@ Use this after the selected review rows are present and before filters are
211
209
  saved. This is add-filters intent, not active filtering. Recommend adding
212
210
  filters when the sample is mixed/noisy, tell the user to Choose filters or skip,
213
211
  and do not say filtering the batch before rubrics and message approval are
214
- saved. Only show worker statuses as running if those branches actually started.
212
+ saved. The Message Draft Builder should start immediately after the bounded
213
+ review batch exists; mark it running only when that branch actually started.
215
214
  When the user chooses filters, immediately persist `enableICPFilters: true` and
216
215
  move to `create-icp-rubric` so the watched app shows Filter Rules while Codex
217
216
  defines the rules in chat. After `save_rubrics`, move to `apply-icp-rubric`
@@ -242,11 +241,28 @@ Review ready:
242
241
  "headline": "Review the message template",
243
242
  "visibleState": "The browser remains on Filter Leads while the message template is reviewed in chat.",
244
243
  "agentIntent": "Codex is waiting for approval before saving the template to the campaign brief and queueing enrichment/filtering.",
245
- "nextAction": "Approve or revise the template",
246
- "safety": "Codex will only continue after you approve."
244
+ "nextAction": "Approve or revise the template"
247
245
  }
248
246
  ```
249
247
 
248
+ Template approved, bounded filter test running:
249
+
250
+ ```json
251
+ {
252
+ "stage": "fit-message",
253
+ "headline": "Template saved",
254
+ "visibleState": "The browser stays on Filter Leads while the bounded enrichment and filter test runs.",
255
+ "agentIntent": "Codex saved the approved message template, queued the review-batch Enrich Prospect cells, and is waiting for at least one row to pass before moving to Messages.",
256
+ "nextAction": "Move to Messages after a passing row"
257
+ }
258
+ ```
259
+
260
+ Do not move to Messages immediately after `approve-message`. The visible route
261
+ is already Filter Leads after `save_rubrics`; approving the message only unlocks
262
+ the bounded cascade from that screen. Move to Messages only once at least one
263
+ review-batch row passes and Generate Message cells are ready/running for the
264
+ passing rows.
265
+
250
266
  Messages waiting for template:
251
267
 
252
268
  ```json
@@ -255,8 +271,7 @@ Messages waiting for template:
255
271
  "headline": "Waiting for the template",
256
272
  "visibleState": "The fit rules are saved and the message template is still being prepared.",
257
273
  "agentIntent": "Codex is waiting until the template can be reviewed.",
258
- "nextAction": "Review template",
259
- "safety": "No enrichment, filtering, or Generate Message cells run until the template is approved."
274
+ "nextAction": "Review template"
260
275
  }
261
276
  ```
262
277
 
@@ -36,7 +36,7 @@ Example skeleton:
36
36
  I created the campaign shell with the brief already in it.
37
37
  You can watch the lead source, filters, and messages fill in from here.
38
38
 
39
- No leads import and nothing sends yet.
39
+ I’ll pause at the next approval gate before sourcing.
40
40
 
41
41
  Watch link: [Open campaign]({watchUrl})
42
42
 
@@ -36,10 +36,13 @@ Step 13 — import/confirm review batch only
36
36
  update_campaign(currentStep=filter-choice)
37
37
 
38
38
  Post-import main thread
39
- launch Lead Fit Builder + Message Draft Builder after workflowTableId exists
39
+ launch Message Draft Builder immediately after workflowTableId + review rows exist
40
+ launch Lead Fit Builder only after user chooses filters
40
41
  save_rubrics({ campaignOfferId, leadScoringRubrics }) after the campaign table exists
41
- show readable filters + one concrete sample message
42
- after approval, update_campaign_brief writes `## Approved Message Template` with `{{...}}` tokens
42
+ keep the watched app on Filter Leads after rubrics are saved
43
+ while on Filter Leads, show the message template recommendation from the background Message Draft Builder
44
+ after approve-message, update_campaign_brief writes `## Approved Message Template` with `{{...}}` tokens
45
+ only then start the bounded review-batch cascade
43
46
 
44
47
  Step 14 — kick bounded cascade + observe sample
45
48
  queue_cells(cellIds=<review-batch Enrich Prospect cells only>) <-- starts bounded chain
@@ -58,7 +61,9 @@ Step 15 — observe messaging
58
61
  get_rows_minimal # confirm passing rows have completed Generate Message cells
59
62
  (rare) queue_cells on any pending Generate Message cells
60
63
  token-contract spot check via get_rows
61
- update_campaign(currentStep=awaiting-user-greenlight)
64
+ update_campaign(currentStep=auto-execute-messaging) with review-ready narration
65
+ ask the user to approve generated review-batch messages before Settings
66
+ only after approval: update_campaign(currentStep=awaiting-user-greenlight)
62
67
  (generate_messages is NOT an MCP tool; messages come from the cascade)
63
68
 
64
69
  Step 16 — awaiting-user-greenlight
@@ -144,7 +149,9 @@ what changed and orient the user to what the watch link will show next —
144
149
  reuse the v1 `create-campaign` watch-mode pattern verbatim.
145
150
 
146
151
  Resume currentStep names covered by this tail: `"auto-execute-leads"`,
147
- `"validate-sample"`, `"auto-execute-messaging"`,
152
+ `"apply-icp-rubric"` (the visible Filter Leads home for the logical
153
+ `validate-sample` loop), `"validate-sample"` (legacy resumes only),
154
+ `"auto-execute-messaging"`,
148
155
  `"awaiting-user-greenlight"`, `"settings"`, `"sequence"`, `"send"`, and
149
156
  `"running"`. New mint-early runs normally
150
157
  enter Step 13 from `"confirm-lead-list"`; the `"auto-execute-leads"` string is
@@ -273,10 +280,12 @@ caller or start the workflow-table cascade too early. The cascade starts in
273
280
  Step 14 only after `save_rubrics` and `update_campaign_brief` have both
274
281
  succeeded.
275
282
 
276
- ## Step 14: validate-sample (loop)
283
+ ## Step 14: validate-sample (logical loop on Filter Leads)
277
284
 
278
- Entered on `CampaignOffer.currentStep === "validate-sample"`. Full
279
- decision tree lives in `references/sample-validation-loop.md`.
285
+ Entered after the message template is approved while the watched campaign is
286
+ already on `CampaignOffer.currentStep === "apply-icp-rubric"` / Filter Leads.
287
+ Do not route to a visible `validate-sample` step. Full decision tree lives in
288
+ `references/sample-validation-loop.md`.
280
289
 
281
290
  **Step 14 starts the bounded cascade, then observes it.** Step 13 imported the
282
291
  review batch only. After `save_rubrics` and the approved message template are
@@ -381,8 +390,9 @@ Template`. If it does not, fail before the cascade runs. Do not repair
381
390
  5. If the sample fails the token contract, diagnose brief-vs-list
382
391
  (same revision loop as Step 14) and escalate if over
383
392
  `maxRevisionRounds`.
384
- 6. On success, `update_campaign({ campaignId, currentStep:
385
- "awaiting-user-greenlight" })`.
393
+ 6. On success, keep `currentStep: "auto-execute-messaging"` and ask the user
394
+ to approve the generated review-batch messages. Only that approval may move
395
+ the campaign to Settings / `awaiting-user-greenlight`.
386
396
 
387
397
  **Do NOT hand-write message bodies via `update_cell`.** `update_cell`
388
398
  is reachable for legitimate operator overrides AFTER the tail hands
@@ -428,8 +438,11 @@ strings, which is why Step 16 requires Step 15 to be complete.
428
438
  6. If the sample fails the token contract or critique, diagnose +
429
439
  loop the same way Step 14 does (brief-vs-list), subject to the same
430
440
  `maxRevisionRounds` cap.
431
- 7. On success, `update_campaign({ campaignId, currentStep:
432
- "awaiting-user-greenlight" })` and orient the user.
441
+ 7. On success, keep `currentStep: "auto-execute-messaging"`, show that the
442
+ review-batch messages are ready in Messages, and ask for approval before
443
+ Settings. Only after the user approves those generated messages should
444
+ `update_campaign({ campaignId, currentStep: "awaiting-user-greenlight" })`
445
+ run.
433
446
 
434
447
  Critique failure modes NEVER escalate. A critic timeout, a total
435
448
  timeout, a budget trip, a fake-proof rejection, or an unsupported-
@@ -451,7 +464,7 @@ Shape:
451
464
  `get_campaign_navigation_state` when available to confirm the watch link is
452
465
  visibly on Settings.
453
466
  4. Explain why the sender matters: Sellable needs a connected LinkedIn sender
454
- before anything can send. Explain Slack reply review before launch: replies
467
+ for launch. Explain Slack reply review before launch: replies
455
468
  and approvals need a place the team will actually monitor, so Slack should be
456
469
  connected or intentionally skipped before launch.
457
470
  5. If no connected sender exists, surface a direct Settings link:
@@ -0,0 +1,9 @@
1
+ {
2
+ "parallelMode": "wide",
3
+ "agentCount": 6,
4
+ "maxToolCallsPerAgent": 2,
5
+ "senderMaxAgents": 2,
6
+ "senderMaxToolCallsPerAgent": 3,
7
+ "progressMode": true,
8
+ "debugMode": true
9
+ }