@sellable/mcp 0.1.170 → 0.1.172
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.
- package/dist/tools/auth.js +2 -2
- package/dist/tools/bootstrap.js +1 -1
- package/dist/tools/leads.js +26 -27
- package/package.json +1 -1
- package/skills/create-campaign/SKILL.md +62 -65
- package/skills/create-campaign-v2/SKILL.md +56 -49
- package/skills/create-campaign-v2/SOUL.md +20 -15
- package/skills/create-campaign-v2/core/flow.v2.json +1 -1
- package/skills/create-campaign-v2/references/watch-link-handoff.md +1 -1
package/dist/tools/auth.js
CHANGED
|
@@ -138,8 +138,8 @@ export async function getAuthStatus() {
|
|
|
138
138
|
"5) Call `mcp__sellable__wait_for_cli_login({ sessionId })` using the sessionId returned by start_cli_login. " +
|
|
139
139
|
"6) If the result is `error.type === 'tool_timeout_guard'`, IMMEDIATELY re-call wait_for_cli_login with the SAME sessionId — do not narrate, do not call start_cli_login again. Loop until you get a different result. " +
|
|
140
140
|
"7) On `ok: true`, the user is signed in and `~/.sellable/config.json` has been written. Branch on `isReturningUser`: " +
|
|
141
|
-
"if true, say `You're in
|
|
142
|
-
"if false, say `You're set up
|
|
141
|
+
"if true, say `You're in.\\n\\nWhat is your LinkedIn profile URL?`; " +
|
|
142
|
+
"if false, say `You're set up.\\n\\nWhat is your LinkedIn profile URL?`";
|
|
143
143
|
if (error instanceof SellableApiError && error.isAuthError) {
|
|
144
144
|
return {
|
|
145
145
|
...base,
|
package/dist/tools/bootstrap.js
CHANGED
|
@@ -270,7 +270,7 @@ export async function bootstrapCreateCampaign(input = {}) {
|
|
|
270
270
|
? resumeDetected
|
|
271
271
|
? `Bootstrap complete.${workspaceNotice} Resume from campaign state and navigation diagnostics first; treat local draft artifacts as debug-only evidence. Then load ${createCampaignSubskill?.name ?? "create-campaign"} instructions with get_subskill_prompt({ subskillName: "${createCampaignSubskill?.name ?? "create-campaign"}" }); if the response has hasMore=true, continue with nextOffset until hasMore=false.`
|
|
272
272
|
: flowVersion === "v2"
|
|
273
|
-
? `Bootstrap complete.${workspaceNotice} Load the compact create-campaign-v2 entry prompt once with get_subskill_prompt({ subskillName: "create-campaign-v2" }); load flow/reference assets lazily only when that stage needs them. Preserve the pre-intake sequence: confirm auth/workspace status, ask for the LinkedIn profile URL
|
|
273
|
+
? `Bootstrap complete.${workspaceNotice} Load the compact create-campaign-v2 entry prompt once with get_subskill_prompt({ subskillName: "create-campaign-v2" }); load flow/reference assets lazily only when that stage needs them. Preserve the pre-intake sequence: confirm auth/workspace status, ask only for the LinkedIn profile URL, require that URL before continuing, run lightweight profile/company lookup, then ask the target, offer, credibility, and prospect-source setup questions. Do not call list_senders or sender discovery during setup; sender availability belongs only to Settings after message approval. Then write the campaign brief, call create_campaign once to mint the watchable shell, surface the returned watch link, and hand off to lead finding.`
|
|
274
274
|
: `Bootstrap complete.${workspaceNotice} Load ${createCampaignSubskill?.name ?? "create-campaign"} instructions with get_subskill_prompt({ subskillName: "${createCampaignSubskill?.name ?? "create-campaign"}" }); if the response has hasMore=true, continue with nextOffset until hasMore=false. Follow that flow before calling create_campaign.`
|
|
275
275
|
: "Bootstrap incomplete. Resolve blockingErrors and rerun bootstrap_create_campaign before provider/search/import tools.";
|
|
276
276
|
// Strip prompt body from createCampaignSubskill — it's loaded via the host
|
package/dist/tools/leads.js
CHANGED
|
@@ -529,7 +529,7 @@ function buildSourceImportWatchNarration({ provider, selectedPostCount, estimate
|
|
|
529
529
|
: "Apollo";
|
|
530
530
|
const sourceDetail = provider === "signal-discovery"
|
|
531
531
|
? `${selectedPostCount ?? "selected"} approved post${selectedPostCount === 1 ? "" : "s"}${typeof estimatedEngagers === "number"
|
|
532
|
-
? ` with about ${estimatedEngagers.toLocaleString("en-US")}
|
|
532
|
+
? ` with about ${estimatedEngagers.toLocaleString("en-US")} people to check`
|
|
533
533
|
: ""}`
|
|
534
534
|
: `the approved ${providerLabel} source`;
|
|
535
535
|
const targetDetail = typeof targetLeadCount === "number"
|
|
@@ -727,7 +727,7 @@ function buildSignalDiscoverySearchWatchNarration() {
|
|
|
727
727
|
stage: "find-leads",
|
|
728
728
|
headline: "Searching LinkedIn posts",
|
|
729
729
|
visibleState: "The browser is showing the LinkedIn post search step while approved themes run.",
|
|
730
|
-
agentIntent: "Codex is searching the approved post themes and will ask before
|
|
730
|
+
agentIntent: "Codex is searching the approved post themes and will ask before checking people from selected posts.",
|
|
731
731
|
nextAction: "Review selected posts before scraping",
|
|
732
732
|
safety: "Scrape approval is the next gate.",
|
|
733
733
|
};
|
|
@@ -803,39 +803,38 @@ function buildSignalDiscoverySourceRecommendation({ selectedPosts, targetEngager
|
|
|
803
803
|
: defaultFitRate;
|
|
804
804
|
const estimatedGoodFit = scrapePlan.estimatedEngagers * fitRateForEstimate;
|
|
805
805
|
const fitRateLabel = targetEngagerCount && effectiveTargetEngagerCount > 0
|
|
806
|
-
? "approved
|
|
807
|
-
:
|
|
806
|
+
? "the approved buyer-search target"
|
|
807
|
+
: `the ${Math.round(defaultFitRate * 100)}% starting estimate`;
|
|
808
808
|
const selectedPoolCopy = recommendedCount < selectedCount
|
|
809
|
-
? `**
|
|
810
|
-
: `**
|
|
809
|
+
? `**Posts considered:** ${selectedCount.toLocaleString("en-US")} selected posts with ${formatApproxInteger(totalVisibleEngagement)} public reactions/comments<br>\n**Recommended first set:** ${recommendedCount.toLocaleString("en-US")} post${recommendedCount === 1 ? "" : "s"} with ${formatApproxInteger(recommendedVisibleEngagement)} public reactions/comments and up to ${formatApproxInteger(scrapePlan.estimatedEngagers)} people to check<br>`
|
|
810
|
+
: `**Public activity in the selected posts:** ${formatApproxInteger(totalVisibleEngagement)} reactions/comments<br>\n**People we can check from this set:** up to ${formatApproxInteger(scrapePlan.estimatedEngagers)}<br>`;
|
|
811
811
|
const message = `## Source Recommendation
|
|
812
812
|
|
|
813
|
-
|
|
813
|
+
Start with LinkedIn posts.
|
|
814
814
|
|
|
815
|
-
|
|
816
|
-
**Working assumption:** ~${Math.round(defaultFitRate * 100)}% of raw post engagers pass headline filtering unless a real sample supports a different rate<br>
|
|
817
|
-
**Engagers needed:** ~${effectiveTargetEngagerCount.toLocaleString("en-US")} raw engagers<br>
|
|
818
|
-
**Planning floor:** continue with LinkedIn engagement only when sampled/projected headline-fit rate is at least ${Math.round(minPlanningFitRate * 100)}%; below that, switch to Sales Nav recent activity<br>
|
|
819
|
-
**Review checkpoint:** copy the confirmed source list into the campaign, then process the first ${reviewBatchSize.toLocaleString("en-US")} leads for fit and message review before scaling
|
|
815
|
+
I found ${recommendedCount.toLocaleString("en-US")} recommended LinkedIn post${recommendedCount === 1 ? "" : "s"} to check first. People there are already reacting or commenting around this problem.
|
|
820
816
|
|
|
821
|
-
|
|
817
|
+
**Goal:** find about ${targetGoodFitLeads.toLocaleString("en-US")} likely prospects<br>
|
|
818
|
+
**People to check:** about ${effectiveTargetEngagerCount.toLocaleString("en-US")} people who reacted or commented<br>
|
|
819
|
+
**Good sign:** at least ${Math.round(minPlanningFitRate * 100)}% of the first sample should look like real prospects; below that, switch to active LinkedIn profiles with the right titles<br>
|
|
820
|
+
**First review:** after the list is built, review the first ${reviewBatchSize.toLocaleString("en-US")} leads before we scale
|
|
822
821
|
|
|
823
|
-
|
|
822
|
+
### Recommended posts
|
|
823
|
+
|
|
824
|
+
| Post | Why this post | Public activity | People we can check |
|
|
824
825
|
|---|---|---:|---:|
|
|
825
|
-
${tableRows || "| Recommended posts | Campaign-matched public
|
|
826
|
+
${tableRows || "| Recommended posts | Campaign-matched public activity | - | - |"}
|
|
826
827
|
|
|
827
828
|
${selectedPoolCopy}
|
|
828
|
-
**
|
|
829
|
+
**Likely prospects from ${fitRateLabel}:** ${formatApproxInteger(estimatedGoodFit)} before cleanup
|
|
829
830
|
|
|
830
|
-
###
|
|
831
|
+
### Next step
|
|
831
832
|
|
|
832
833
|
Approve scraping these ${recommendedCount} recommended post${recommendedCount === 1 ? "" : "s"}.
|
|
833
834
|
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
**First pass:** build the source list, copy it into the campaign, then use the first ${reviewBatchSize.toLocaleString("en-US")} campaign rows as the internal setup slice for filters and messages before scaling.
|
|
835
|
+
After approval, I'll build the source list, add it to the campaign, and review the first ${reviewBatchSize.toLocaleString("en-US")} leads before we scale.
|
|
837
836
|
|
|
838
|
-
**Fallback:** if the
|
|
837
|
+
**Fallback:** if the first sample is too noisy or has too few prospects, switch to active LinkedIn profiles with the right titles.
|
|
839
838
|
|
|
840
839
|
Approval card should say:
|
|
841
840
|
|
|
@@ -1431,7 +1430,7 @@ export const leadToolDefinitions = [
|
|
|
1431
1430
|
},
|
|
1432
1431
|
targetEngagerCount: {
|
|
1433
1432
|
type: "number",
|
|
1434
|
-
description: "Signal Discovery: target number of
|
|
1433
|
+
description: "Signal Discovery: target number of people from selected posts to check. Default planning target is about 300 likely prospects at a 20% starting pass assumption, or about 1500 people to check. Use real sample math when available: target likely prospects divided by sampled pass rate. If the sampled/projected fit rate is below the 10% planning floor, switch to the next provider instead of scaling noisy post reactions. Limits selected posts before starting scrape, with a backend hard cap of 10 posts.",
|
|
1435
1434
|
},
|
|
1436
1435
|
maxPostsToScrape: {
|
|
1437
1436
|
type: "number",
|
|
@@ -2540,7 +2539,7 @@ export async function importLeads(input) {
|
|
|
2540
2539
|
const capClause = importSelection.availableEngagers >= importSelection.targetEngagerCount
|
|
2541
2540
|
? ` The selected posts can cover the target, but maxPostsToScrape=${normalizePositiveInteger(maxPostsToScrape)} prevents reaching it. Increase maxPostsToScrape or remove that cap.`
|
|
2542
2541
|
: " Select/promote more right-content posts, run another narrow Signal Discovery search, or switch to Sales Nav recent activity if the lane cannot produce enough source candidates.";
|
|
2543
|
-
throw new Error(`Signal Discovery selected posts only cover about ${importSelection.estimatedEngagers.toLocaleString("en-US")}
|
|
2542
|
+
throw new Error(`Signal Discovery selected posts only cover about ${importSelection.estimatedEngagers.toLocaleString("en-US")} people to check, below the approved ${importSelection.targetEngagerCount.toLocaleString("en-US")} source-candidate target. Do not scrape this under-capacity post set.${capClause}`);
|
|
2544
2543
|
}
|
|
2545
2544
|
const effectiveHeadlineICPCriteria = headlineICPCriteria && headlineICPCriteria.length > 0
|
|
2546
2545
|
? headlineICPCriteria
|
|
@@ -2584,7 +2583,7 @@ export async function importLeads(input) {
|
|
|
2584
2583
|
maxPostsToScrape: normalizePositiveInteger(maxPostsToScrape) ?? null,
|
|
2585
2584
|
limitedSelectedPosts: importSelection.limited,
|
|
2586
2585
|
targetLeadCount: cappedTargetLeadCount ?? null,
|
|
2587
|
-
message: `Started scraping ${postsToScrape.length} posts (~${result.estimatedEngagers}
|
|
2586
|
+
message: `Started scraping ${postsToScrape.length} posts (~${result.estimatedEngagers} people to check). Leads will appear as scraping completes.${importSelection.limited
|
|
2588
2587
|
? ` Limited from ${uniqueSelectedPosts.length} selected posts by the approved source-capacity scrape plan.`
|
|
2589
2588
|
: ""} The watched campaign has been moved to confirm-lead-list with import progress copy; do not call update_campaign to fix that step.`,
|
|
2590
2589
|
};
|
|
@@ -3149,11 +3148,11 @@ export async function selectPromisingPosts(input) {
|
|
|
3149
3148
|
const { reviewBatchSize } = getSignalDiscoverySourcePlanDefaults();
|
|
3150
3149
|
sourceRecommendation = `## Source Recommendation
|
|
3151
3150
|
|
|
3152
|
-
|
|
3151
|
+
Start with LinkedIn posts.
|
|
3153
3152
|
|
|
3154
|
-
**
|
|
3153
|
+
**Next step:** approve scraping ${selectionResult.selectedCount} recommended LinkedIn post${selectionResult.selectedCount === 1 ? "" : "s"}.
|
|
3155
3154
|
|
|
3156
|
-
|
|
3155
|
+
After approval, I'll build the source list, add it to the campaign, and review the first ${reviewBatchSize.toLocaleString("en-US")} leads before we scale.
|
|
3157
3156
|
|
|
3158
3157
|
Approval card should say:
|
|
3159
3158
|
|
package/package.json
CHANGED
|
@@ -110,8 +110,8 @@ clear business decisions, tradeoffs, and approval gates. Use product language:
|
|
|
110
110
|
Approval and safety copy should be tasteful. State what the current approval
|
|
111
111
|
covers once, in one short sentence, then move on. Do not append repeated
|
|
112
112
|
"nothing starts / no leads import / no sending" disclaimers to routine progress
|
|
113
|
-
updates. Use customer-facing gate language like "Next, we'll
|
|
114
|
-
|
|
113
|
+
updates. Use customer-facing gate language like "Next, we'll choose where to
|
|
114
|
+
find buyers" or "Next gate: selected-post scrape" instead
|
|
115
115
|
of internal terms like "source scouting" or long negative lists.
|
|
116
116
|
|
|
117
117
|
When explaining source decisions, show the concrete counts behind the
|
|
@@ -143,15 +143,23 @@ to the campaign yet. The gate should say:
|
|
|
143
143
|
approves me to look for the best places to find buyers. I won't add anyone
|
|
144
144
|
yet."
|
|
145
145
|
|
|
146
|
+
After brief approval, introduce the step in plain customer language: "Brief
|
|
147
|
+
approved. Next, we'll choose where to find buyers. I won't add anyone yet."
|
|
148
|
+
Do not say "lead-source scouting", "source scouting", or "not importing leads"
|
|
149
|
+
in the customer-facing transition.
|
|
150
|
+
Use this vocabulary ladder: "buyers" means the target market; "people to
|
|
151
|
+
check" means raw reactions/comments before fit is known; "prospects" means
|
|
152
|
+
likely usable people after fit; "leads" means campaign rows.
|
|
153
|
+
|
|
146
154
|
Use a customer-facing shape like:
|
|
147
155
|
|
|
148
156
|
```text
|
|
149
157
|
## Find Buyers Plan
|
|
150
158
|
|
|
151
159
|
I recommend starting with people already talking on LinkedIn about [plain
|
|
152
|
-
topic]. That should help us find
|
|
160
|
+
topic]. That should help us find people who already care about [plain problem].
|
|
153
161
|
|
|
154
|
-
I'll check whether there are enough
|
|
162
|
+
I'll check whether there are enough likely prospects there. If not, I'll try
|
|
155
163
|
[plain fallback] next.
|
|
156
164
|
|
|
157
165
|
Approving this means I can look for the best places to find buyers. I won't add
|
|
@@ -162,7 +170,8 @@ Do not surface blanket source heuristics as product copy. Make the
|
|
|
162
170
|
recommendation specific to the campaign. If LinkedIn engagement is recommended,
|
|
163
171
|
name the exact post themes you will search in plain language, such as "Power BI
|
|
164
172
|
dashboards they don't trust" or "teams trying to agree on the right KPIs."
|
|
165
|
-
Avoid using "Signal Discovery", "source scouting", "
|
|
173
|
+
Avoid using "Signal Discovery", "lead-source scouting", "source scouting",
|
|
174
|
+
"lane", "provider",
|
|
166
175
|
"precision/scale tradeoff", "evidence quality", "pilot volume", "workflow
|
|
167
176
|
pain", or "ICP" in customer-facing chat; those are internal labels. If relevant public
|
|
168
177
|
conversations look unlikely, recommend the specific Sales Nav or Prospeo path
|
|
@@ -214,29 +223,26 @@ For LinkedIn engagement, the customer-facing approval card must use the exact
|
|
|
214
223
|
action shape "Approve scraping N recommended LinkedIn posts?" and the chat
|
|
215
224
|
summary should be a compact `## Source Recommendation` block with:
|
|
216
225
|
|
|
217
|
-
- goal: about 300
|
|
218
|
-
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
activity instead of scraping noisy engagers. Do not use the 10% floor as the
|
|
226
|
+
- goal: about 300 likely prospects
|
|
227
|
+
- people to check: use sample math first. If there is no stronger sample, use
|
|
228
|
+
about 1,500 people who reacted or commented from the 20% starting estimate.
|
|
229
|
+
- good-sign floor: keep LinkedIn posts only when at least 10% of the first
|
|
230
|
+
sample looks like real prospects; below that, move to active LinkedIn profiles
|
|
231
|
+
instead of scraping noisy reactions. Do not use the 10% floor as the
|
|
224
232
|
scrape-count denominator when the actual sample rate is higher.
|
|
225
|
-
-
|
|
226
|
-
|
|
227
|
-
- a selected-post table with post author/topic, why it fits,
|
|
228
|
-
|
|
229
|
-
- total
|
|
230
|
-
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
headline-fit rate falls below 10%, or if the source sample is vendor-heavy,
|
|
235
|
-
agency-heavy, or off-ICP
|
|
233
|
+
- first review: after the source list exists, add confirmed source rows to the
|
|
234
|
+
campaign and review the first 15 leads before scaling
|
|
235
|
+
- a selected-post table with post author/topic, why it fits, public activity,
|
|
236
|
+
and people we can check
|
|
237
|
+
- total public activity and likely prospect pool
|
|
238
|
+
- next step: build the source list, add it to the campaign, and review the
|
|
239
|
+
first 15 leads before scaling
|
|
240
|
+
- fallback: switch to active LinkedIn profiles if the first sample is too noisy
|
|
241
|
+
or has too few prospects
|
|
236
242
|
|
|
237
243
|
When the user has not supplied a source and multiple source angles are viable,
|
|
238
244
|
scout those angles as independent branches when the host can actually do it:
|
|
239
|
-
LinkedIn
|
|
245
|
+
LinkedIn posts / people reacting or commenting (internal `signal-discovery`
|
|
240
246
|
provider prompt), Sales Nav / title + company filters, and Prospeo Contact /
|
|
241
247
|
domains, hiring filters, or broad verified-contact expansion when relevant. In
|
|
242
248
|
Codex, explicitly spawn the named custom scouts
|
|
@@ -257,7 +263,7 @@ only an internal approval proof.
|
|
|
257
263
|
For campaign-attached Signal Discovery sampling, promote/select the exact posts
|
|
258
264
|
with `select_promising_posts` before `fetch_post_engagers` so the user can see
|
|
259
265
|
which posts are being sampled in the watched app. The watch guide should say
|
|
260
|
-
that we are
|
|
266
|
+
that we are checking people from these posts to confirm the right people are
|
|
261
267
|
actually engaging and the source is viable.
|
|
262
268
|
|
|
263
269
|
After confirmed source rows exist in the campaign table, use the same registry pattern for
|
|
@@ -445,48 +451,44 @@ explicit client prospect identity in the invocation, do one lightweight lookup
|
|
|
445
451
|
first:
|
|
446
452
|
|
|
447
453
|
- LinkedIn profile: call `mcp__sellable__fetch_linkedin_profile`.
|
|
448
|
-
-
|
|
449
|
-
|
|
454
|
+
- Non-profile URLs or company-page inputs are not enough to start this flow; ask
|
|
455
|
+
again for the person's LinkedIn profile URL.
|
|
450
456
|
- Existing client prospect id: use it directly and do one company/profile lookup
|
|
451
|
-
only if a URL
|
|
457
|
+
only if a LinkedIn profile URL is also available.
|
|
452
458
|
|
|
453
459
|
Then summarize what you found in one or two lines and ask the user to confirm
|
|
454
|
-
the current company/focus before continuing
|
|
455
|
-
|
|
460
|
+
the current company/focus before continuing. Do not mention connected sender
|
|
461
|
+
availability in this confirmation.
|
|
456
462
|
|
|
457
463
|
If the user did not provide the launch identity, ask in normal chat for the
|
|
458
|
-
LinkedIn profile URL
|
|
459
|
-
|
|
464
|
+
LinkedIn profile URL. Do not ask them to choose an input type with the structured
|
|
465
|
+
question tool:
|
|
460
466
|
|
|
461
467
|
```text
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
What is your LinkedIn profile URL? If you do not have it handy, send your
|
|
465
|
-
company website instead. I’ll research the person and company from that, then
|
|
466
|
-
ask you to correct anything stale before we pick the target, offer, proof, and
|
|
467
|
-
lead source.
|
|
468
|
+
What is your LinkedIn profile URL?
|
|
468
469
|
```
|
|
469
470
|
|
|
470
|
-
After the user pastes a URL
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
471
|
+
After the user pastes a LinkedIn profile URL, call
|
|
472
|
+
`mcp__sellable__fetch_linkedin_profile` and infer the current or most recent
|
|
473
|
+
company from the profile. If they paste a non-profile URL or company page
|
|
474
|
+
instead, ask again for the person's LinkedIn profile URL. Retain the profile URL as
|
|
475
|
+
`senderLinkedinUrl` for `create_campaign`; if a `clientProspectId` is available,
|
|
476
|
+
pass that instead.
|
|
476
477
|
|
|
477
|
-
After the user confirms the company/focus,
|
|
478
|
-
|
|
479
|
-
strategy hardens:
|
|
478
|
+
After the user confirms the company/focus, ask the full setup intake before
|
|
479
|
+
inferred strategy hardens:
|
|
480
480
|
|
|
481
481
|
```text
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
482
|
+
Who should we target first?
|
|
483
|
+
What offer should this campaign lead with?
|
|
484
|
+
What is the strongest credibility signal we can lead with?
|
|
485
|
+
How should we find prospects: find prospects for me, use a CSV of LinkedIn
|
|
486
|
+
profiles, or use a CSV of company domains?
|
|
485
487
|
```
|
|
486
488
|
|
|
487
489
|
The setup questions should use the confirmed company context so they do not feel
|
|
488
|
-
generic. When you present a recommendation, introduce it as based on
|
|
489
|
-
research you just did and keep it editable.
|
|
490
|
+
generic. When you present a researched recommendation, introduce it as based on
|
|
491
|
+
the research you just did and keep it editable.
|
|
490
492
|
|
|
491
493
|
### Sufficient Intake Bypass
|
|
492
494
|
|
|
@@ -514,9 +516,9 @@ If the invocation or any later user message explicitly asks for "yolo mode",
|
|
|
514
516
|
me", "use best estimates", or "just run it", enable YOLO mode for the rest of
|
|
515
517
|
the run. Treat YOLO as `interactionMode: "autonomous"` plus an intake policy:
|
|
516
518
|
|
|
517
|
-
- If the campaign subject is missing, ask only for the LinkedIn profile URL
|
|
518
|
-
|
|
519
|
-
|
|
519
|
+
- If the campaign subject is missing, ask only for the LinkedIn profile URL in
|
|
520
|
+
normal chat; do not continue from a non-profile URL and do not ask buyer, offer,
|
|
521
|
+
proof, source, or filter setup questions before the LinkedIn URL.
|
|
520
522
|
- Treat any freeform directions already provided, or added later by the user, as
|
|
521
523
|
operator directions for the rest of the run. If directions conflict, the newest
|
|
522
524
|
user direction wins.
|
|
@@ -675,9 +677,7 @@ updates.
|
|
|
675
677
|
```text
|
|
676
678
|
You're in — {activeWorkspaceName} workspace, ready to roll.
|
|
677
679
|
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
e.g. https://www.linkedin.com/in/client-handle or https://example.com
|
|
680
|
+
What is your LinkedIn profile URL?
|
|
681
681
|
```
|
|
682
682
|
|
|
683
683
|
- If `isReturningUser === false`, prepend ONE line confirming the new
|
|
@@ -686,17 +686,14 @@ updates.
|
|
|
686
686
|
```text
|
|
687
687
|
You're set up — your {activeWorkspaceName} workspace is ready.
|
|
688
688
|
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
e.g. https://www.linkedin.com/in/client-handle or https://example.com
|
|
689
|
+
What is your LinkedIn profile URL?
|
|
692
690
|
```
|
|
693
691
|
|
|
694
692
|
No other lines. No "all set", no "signed in", no other acknowledgement.
|
|
695
693
|
|
|
696
|
-
After the user pastes the URL, proceed with the
|
|
697
|
-
setup in the v2 subskill prompt. Resolve
|
|
698
|
-
`fetch_linkedin_profile
|
|
699
|
-
when possible, and mark the client/company research gate with
|
|
694
|
+
After the user pastes the LinkedIn profile URL, proceed with the
|
|
695
|
+
identity-first campaign setup in the v2 subskill prompt. Resolve it with
|
|
696
|
+
`fetch_linkedin_profile` and mark the client/company research gate with
|
|
700
697
|
`complete_sender_research` when that protocol is required.
|
|
701
698
|
|
|
702
699
|
3. If auth is not OK with `error.type === "workspace"` (token valid, no active
|
|
@@ -39,43 +39,43 @@ unless the user asks for debug output.
|
|
|
39
39
|
4. Create the watchable campaign shell with `create_campaign` and the v1 brief.
|
|
40
40
|
5. Surface the direct watch link.
|
|
41
41
|
6. Choose and approve the lead source.
|
|
42
|
-
7. Materialize the approved source list and confirm
|
|
43
|
-
|
|
44
|
-
execution slice for filter/message setup; do not present that as source
|
|
45
|
-
sampling.
|
|
42
|
+
7. Materialize the approved source list and confirm 15 campaign-table rows for
|
|
43
|
+
filter/message setup; do not present that as source sampling.
|
|
46
44
|
8. Ask whether to use filters or skip them.
|
|
47
45
|
9. Persist lead rubrics when filters are enabled, then ask approval while the
|
|
48
46
|
app remains on Filter Rules.
|
|
49
47
|
10. Move to Filter Leads only after saved filters are approved.
|
|
50
48
|
11. Review and approve/revise the message template.
|
|
51
|
-
12. Sync the approved template into the brief, then queue bounded
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
review it, then hand off to Settings, sender, sequence, and launch.
|
|
55
|
-
|
|
56
|
-
There is no normal approval-packet, commit-gate, atomic-mint, or local
|
|
57
|
-
artifact-validation step. Those belong only to legacy validation/rehearsal
|
|
58
|
-
flows and the separate `create-campaign-v2-validation` subskill.
|
|
49
|
+
12. Sync the approved template into the brief, then queue bounded filtering.
|
|
50
|
+
13. After one row passes with a generated message, review it, then hand off to
|
|
51
|
+
Settings, sender, sequence, and launch.
|
|
59
52
|
|
|
60
53
|
## Identity-First Campaign Setup
|
|
61
54
|
|
|
62
|
-
Do not treat the active
|
|
63
|
-
client/company
|
|
64
|
-
|
|
55
|
+
Do not treat the active workspace as the campaign subject. Resolve the
|
|
56
|
+
client/company, then confirm target, offer, trust proof, and prospect source
|
|
57
|
+
before drafting the brief.
|
|
65
58
|
|
|
66
59
|
First visible request when no identity is known:
|
|
67
60
|
|
|
68
61
|
```text
|
|
69
|
-
What is your LinkedIn profile URL?
|
|
62
|
+
What is your LinkedIn profile URL?
|
|
70
63
|
```
|
|
71
64
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
65
|
+
Require a LinkedIn profile URL before setup continues. If the user provides
|
|
66
|
+
anything else, ask again for the person's LinkedIn profile URL. Retain it as
|
|
67
|
+
`senderLinkedinUrl` or resolve `clientProspectId` for `create_campaign`. Run one
|
|
68
|
+
lightweight profile/company lookup before strategy questions, then say public
|
|
69
|
+
research may be stale and invite corrections.
|
|
70
|
+
|
|
71
|
+
Restore the full setup intake before the brief. Ask bounded choices for:
|
|
72
|
+
|
|
73
|
+
- target: "Who should we target first?" with the researched recommendation
|
|
74
|
+
- offer: "What offer should this campaign lead with?" with Sellable's
|
|
75
|
+
recommendation editable
|
|
76
|
+
- trust proof: "What is the strongest credibility signal we can lead with?"
|
|
77
|
+
- prospect source: "How should we find prospects?" with "Find prospects for me",
|
|
78
|
+
"Use a CSV of LinkedIn profiles", and "Use a CSV of company domains"
|
|
79
79
|
|
|
80
80
|
Do not call `list_senders`, infer the campaign from connected senders, or show
|
|
81
81
|
a sender picker during setup. Sender availability belongs only
|
|
@@ -109,8 +109,8 @@ real examples after we find and filter leads."
|
|
|
109
109
|
|
|
110
110
|
Enable YOLO mode when the user asks for yolo/autopilot, passes `--yolo` or
|
|
111
111
|
`mode=yolo`, or says to use best guesses/estimates and answer for them. Ask only
|
|
112
|
-
for the LinkedIn profile URL
|
|
113
|
-
|
|
112
|
+
for the LinkedIn profile URL if the client/company is missing; do not continue
|
|
113
|
+
from a website or domain. After the lookup, infer buyer, offer/CTA, proof,
|
|
114
114
|
source, filters, and message direction from company evidence plus user
|
|
115
115
|
directions; newest directions win.
|
|
116
116
|
Set `interactionMode: "autonomous"` once `campaignId` exists. Auto-select
|
|
@@ -123,7 +123,7 @@ launch. Never call `start_campaign` without explicit launch confirmation.
|
|
|
123
123
|
Use the host-native structured question gate (`request_user_input` or
|
|
124
124
|
AskUserQuestion) only for bounded choices:
|
|
125
125
|
|
|
126
|
-
-
|
|
126
|
+
- target, offer, credibility, and prospect-source setup choices after research
|
|
127
127
|
- source approval or source revision
|
|
128
128
|
- use filters vs skip filters
|
|
129
129
|
- approve message vs revise message
|
|
@@ -184,11 +184,17 @@ to the campaign yet. Write short, plain sentences:
|
|
|
184
184
|
- one concise line: "This only approves me to look for the best places to find
|
|
185
185
|
buyers. I won't add anyone yet."
|
|
186
186
|
|
|
187
|
+
After brief approval, say: "Brief approved. Next, we'll choose where to find
|
|
188
|
+
buyers. I won't add anyone yet." Do not say "lead-source scouting", "source
|
|
189
|
+
scouting", or "not importing leads" in customer-facing copy.
|
|
190
|
+
Terms: buyers=market; people to check=raw reactions/comments; prospects=likely
|
|
191
|
+
after fit; leads=rows.
|
|
192
|
+
|
|
187
193
|
Do not surface blanket source heuristics as product copy. Make the recommendation
|
|
188
194
|
specific to the campaign. If LinkedIn engagement is recommended, name the exact
|
|
189
195
|
post themes you will search in plain language, such as "Power BI dashboards
|
|
190
196
|
they don't trust" or "teams trying to agree on the right KPIs." Avoid using
|
|
191
|
-
"Signal Discovery", "source scouting", "lane", "provider",
|
|
197
|
+
"Signal Discovery", "lead-source scouting", "source scouting", "lane", "provider",
|
|
192
198
|
"precision/scale tradeoff", "evidence quality", "pilot volume", "workflow
|
|
193
199
|
pain", or "ICP" in customer-facing chat; those are internal labels. If the
|
|
194
200
|
campaign looks unlikely
|
|
@@ -213,13 +219,13 @@ if it also falls below 10%, tighten the ICP/source direction instead of
|
|
|
213
219
|
inventing another provider.
|
|
214
220
|
|
|
215
221
|
After scouting, show a second approval gate for the concrete source action.
|
|
216
|
-
For LinkedIn engagement (`signal-discovery` internally),
|
|
217
|
-
|
|
218
|
-
|
|
222
|
+
For LinkedIn engagement (`signal-discovery` internally), use plain language:
|
|
223
|
+
selected posts, people we can check, likely prospects, what the first
|
|
224
|
+
review will inspect, cleanup risk, and fallback. Label the approval like
|
|
219
225
|
"Approve scraping N recommended LinkedIn posts?" where N is the smallest
|
|
220
|
-
right-content post set that clears the approved
|
|
226
|
+
right-content post set that clears the approved buyer-search target. Do not
|
|
221
227
|
default to 3 just because `selectionTarget` was 3 for sampling. For Sales Nav or
|
|
222
|
-
Prospeo, name the specific search/import
|
|
228
|
+
Prospeo, name the specific search/import path and source lead count. Do not call
|
|
223
229
|
`import_leads` or `confirm_lead_list` until this gate is approved.
|
|
224
230
|
|
|
225
231
|
For Sales Nav and Prospeo, do not ask to import only the internal 15-row
|
|
@@ -246,35 +252,36 @@ selected posts exist:
|
|
|
246
252
|
```markdown
|
|
247
253
|
## Source Recommendation
|
|
248
254
|
|
|
249
|
-
|
|
255
|
+
Start with LinkedIn posts.
|
|
256
|
+
|
|
257
|
+
I found 3 posts worth checking first.
|
|
250
258
|
|
|
251
|
-
**Goal:**
|
|
252
|
-
**
|
|
253
|
-
**
|
|
254
|
-
|
|
255
|
-
**
|
|
259
|
+
**Goal:** find about 300 likely prospects<br>
|
|
260
|
+
**People to check:** about 1,500 people who reacted or commented<br>
|
|
261
|
+
**Good sign:** at least 10% of the first sample looks like real prospects; below
|
|
262
|
+
that, switch to active LinkedIn profiles with the right titles<br>
|
|
263
|
+
**First review:** after the list is built, review the first 15 leads before we
|
|
264
|
+
scale<br>
|
|
256
265
|
|
|
257
|
-
###
|
|
266
|
+
### Recommended posts
|
|
258
267
|
|
|
259
|
-
Show author/topic, why it fits, and
|
|
268
|
+
Show author/topic, why it fits, public activity, and people we can check.
|
|
260
269
|
|
|
261
|
-
**
|
|
262
|
-
**
|
|
270
|
+
**Public activity in the selected posts:** ~2,800 reactions/comments<br>
|
|
271
|
+
**Likely prospects:** ~560 before cleanup
|
|
263
272
|
|
|
264
273
|
### Recommendation
|
|
265
274
|
|
|
266
275
|
Approve scraping these 3 posts.
|
|
267
276
|
|
|
268
|
-
|
|
269
|
-
the
|
|
270
|
-
slice so filters and generated copy are configured before scaling.
|
|
277
|
+
After approval, I'll build the source list, add it to the campaign, and
|
|
278
|
+
review the first 15 leads before we scale.
|
|
271
279
|
|
|
272
|
-
**Fallback:** if
|
|
273
|
-
|
|
274
|
-
activity.
|
|
280
|
+
**Fallback:** if the first sample is too noisy or has too few prospects,
|
|
281
|
+
switch to active LinkedIn profiles with the right titles.
|
|
275
282
|
```
|
|
276
283
|
|
|
277
|
-
A source recommendation must show concrete evidence: source
|
|
284
|
+
A source recommendation must show concrete evidence: source path, filters or
|
|
278
285
|
recipe, raw volume, sample size, sampled fits as n/N plus percentage/range,
|
|
279
286
|
estimated usable prospects, cleanup risk, runner-up, and what approval
|
|
280
287
|
authorizes. For Sales Nav/Prospeo it must also show source export math:
|
|
@@ -116,10 +116,10 @@ For source decisions, confidence comes from concrete counts. Do not say
|
|
|
116
116
|
sample size and what was counted. Prefer:
|
|
117
117
|
|
|
118
118
|
```text
|
|
119
|
-
I searched 4
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
119
|
+
I searched 4 topics over the last 30 days. The best 5 posts gave us 62 people
|
|
120
|
+
to check; 31 looked like likely prospects, so I’d treat this as directional 50%
|
|
121
|
+
sample fit, not a guaranteed audience. That gives us roughly 150-300 likely
|
|
122
|
+
prospects if the remaining people behave similarly.
|
|
123
123
|
```
|
|
124
124
|
|
|
125
125
|
Avoid:
|
|
@@ -159,9 +159,8 @@ lead sourcing or anything can send.
|
|
|
159
159
|
Good identity setup:
|
|
160
160
|
|
|
161
161
|
```text
|
|
162
|
-
I’ll ask for the LinkedIn profile URL first
|
|
163
|
-
|
|
164
|
-
anything stale before we pick the target, offer, proof, and lead source.
|
|
162
|
+
I’ll ask for the LinkedIn profile URL first. Then we’ll confirm who to target,
|
|
163
|
+
what to offer, what proof to lead with, and how to find prospects.
|
|
165
164
|
```
|
|
166
165
|
|
|
167
166
|
Bad:
|
|
@@ -180,8 +179,8 @@ Better:
|
|
|
180
179
|
|
|
181
180
|
```text
|
|
182
181
|
I found Christian Reyes connected here. Is that you, and is this campaign for
|
|
183
|
-
Sellable? If not,
|
|
184
|
-
|
|
182
|
+
Sellable? If not, send the LinkedIn profile URL for the person this campaign is
|
|
183
|
+
for.
|
|
185
184
|
```
|
|
186
185
|
|
|
187
186
|
Bad:
|
|
@@ -237,17 +236,23 @@ places we could look, I recommend starting here, here is why, and here is where
|
|
|
237
236
|
I'll look next if this is too thin." Ask them to approve that plan or choose a
|
|
238
237
|
different source. Use plain customer language for the boundary: "This only
|
|
239
238
|
approves me to look for the best places to find buyers. I won't add anyone
|
|
240
|
-
yet."
|
|
241
|
-
|
|
242
|
-
"
|
|
239
|
+
yet." After brief approval, say "Brief approved. Next, we'll choose where to
|
|
240
|
+
find buyers. I won't add anyone yet." Do not say "lead-source scouting",
|
|
241
|
+
"source scouting", or "not importing leads" in the customer-facing transition.
|
|
242
|
+
Customer-facing source-plan copy must avoid "lane", "provider",
|
|
243
|
+
"lead-source scouting", "source scouting", "precision/scale tradeoff", "evidence quality",
|
|
244
|
+
"pilot volume", "workflow pain", and "ICP". Use "buyers" for the target market,
|
|
245
|
+
"people to check" for raw reactions/comments, "prospects" after fit looks
|
|
246
|
+
usable, and "leads" for campaign rows. Second, after the source evidence exists, show
|
|
243
247
|
counts, samples, fit math, cleanup risk, and ask for source approval before
|
|
244
248
|
import.
|
|
245
249
|
|
|
246
250
|
For LinkedIn engagement, the second gate is not "approve source" in the abstract.
|
|
247
251
|
It is a concrete scrape approval: show a compact `## Source Recommendation`
|
|
248
|
-
with the ~300
|
|
249
|
-
|
|
250
|
-
|
|
252
|
+
with the ~300 likely-prospect goal, people-to-check count, selected-post table,
|
|
253
|
+
total public activity, likely prospect pool, first-review checkpoint, and fallback.
|
|
254
|
+
Keep the 10% planning floor as plain "at least 10% of the first sample should
|
|
255
|
+
look like real prospects." The approval question should be
|
|
251
256
|
"Approve scraping N recommended LinkedIn posts?" where N comes from the
|
|
252
257
|
source-candidate math, not the default promoted sample count.
|
|
253
258
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":"v2.1-compact","workflow":"create-campaign-v2","principle":"CampaignOffer state and watch link are canonical. Create the watched shell, approve source, materialize and confirm the source list, internally process the first campaign-table execution slice, save rubrics for approval, approve the message template, then run the bounded filter/message cascade before Settings, sequence, and explicit start.","normalCustomerPath":"Use campaign state, MCP responses, and concise watchNarration. Do not create, read, link, or surface local draft files.","legacyCompatibility":{"validationSubskill":"create-campaign-v2-validation","tailSubskill":"create-campaign-v2-tail","rule":"Legacy validation/rehearsal files are opt-in diagnostics only and are not active flow gates."},"commitGateChoices":["approve","revise-brief","revise-leads","revise-rubric","revise-messaging","abort"],"canonicalStateFields":["campaignId","watchUrl","campaignBrief","currentStep","watchNarration","interactionMode","providerSearchAssociation","selectedLeadListId","workflowTableId","filterChoice","leadScoringRubrics","messageDraftRecommendation","approvedMessageTemplate","senderIds","sequenceTemplate","runningState"],"watchNarrationTransitionContract":{"rule":"Every watched currentStep switch must include fresh watchNarration.","requiredFields":["stage","headline","visibleState","agentIntent","nextAction"],"copyMustInclude":["what just happened","current visible page/state","next user action"],"appliesToTools":["create_campaign","update_campaign","attach_recommended_sequence","start_campaign"]},"lazyReferences":{"watch":["references/watch-link-handoff.md","references/watch-guide-narration.md"],"source":["references/lead-validation-preview.md","references/step-13-import-leads.md"],"filter":["references/filter-leads.md"],"message":[],"tail":["references/sample-validation-loop.md","references/step-15-re-cascade.md","references/final-handoff-contract.md"]},"safetyBoundaries":["Do not call list_senders before Settings after message approval.","Do not import leads until the source decision is approved.","Do not queue cells until confirmed source rows exist in the campaign and the message/filter gates are satisfied.","Do not call start_campaign until the user explicitly confirms launch.","Do not use local files as durable state in normal customer runs."],"yoloMode":{"triggerPhrases":["yolo","--yolo","mode=yolo","autopilot","use best guesses","use best estimates","answer for me","just run it"],"identityRequestOnlyWhenMissing":true,"operatorDirectionsRule":"Treat freeform directions supplied at invocation or later as durable operator directions; newest conflicting direction wins.","setInteractionModeAfterCampaignCreate":"autonomous","autoSelectsPreLaunchChoices":["campaign focus","brief approval","source plan","source review/import approval","filters vs skip filters","filter rubric approval","message template approval when recommendation is approve-message","generated message review when quality floor passes","sender selection when exactly one connected sender is safe"],"mustPauseFor":["missing LinkedIn profile or company website","missing credentials or required data","ambiguous choice with no reasonable estimate","source/message/filter quality floor failure","final live launch confirmation"],"neverAutoStart":true},"steps":[{"id":"bootstrap","label":"Bootstrap","onEnter":[{"tool":"bootstrap_create_campaign","requiredValues":{"flowVersion":"v2"}},{"tool":"get_subskill_prompt","requiredValues":{"subskillName":"create-campaign-v2"},"purpose":"load the compact entry prompt once"}],"allowedTools":["bootstrap_create_campaign","get_auth_status","get_active_workspace","get_subskill_prompt","AskUserQuestion","request_user_input"],"doNotAllow":["create_campaign","list_senders","save_rubrics","import_leads","confirm_lead_list","update_campaign","queue_cells","start_campaign"],"waitFor":"bootstrap_complete","transitions":{"bootstrap_complete":"brief-interview"}},{"id":"brief-interview","label":"Client, offer, research, and brief","onEnter":[{"action":"resolve_campaign_identity_before_strategy_packet","allowedTools":["fetch_company","fetch_linkedin_profile","WebFetch","WebSearch"],"mustNotInferFromNameOnly":true,"doNotPresentSenderPickerBeforeIdentityInference":true,"fallback":"Ask for the LinkedIn profile URL first; use the company website as the fallback before strategy questions."},{"action":"run_campaign_identity_research_before_strategy_packet","target":"research-sender","requiredCompletion":"complete_sender_research","allowedTools":["get_subskill_prompt","fetch_linkedin_profile","fetch_company","fetch_linkedin_posts","fetch_company_posts","WebFetch","WebSearch","complete_sender_research"]},{"action":"ask_campaign_focus_only_when_ambiguous","uses":"request_user_input","singleChoice":true,"requiredOption":"Other / custom","neverCollectOpenTextWithStructuredQuestion":true,"skipIf":"yolo_mode with any reasonable best-estimate focus"},{"action":"confirm_current_offer_before_strategy_packet","uses":"request_user_input","question":"Do you already know the offer for this campaign?","options":["Use my current offer","Use Sellable's researched recommendation","Shape the offer together"],"copyMustInclude":["research basis","public website or LinkedIn may be stale","recommendation is editable"],"skipIf":"user already supplied a clear current offer or yolo_mode with any reasonable researched recommendation"},{"action":"infer_strategy_packet_in_yolo_mode","when":"yolo_mode","skipStructuredSetupQuestions":true,"inferFields":["buyer segment","offer/CTA","proof to use or avoid","lead source","filter choice","message direction"],"sourceOfTruth":"identity/company lookup plus operator directions","mustStateAssumptionsInBrief":true},{"action":"create_watchable_campaign_shell_with_v1_brief","tool":"create_campaign","requiredFields":["name","campaignBrief","clientProspectId or senderLinkedinUrl","currentStep","watchNarration"],"requiredValues":{"currentStep":"create-offer","watchNarration.stage":"brief"},"capture":["campaignId","watchUrl"],"canonicalStateWrites":["campaignId","watchUrl","campaignBrief","currentStep:create-offer"]},{"action":"surface_campaign_shell_watch_link","watchUrlSource":"create_campaign.watchUrl","requiredWatchUrlShape":"direct /campaign-builder/{campaignId}?mode={claude|codex} watch URL with token auto-login and workspace routing","copyMustInclude":["We'll keep building the campaign in this chat.","You can watch it come together in real time here:","I'll ask for your approval whenever I need your expertise or taste before moving forward.","Send changes here in chat."],"immediateNextMainChatLine":"Next, we'll find where the right buyers are already talking.","codexBrowserHandoff":{"openWhenAvailable":false,"printWatchLinkOnly":true,"tellUserCommandEnterOrClick":false,"mustNotUseBrowserAutomation":true,"fallbackMustNotClaimInspection":true}}],"allowedTools":["get_subskill_prompt","fetch_company","fetch_company_posts","fetch_linkedin_profile","fetch_linkedin_posts","complete_sender_research","create_campaign","AskUserQuestion","request_user_input"],"doNotAllow":["list_senders","save_rubrics","import_leads","confirm_lead_list","queue_cells","start_campaign"],"waitFor":["campaign_shell_created","brief_ready","confirm_with_user"],"transitions":{"campaign_shell_created":"brief-review","brief_ready":"brief-review","confirm_with_user":"brief-review"}},{"id":"brief-review","label":"Brief review","onEnter":[{"action":"render_brief_inline","minimumVisibleDetail":"campaign direction, buyer, offer, proof, source plan, safety gates","copyMustInclude":["compact provenance labels: [from you], [from LinkedIn], [from website], [from case study], or [Sellable recommendation]","This is based on what I found. If your site or LinkedIn is stale, tell me what to update before I build the lead list.","Do you agree with this researched campaign direction?"],"copyMustNotInclude":["quoted first-message copy","Message Angle as final proof"],"messageHintRule":"If any message-shape hint remains, keep it as non-final bullets and say real examples come after leads are found and filtered."},{"action":"ask_brief_choice","uses":"request_user_input","choices":["Approve brief","Revise brief","Pause here"],"skipIf":"yolo_mode; auto_continue after rendering assumptions unless brief quality floor fails"}],"requiredCampaignState":["campaignId","watchUrl","campaignBrief","currentStep"],"allowedTools":["AskUserQuestion","request_user_input","update_campaign"],"doNotAllow":["create_campaign","list_senders","import_leads","confirm_lead_list","queue_cells","start_campaign"],"waitFor":["user_brief_confirmed","revise_brief","auto_continue"],"transitions":{"user_brief_confirmed":"find-leads","revise_brief":"brief-interview","auto_continue":"find-leads"}},{"id":"find-leads","label":"Find leads","sourceSelectionFunnel":{"preScoutRecommendationGate":{"required":true,"label":"Find buyers plan approval","mustHappenBefore":["get_provider_prompt","search_signals","search_sales_nav","search_prospeo","fetch_post_engagers","source-scout dispatch"],"show":["buyer groups or places we could check","best place to start","why the right buyers are likely to be there","what signs the next search will check","where to look next if the first place is too thin","what approval authorizes"],"approvalAuthorizes":"looking for the best places to find buyers; no one added yet","explanationMustInclude":["where the right buyers are already talking","why that place fits this campaign","enough good-fit people","what counts as a good sign","where to look next if thin","I won't add anyone yet"],"yoloMode":{"autoApproveRecommendedLane":true,"mustShowAssumedChoice":true,"pauseIfConfidenceLow":true},"customerLanguage":{"readability":"grade-5","requiredHeading":"Find Buyers Plan","prefer":["place to look","best place to start","good-fit buyers","talking about this problem","I won't add anyone yet"],"avoid":["lane","source scouting","provider","precision/scale tradeoff","evidence quality","pilot volume","ICP","workflow pain"],"example":"I recommend starting with people already talking on LinkedIn about [plain topic]. That should help us find buyers who already care about [plain problem]. If this is too thin, I'll try [plain fallback] next. Approving this means I can look for the best places to find buyers. I won't add anyone to the campaign yet."}},"defaultWhenSourceUnspecified":["signal-discovery","sales-nav-recent-active","sales-nav-general","prospeo"],"userFacingFallbackOrder":["LinkedIn posts where the right buyers are already talking","people with the right titles who recently posted on LinkedIn","people with the right titles from a broader LinkedIn search","a broader company and contact search"],"parallelAllowedOnlyWhen":["user explicitly requested source comparison","resuming already-started parallel scouts","first viable source is borderline and one cheap fallback check is needed"]},"onEnter":[{"tool":"update_campaign","requiredValues":{"currentStep":"pick-provider","watchNarration.stage":"find-leads","watchNarration.headline":"Review where to find buyers","watchNarration.visibleState":"The app is showing source selection before lead finding starts.","watchNarration.agentIntent":"Codex is explaining where it recommends looking first and where it will look next if that is too thin.","watchNarration.nextAction":"Approve where to look first or choose another place","watchNarration.safety":"Only deciding where to look. No one is added yet."},"purpose":"show the visible source-plan approval checkpoint before provider lanes"},{"tool":"get_source_scout_registry","purpose":"load canonical source scout names before optional branch launch"},{"action":"show_pre_scout_source_recommendation","uses":"request_user_input","oneShot":true,"skipIf":"approved or leadSourceProvider or yolo_mode auto-selected source_lane_approved","requiredBeforeTools":["get_provider_prompt","search_signals","search_sales_nav","search_prospeo","fetch_post_engagers"],"requiredInlineFields":["plain-language buyer groups or places for this campaign","best place to start","why the right buyers are likely to be there","what signs the next search will check","where to look next if the first place is too thin","what approval authorizes"],"choices":["Start with LinkedIn posts","Start with active LinkedIn profiles","Start with company/contact search","Choose a different place","Pause here"],"approvalChoiceLabelsByProvider":{"signal-discovery":"Start with LinkedIn posts","sales-nav":"Start with active LinkedIn profiles","prospeo":"Start with company/contact search"},"approvalState":"source_lane_approved","copyMustAvoid":["lane","source scouting","provider","precision/scale tradeoff","evidence quality","pilot volume","ICP","workflow pain"],"copyMustInclude":["Find Buyers Plan","best place to start","I won't add anyone"]},{"action":"persist_provider_search_step_before_search","tool":"update_campaign","requiredPrecondition":"source_lane_approved","providerCurrentStepMap":{"signal-discovery":"signal-discovery","sales-nav":"sales-nav","prospeo":"prospeo"},"requiredValues":{"leadSourceType":"new","leadSourceProvider":"approved provider","currentStep":"provider-specific current step","watchNarration.stage":"find-leads","watchNarration.headline":"Searching the approved place","watchNarration.safety":"Looking only. No one is added yet."},"mustRunBefore":["search_signals","search_sales_nav","search_prospeo","fetch_post_engagers"]},{"action":"run_sequential_source_funnel","requiredPrecondition":"source_lane_approved","defaultOrder":["source-scout-linkedin-engagement","source-scout-sales-nav","source-scout-prospeo-contact"],"campaignOfferIdRequired":true,"stopOnFirstViableUnlessComparisonRequested":true}],"requiredCampaignState":["campaignId","campaignBrief","currentStep"],"allowedTools":["get_source_scout_registry","get_provider_prompt","lookup_sales_nav_filter","search_sales_nav","search_prospeo","search_signals","select_promising_posts","fetch_post_engagers","fetch_company","fetch_linkedin_profile","load_csv_linkedin_leads","load_csv_domains","get_rows_minimal","update_campaign","AskUserQuestion","request_user_input"],"doNotAllow":["create_campaign","list_senders","save_rubrics","import_leads","confirm_lead_list","queue_cells","start_campaign"],"waitFor":["lead_review_ready","revise_brief","confirm_with_user"],"transitions":{"lead_review_ready":"lead-review","revise_brief":"brief-interview","confirm_with_user":"lead-review"}},{"id":"lead-review","label":"Source approval","onEnter":[{"action":"show_source_decision_card","requiredInlineFields":["primary source and exact filters/recipe","specific source action awaiting approval","for Signal Discovery: compact Source Recommendation with target, raw-engager math, recommended scrape-set table, review checkpoint, estimated fit, and fallback","for Signal Discovery: recommended scrape post count and target engager/source-candidate volume","internal campaign-table execution slice size, clearly separate from source sampling","runner-up and why it lost","raw volume","sampled people","sampled fits as n/N plus percentage/range","estimated usable prospects","cleanup risk","what approval authorizes"]},{"action":"ask_source_review_choice","uses":"request_user_input","choices":["Approve scraping N recommended LinkedIn posts","Run the approved source import","Revise source","Pause here"],"approvalChoiceLabelsByProvider":{"signal-discovery":"Approve scraping {scrapePostCount} recommended LinkedIn posts?","sales-nav":"Import the approved Sales Nav source list","prospeo":"Import the approved Prospeo source list"},"postApprovalContract":{"singleUseApproval":true,"doNotRepeatAfterApproval":["Source Recommendation","show_source_decision_card","ask_source_review_choice"],"requiredNextActionAfterApproval":"ack once; call import_leads immediately","signalDiscoveryNextTool":"import_leads({ provider: \"signal-discovery\", targetEngagerCount, maxPostsToScrape, confirmed: true })"},"autoSelectIf":"yolo_mode and projected usable pool clears the source quality floor"}],"requiredCampaignState":["campaignId","campaignBrief","providerSearchAssociation"],"allowedTools":["AskUserQuestion","request_user_input","update_campaign"],"doNotAllow":["create_campaign","list_senders","save_rubrics","import_leads","confirm_lead_list","queue_cells","start_campaign"],"waitFor":["lead_review_confirmed","revise_leads","confirm_with_user","auto_continue"],"transitions":{"lead_review_confirmed":"auto-execute-leads","revise_leads":"find-leads","confirm_with_user":"auto-execute-leads","auto_continue":"auto-execute-leads"}},{"id":"auto-execute-leads","label":"Materialize confirmed source list","currentStepValue":"auto-execute-leads","reference":"references/step-13-import-leads.md","onEnter":[{"tool":"get_subskill_prompt","requiredValues":{"subskillName":"create-campaign-v2-tail"},"purpose":"load tail contract before execution tools"},{"tool":"import_leads","requiredFields":["campaignOfferId","selected source/list","sourceListTarget or targetEngagerCount"],"requiredValues":{"salesNavProspeoDefaultSourceListTarget":1000,"signalDiscoveryDefaultEngagerTarget":1500},"modeAddHandshake":{"firstCallReturns":"needsModeSelection when adding to an existing campaign-attached list","requiredFollowup":"retry with mode=add after explicit user/source-state compatibility is confirmed"},"surfaceDedupRatio":true},{"tool":"wait_for_lead_list_ready"},{"tool":"confirm_lead_list","requiredFields":["campaignOfferId","selectedLeadListId","reviewBatchLimit"],"requiredValues":{"reviewBatchLimit":15},"capture":["workflowTableId","reviewBatchRowIds"]},{"tool":"wait_for_campaign_table_ready"},{"tool":"get_rows_minimal","requiredValues":{"tableId":"{workflowTableId}","limit":15},"capture":["reviewBatchRowHash"]},{"action":"summarize_review_batch_and_advance_to_filter_choice","purpose":"ask filter choice immediately; no post-lead registries, filter refs, or message prompts first","maxCustomerCopyLines":4}],"requiredCampaignState":["campaignId","providerSearchAssociation","selectedLeadListId"],"allowedTools":["get_subskill_prompt","import_leads","wait_for_lead_list_ready","confirm_lead_list","wait_for_campaign_table_ready","get_rows_minimal","update_campaign","AskUserQuestion","request_user_input"],"doNotAllow":["get_post_find_leads_scout_registry","Task","spawn_agent","list_senders","queue_cells","start_campaign","enrich_with_prospeo","bulk_enrich_with_prospeo","save_rubrics"],"waitFor":"source_list_confirmed_and_review_sample_ready","transitions":{"source_list_confirmed_and_review_sample_ready":"filter-choice","escalation_triggered":"escalation"}},{"id":"filter-choice","label":"Filter choice","currentStepValue":"filter-choice","onEnter":[{"tool":"update_campaign","requiredValues":{"currentStep":"filter-choice","watchNarration.stage":"fit-message"}},{"action":"ask_filter_choice","uses":"request_user_input","choices":["Use filters","Skip filters","Revise source"],"autoSelectIf":"yolo_mode; choose filters unless source is tightly curated or user directed otherwise"}],"hardRules":["ask_filter_choice_immediately_after_review_batch_import","do_not_call_get_subskill_prompt_before_filter_choice","do_not_call_get_subskill_asset_before_filter_choice","do_not_call_get_post_find_leads_scout_registry_before_filter_choice","do_not_spawn_post_lead_agents_before_filter_choice"],"requiredCampaignState":["campaignId","campaignBrief","selectedLeadListId","workflowTableId"],"allowedTools":["AskUserQuestion","request_user_input","update_campaign","get_campaign_navigation_state"],"doNotAllow":["get_subskill_prompt","get_subskill_asset","get_post_find_leads_scout_registry","Task","spawn_agent","create_campaign","list_senders","import_leads","confirm_lead_list","queue_cells","start_campaign","generate_messages"],"waitFor":["filters_enabled","filters_skipped","revise_leads"],"transitions":{"filters_enabled":"post-lead-workstreams","filters_skipped":"message-generation","revise_leads":"find-leads"}},{"id":"post-lead-workstreams","label":"Filter workstream","onEnter":[{"action":"persist_add_filters_approval","tool":"update_campaign","when":"filters_enabled","requiredFields":["campaignId","enableICPFilters","currentStep","watchNarration"],"requiredValues":{"enableICPFilters":true,"currentStep":"create-icp-rubric","watchNarration.stage":"fit-message","watchNarration.headline":"Create filter rules","watchNarration.visibleState":"Filters are enabled and the browser is showing Filter Rules while Codex defines the rubric.","watchNarration.nextAction":"Review saved filter rules"},"mustRunBefore":["get_post_find_leads_scout_registry","launch_lead_fit_builder_after_filter_choice","save_rubrics"]},{"tool":"get_post_find_leads_scout_registry","purpose":"load canonical post-lead worker names only after the user has chosen filters"},{"action":"launch_lead_fit_builder_after_filter_choice","mode":"parallel_when_host_supports_subagents","target":"post-find-leads-filter-scout","inputs":["campaignId","campaignBrief","selectedLeadListId","workflowTableId","reviewBatchRowIds/hash","filterChoice"],"stateSource":"live campaign/table","debugFilesOptionalOnly":true},{"action":"launch_message_draft_builder_after_filter_decision","mode":"parallel_when_host_supports_subagents","target":"post-find-leads-message-scout","when":"filters_enabled","doesNotQueueCells":true,"customerNarration":"Say message agent is drafting."},{"action":"save_filter_rubrics_to_campaign","tool":"save_rubrics","when":"filters_enabled and rubrics are production-shaped","requiredFields":["campaignOfferId","leadScoringRubrics"],"writesCampaignState":"leadScoringRubrics","requiredSideEffects":{"enableICPFilters":true,"currentStep":"create-icp-rubric","watchNarration.headline":"Filter rules saved for review"}},{"action":"ask_filter_rubric_review_choice","uses":"request_user_input","choices":["Approve filters","Revise filters","Pause"],"purpose":"let the user read saved rubrics before Filter Leads or enrichment","copyMustInclude":["These rules prevent wasted sends before I score/import the list.","Recommended because of the researched ICP, source sample, and repeated false-positive patterns.","one pass example and one block example when available","Approval here means approving filter rules, not message drafts or campaign launch."],"autoSelectIf":"yolo_mode and rubrics are production-shaped"}],"requiredCampaignState":["campaignId","campaignBrief","selectedLeadListId","workflowTableId"],"allowedTools":["get_subskill_prompt","get_subskill_asset","get_post_find_leads_scout_registry","save_rubrics","get_campaign","get_rows_minimal","update_campaign","get_campaign_navigation_state","AskUserQuestion","request_user_input","Task","spawn_agent"],"doNotAllow":["create_campaign","list_senders","import_leads","confirm_lead_list","queue_cells","start_campaign","check_rubric","generate_messages"],"waitFor":["filter_rubrics_approved","revise_leads","revise_rubric","revise_messaging"],"hardRules":["after_save_rubrics_currentStep_must_stay_create-icp-rubric_until_filter_approval","filter_approval_required_before_apply-icp-rubric_or_queue_cells","do_not_move_browser_to_messages_until_filter_leads_step_is_current_or_filters_are_explicitly_skipped","no_post_lead_worker_or_deep_prompt_before_filter_choice","lead_fit_builder_starts_only_after_filters_enabled","msg_draft_after_filter_choice","msg_draft_no_cells"],"transitions":{"filter_rubrics_approved":"message-generation","revise_leads":"find-leads","revise_rubric":"filter-rubric","revise_messaging":"message-generation","confirm_with_user":"message-review"}},{"id":"filter-rubric","label":"Rubric revision","onEnter":[{"tool":"get_subskill_asset","requiredValues":{"subskillName":"create-campaign-v2","assetPath":"references/filter-leads.md"}},{"tool":"save_rubrics","requiredFields":["campaignOfferId","leadScoringRubrics"],"writesCampaignState":"leadScoringRubrics","requiredSideEffects":{"enableICPFilters":true,"currentStep":"create-icp-rubric","watchNarration.headline":"Filter rules saved for review"}},{"action":"ask_filter_rubric_review_choice","uses":"request_user_input","choices":["Approve filters","Revise filters","Pause"],"copyMustInclude":["These rules prevent wasted sends before I score/import the list.","approval object is filter rules only","not campaign launch"],"autoSelectIf":"yolo_mode and rubrics are production-shaped"}],"requiredCampaignState":["campaignId","workflowTableId"],"allowedTools":["get_subskill_asset","save_rubrics","AskUserQuestion","request_user_input"],"doNotAllow":["create_campaign","list_senders","import_leads","confirm_lead_list","update_campaign","queue_cells","start_campaign","check_rubric"],"waitFor":["filter_rubrics_approved","revise_leads","confirm_with_user","auto_continue"],"transitions":{"filter_rubrics_approved":"message-generation","revise_leads":"find-leads","confirm_with_user":"message-generation","auto_continue":"message-generation"}},{"id":"message-generation","label":"Message generation","onEnter":[{"action":"set_message_review_visible_step_by_filter_choice","tool":"update_campaign","branchRules":["yes after filter approval: currentStep=apply-icp-rubric; wait on Filter Leads","no: currentStep=messages; message review"],"watchNarration.stage":"fit-message"},{"action":"run_or_reconcile_message_draft_builder","target":"post-find-leads-message-scout","toolCallRequiredBeforeDraft":["run background post-find-leads-message-scout when available","get_subskill_prompt({ subskillName: \"generate-messages\", offset, limit }) until hasMore=false"],"stateSource":"campaignBrief, source, selectedLeadListId, workflowTableId, execution-slice row ids/hash","outputState":"messageDraftRecommendation"}],"requiredCampaignState":["campaignId","campaignBrief","selectedLeadListId","workflowTableId"],"allowedTools":["get_subskill_prompt","get_campaign","get_rows_minimal","update_campaign","AskUserQuestion","request_user_input"],"toolRules":["Run post-find-leads-message-scout when available; otherwise parent fallback must load get_subskill_prompt({ subskillName: \"generate-messages\" }) from live state.","brief.md, lead-review.md, and lead-sample.json are optional debug context only.","messageDraftRecommendation returns templateRecommendation, tokenFillRules, renderedSample, concerns, status, basisToken, outputAt, outputHash, and error/retry detail.","If campaign/source/table/execution-slice basis does not match, classify the output stale or blocked."],"doNotAllow":["create_campaign","list_senders","save_rubrics","import_leads","confirm_lead_list","queue_cells","start_campaign","generate_messages"],"waitFor":["message_validation_ready","revise_rubric","revise_messaging","confirm_with_user","auto_continue"],"transitions":{"message_validation_ready":"message-review","revise_rubric":"filter-rubric","revise_messaging":"message-generation","confirm_with_user":"message-review","auto_continue":"message-review"}},{"id":"message-review","label":"Message review","onEnter":[{"action":"render_message_review_from_state","requiredState":["campaignBrief","selectedLeadListId","workflowTableId","messageDraftRecommendation"],"requiredVisibleLabels":["## Message Template","## Rendered Example","Good token fill:","My take:","Question: approve-message or revise-messaging?","Recommendation:"],"doNotShowByDefault":["Token Notes","Good omit / fallback","Bad fill to avoid","Token Adherence Table"],"internalPersistenceOnly":["Token Fill Rules","Token Fill Examples","fallback guidance","bad-fill avoidance notes"],"copyMustInclude":["QA receipt before approval","tokens resolved","company/person match","proof claim","prospect angle","language/tone when known","obvious bad fits","Would I take this call?","weak personalization can burn the sender's reputation","full list remains paused"]},{"action":"ask_message_review_choice","uses":"request_user_input","choices":["approve-message","revise-messaging"],"copyMustInclude":["approve the message direction only","not campaign launch","not LinkedIn sender approval"],"autoSelectIf":"yolo_mode and Recommendation is approve-message; revise autonomously when Recommendation is revise-messaging"},{"action":"sync_approved_message_set_to_campaign_brief","tool":"update_campaign_brief","when":"after approve-message","requiredFields":["campaignId","approvedMessageTemplate","Token Fill Rules","Token Fill Examples"],"writesCampaignState":"approvedMessageTemplate"}],"requiredCampaignState":["campaignId","workflowTableId","messageDraftRecommendation"],"allowedTools":["AskUserQuestion","request_user_input","update_campaign_brief","update_campaign","get_rows_minimal"],"doNotAllow":["create_campaign","list_senders","save_rubrics","import_leads","confirm_lead_list","start_campaign"],"waitFor":["message_approved","revise_messaging"],"transitions":{"message_approved":"validate-sample","revise_messaging":"message-generation"}},{"id":"validate-sample","label":"Validate campaign-table execution slice","currentStepValue":"apply-icp-rubric","reference":"references/sample-validation-loop.md","visibleStepRule":"Filter Leads is reached only after saved-filter approval. On approve-message, save the template, refresh apply-icp-rubric narration, then queue bounded Enrich Prospect cells.","onEnter":[{"tool":"update_campaign","requiredValues":{"currentStep":"apply-icp-rubric","watchNarration.stage":"fit-message"},"purpose":"keep the watched UI on Filter Leads while the approved template starts the bounded cascade","watchNarrationRule":"Say the template is saved and Filter Leads is now running the bounded enrichment and scoring pass."},{"tool":"queue_cells","purpose":"queue ICP/enrichment cells for the internal campaign-table execution slice only","requiredFields":["workflowTableId","reviewBatchRowIds","cellTypes"],"targetCountSource":"reviewBatchRowIds.length (default 15)"},{"tool":"wait_for_campaign_table_ready","purpose":"wait_for_review_batch_scoring_cells"},{"tool":"get_rows_minimal","requiredValues":{"includeRows":false},"purpose":"read compact row status only"},{"tool":"wait_for_rubric_results","requiredFields":["workflowTableId","targetCount","minPassedCount"],"requiredValues":{"includeRows":false,"minPassedCount":1},"minPassedCountSource":"firstPassingRowForMessageStart","readVia":"stats_only_tool_result","customerSummaryPattern":["{checked} leads checked","{passed} passed and have draft messages","{blocked} blocked before sending","full list remains paused until approval"]},{"action":"handle_partial_or_timeout_sample","customerStatus":"sample-needs-revision","rule":"Do not repeat waits indefinitely; surface partial status and route to revision.","copyMustInclude":"completed, passed, pending, blocked before sending, and full list remains paused"}],"requiredCampaignState":["campaignId","workflowTableId","approvedMessageTemplate","filterRubricsApproved when enableICPFilters=true"],"allowedTools":["get_subskill_asset","queue_cells","wait_for_campaign_table_ready","get_rows_minimal","wait_for_rubric_results","update_campaign","AskUserQuestion","request_user_input"],"doNotAllow":["import_leads","list_senders","start_campaign","enrich_with_prospeo","bulk_enrich_with_prospeo","check_rubric"],"hardRules":["wait_for_rubric_results_never_retain_rows_payload_in_tail_context","wait_for_rubric_results_targetCount_always_explicit","timeout_never_repeats_without_customer_handoff","timeout_or_underfloor_sample_never_advances_to_settings"],"waitFor":["sample_validated","sample_revision_required"],"transitions":{"sample_validated":"auto-execute-messaging","sample_revision_required":"lead-review","revise_leads":"find-leads","revise_rubric":"filter-rubric","escalation_triggered":"escalation"}},{"id":"auto-execute-messaging","label":"Generate initial campaign-row messages","currentStepValue":"auto-execute-messaging","references":["references/parallel-critique-protocol.md","references/thomas-variant-selection.md","references/thomas-revision-filters.md","references/step-15-re-cascade.md"],"onEnter":[{"tool":"queue_cells","batchSize":100,"when":"any passing row has pending or empty Generate Message cell","cellSource":"pending_generate_message_cells_for_passing_rows using approved_campaign_brief_template"},{"tool":"wait_for_rubric_results","purpose":"wait_for_first_passing_generated_message","requiredValues":{"includeRows":false,"minPassedCount":1,"minMessagesCount":1},"readVia":"stats_only_tool_result"},{"action":"observe_generate_message_results","reference":"references/step-15-re-cascade.md"},{"action":"enforce_token_contract","modeFromConfig":"messaging.tokenContract"},{"action":"optional_critique_pass","enabledFromConfig":"messaging.critique.enabled","plan":"85-03","protocol":"references/parallel-critique-protocol.md","sampleSizeFromConfig":"messaging.critique.sampleSize","budgetUsdCapFromConfig":"messaging.critique.budgetUsdCap","perCriticTimeoutFromConfig":"messaging.critique.perCriticTimeoutSeconds","totalTimeoutFromConfig":"messaging.critique.totalTimeoutSeconds","criticsFromConfig":"messaging.critique.critics","enforceFinalizerPassFromConfig":"messaging.critique.synthesis.enforceFinalizerPass","rejectOnFakeProofFromConfig":"messaging.critique.rejectOnFakeProof","rejectOnUnsupportedTokenFromConfig":"messaging.critique.rejectOnUnsupportedToken","onBudgetTrip":"halt_critique_continue_plain_tail","onPerCriticTimeout":"drop_critic_proceed_with_remaining","onTotalTimeout":"persist_plain_message_for_row","onTokenContractViolation":"persist_plain_message_for_row","onFakeProof":"persist_plain_message_for_row"},{"action":"optional_opus_subset","enabledFromConfig":"messaging.critique.opus.enabled","selection":"references/thomas-variant-selection.md","maxMessagesPerPassFromConfig":"messaging.critique.opus.maxMessagesPerPass","budgetUsdCapFromConfig":"messaging.critique.opus.budgetUsdCap","onOpusBudgetTrip":"halt_opus_continue_non_opus","onOpusTokenContractViolation":"fallback_to_non_opus_rewrite"},{"tool":"update_campaign","requiredValues":{"currentStep":"auto-execute-messaging","watchNarration.stage":"review-ready"},"watchNarrationRule":"Say the first passing generated message is ready in Messages; next is review before Settings."},{"action":"ask_generated_message_review_choice","uses":"request_user_input","choices":["Approve reviewed draft rows and continue to Settings","Revise filters","Revise message template","Pause here"],"copyMustInclude":["approve these draft rows for the next review step","not approving the full campaign launch","not approving the user's LinkedIn account for sending yet","tokens resolved","company/person match","proof claim","prospect angle","language/tone when known","obvious bad fits","Would I take this call?","weak personalization can burn the sender's reputation","full list remains paused"],"autoSelectIf":"yolo_mode and the first passing generated message clears the quality floor"}],"allowedTools":["get_subskill_asset","get_rows_minimal","queue_cells","wait_for_rubric_results","update_campaign","AskUserQuestion","request_user_input"],"doNotAllow":["import_leads","list_senders","start_campaign","generate_messages"],"hardRules":["critique_failure_never_escalates","critique_sample_size_bounded_by_config","first_passing_generated_message_unblocks_review","critics_fixed_at_targeting_copy_voice","synthesis_enforces_phase_84_token_contract","opus_reserved_for_highest_value_subset","proposed_token_never_persisted_in_rewrite"],"waitFor":["generated_messages_approved","sample_revision_required"],"transitions":{"generated_messages_approved":"awaiting-user-greenlight","revise_filters":"filter-rubric","revise_messaging":"message-generation","escalation_triggered":"escalation"}},{"id":"awaiting-user-greenlight","label":"Settings, sender, sequence, and greenlight","currentStepValue":"awaiting-user-greenlight","reference":"references/final-handoff-contract.md","onEnter":[{"tool":"get_campaign","purpose":"verify senderIds and sequence state before final handoff"},{"tool":"list_senders","purpose":"surface available connected senders only at Settings"},{"tool":"update_campaign","requiredValues":{"currentStep":"settings","watchNarration.stage":"review-ready"},"purpose":"park the watched UI on Settings before sender selection","watchNarrationRule":"Say message review is complete and the browser is now on Settings so the user can connect or choose a sender. Include that lead research and filtering already happened outside the user's LinkedIn account, this sender is only used for approved sending after final launch, and no launch happens from Settings."},{"action":"surface_sender_and_slack_handoff","requiredVisibleContent":["connect or select a LinkedIn sender","Lead research and filtering already happened outside your LinkedIn account.","This account is only used for approved sending after final launch.","Nothing sends until the final Start step.","Slack reply review","recommended sequence","final launch confirmation is still ahead"]},{"action":"ask_sender_selection","uses":"request_user_input","singleChoice":true,"choices":["Use this connected sender","Connect a different sender in Settings","Pause here"],"autoSelectIf":"yolo_mode and exactly one safe connected sender is available"},{"action":"attach_selected_sender","tool":"update_campaign","when":"user selected an available connected sender","requiredValues":{"senderIds":["{selectedSenderId}"],"currentStep":"sequence","watchNarration.stage":"review-ready"},"watchNarrationRule":"Say the sender was attached and the browser is moving to Sequence review. Include that the sequence is editable before launch and still not sending until Start."},{"tool":"attach_recommended_sequence","when":"after senderIds are attached","requiredValues":{"campaignId":"{campaignId}","currentStep":"send","watchNarration.stage":"review-ready"},"watchNarrationRule":"The sequence tool owns the visible beat: say the recommended follow-up sequence is attached, the browser is now on final launch review, and the next action is final launch greenlight. Include that it is still not sending until Start. Do not follow with a separate step-only update_campaign fixup."},{"action":"ask_final_launch_greenlight","uses":"request_user_input","singleChoice":true,"choices":["Start campaign","Review campaign first","Pause here"],"copyMustInclude":["quality confidence means sample messages and prospect angle were checked","launch confidence means sender, sequence, and Start are ready","approved messages begin sending according to the sequence","replies and meetings follow connected settings","you can monitor and pause","no hidden extra approval disappears after clicking Start"],"onUserStart":"claude-greenlight","neverAutoSelectInYolo":true}],"allowedTools":["get_campaign","get_campaign_navigation_state","list_senders","update_campaign","attach_recommended_sequence","AskUserQuestion","request_user_input"],"doNotAllow":["start_campaign","import_leads"],"autoStart":false,"watchRequired":true,"waitFor":["sender_connection_required","sender_attached","sequence_attached","ready_to_launch","user_greenlight","ui_start_detected"],"transitions":{"sender_connection_required":"awaiting-user-greenlight","sender_attached":"awaiting-user-greenlight","sequence_attached":"awaiting-user-greenlight","ready_to_launch":"awaiting-user-greenlight","user_greenlight":"claude-greenlight","ui_start_detected":"running"}},{"id":"claude-greenlight","label":"Explicit launch","reference":"references/final-handoff-contract.md","onEnter":[{"tool":"get_campaign","purpose":"detect_already_running"},{"action":"verify_sequence_and_current_step_before_start","requiredState":["workflowTableId","senderIds","sequenceTemplate","at least one approved generated message","currentStep in awaiting-user-greenlight|claude-greenlight|send"]},{"tool":"start_campaign","requiredFields":["campaignId"],"persistsCurrentStep":"running","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."}],"allowedTools":["get_campaign","attach_recommended_sequence","start_campaign","AskUserQuestion","request_user_input"],"watchRequired":true,"waitFor":"campaign_started","transitions":{"campaign_started":"running"}},{"id":"running","label":"Campaign is live","currentStepValue":"running","onEnter":[{"action":"surface_watch_link"},{"action":"surface_campaign_live_confirmation"}],"allowedTools":["get_campaign","AskUserQuestion","request_user_input"],"doNotAllow":["start_campaign"],"terminal":true},{"id":"escalation","label":"Escalation","reference":"references/escalation-ladder.md","allowedTools":["AskUserQuestion","request_user_input"],"doNotAllow":["start_campaign","import_leads"],"transitions":{"revise_brief":"brief-interview","revise_leads":"find-leads","revise_rubric":"filter-rubric","revise_messaging":"message-generation","abort":"abort"}},{"id":"abort","label":"Abort","allowedTools":["AskUserQuestion","request_user_input"],"terminal":true}]}
|
|
1
|
+
{"version":"v2.1-compact","workflow":"create-campaign-v2","principle":"CampaignOffer state and watch link are canonical. Create the watched shell, approve source, materialize and confirm the source list, internally process the first campaign-table execution slice, save rubrics for approval, approve the message template, then run the bounded filter/message cascade before Settings, sequence, and explicit start.","normalCustomerPath":"Use campaign state, MCP responses, and concise watchNarration. Do not create, read, link, or surface local draft files.","legacyCompatibility":{"validationSubskill":"create-campaign-v2-validation","tailSubskill":"create-campaign-v2-tail","rule":"Legacy validation/rehearsal files are opt-in diagnostics only and are not active flow gates."},"commitGateChoices":["approve","revise-brief","revise-leads","revise-rubric","revise-messaging","abort"],"canonicalStateFields":["campaignId","watchUrl","campaignBrief","currentStep","watchNarration","interactionMode","providerSearchAssociation","selectedLeadListId","workflowTableId","filterChoice","leadScoringRubrics","messageDraftRecommendation","approvedMessageTemplate","senderIds","sequenceTemplate","runningState"],"watchNarrationTransitionContract":{"rule":"Every watched currentStep switch must include fresh watchNarration.","requiredFields":["stage","headline","visibleState","agentIntent","nextAction"],"copyMustInclude":["what just happened","current visible page/state","next user action"],"appliesToTools":["create_campaign","update_campaign","attach_recommended_sequence","start_campaign"]},"lazyReferences":{"watch":["references/watch-link-handoff.md","references/watch-guide-narration.md"],"source":["references/lead-validation-preview.md","references/step-13-import-leads.md"],"filter":["references/filter-leads.md"],"message":[],"tail":["references/sample-validation-loop.md","references/step-15-re-cascade.md","references/final-handoff-contract.md"]},"safetyBoundaries":["Do not call list_senders before Settings after message approval.","Do not import leads until the source decision is approved.","Do not queue cells until confirmed source rows exist in the campaign and the message/filter gates are satisfied.","Do not call start_campaign until the user explicitly confirms launch.","Do not use local files as durable state in normal customer runs."],"yoloMode":{"triggerPhrases":["yolo","--yolo","mode=yolo","autopilot","use best guesses","use best estimates","answer for me","just run it"],"identityRequestOnlyWhenMissing":true,"operatorDirectionsRule":"Treat freeform directions supplied at invocation or later as durable operator directions; newest conflicting direction wins.","setInteractionModeAfterCampaignCreate":"autonomous","autoSelectsPreLaunchChoices":["campaign focus","brief approval","source plan","source review/import approval","filters vs skip filters","filter rubric approval","message template approval when recommendation is approve-message","generated message review when quality floor passes","sender selection when exactly one connected sender is safe"],"mustPauseFor":["missing LinkedIn profile URL","missing credentials or required data","ambiguous choice with no reasonable estimate","source/message/filter quality floor failure","final live launch confirmation"],"neverAutoStart":true},"steps":[{"id":"bootstrap","label":"Bootstrap","onEnter":[{"tool":"bootstrap_create_campaign","requiredValues":{"flowVersion":"v2"}},{"tool":"get_subskill_prompt","requiredValues":{"subskillName":"create-campaign-v2"},"purpose":"load the compact entry prompt once"}],"allowedTools":["bootstrap_create_campaign","get_auth_status","get_active_workspace","get_subskill_prompt","AskUserQuestion","request_user_input"],"doNotAllow":["create_campaign","list_senders","save_rubrics","import_leads","confirm_lead_list","update_campaign","queue_cells","start_campaign"],"waitFor":"bootstrap_complete","transitions":{"bootstrap_complete":"brief-interview"}},{"id":"brief-interview","label":"Client, offer, research, and brief","onEnter":[{"action":"resolve_campaign_identity_before_strategy_packet","allowedTools":["fetch_company","fetch_linkedin_profile","WebFetch","WebSearch"],"mustNotInferFromNameOnly":true,"doNotPresentSenderPickerBeforeIdentityInference":true,"fallback":"Ask only: \"What is your LinkedIn profile URL?\" If the user provides a non-profile URL, company page, or anything else, ask again for the person's LinkedIn profile URL before strategy questions.","requiresLinkedInProfileUrl":true,"doNotAcceptAsIdentityInput":["non-profile URL","company page","anything other than a LinkedIn profile URL"]},{"action":"run_campaign_identity_research_before_strategy_packet","target":"research-sender","requiredCompletion":"complete_sender_research","allowedTools":["get_subskill_prompt","fetch_linkedin_profile","fetch_company","fetch_linkedin_posts","fetch_company_posts","WebFetch","WebSearch","complete_sender_research"],"requiredInput":"LinkedIn profile URL"},{"action":"confirm_target_before_strategy_packet","uses":"request_user_input","singleChoice":true,"question":"Who should we target first?","options":["Use Sellable's researched recommendation","Choose a different buyer","I'm not sure yet"],"copyMustInclude":["research basis","recommendation is editable","who to target"],"requiredOption":"Other / custom","neverCollectOpenTextWithStructuredQuestion":true,"skipIf":"user already supplied a clear target or yolo_mode with any reasonable best-estimate target"},{"action":"confirm_current_offer_before_strategy_packet","uses":"request_user_input","question":"What offer should this campaign lead with?","options":["Use Sellable's researched recommendation","Use my current offer","Shape the offer together"],"copyMustInclude":["research basis","public LinkedIn research may be stale","recommendation is editable"],"skipIf":"user already supplied a clear current offer or yolo_mode with any reasonable researched recommendation"},{"action":"confirm_trust_proof_before_strategy_packet","uses":"request_user_input","singleChoice":true,"question":"What is the strongest credibility signal we can lead with?","options":["Use Sellable's recommended proof","Use customer proof or a case study","Use founder or company credibility"],"copyMustInclude":["build trust with prospects","recommendation is editable","proof to use or avoid"],"requiredOption":"Other / custom","skipIf":"user already supplied clear proof to use or avoid, or yolo_mode with any reasonable researched proof"},{"action":"choose_prospect_source_path_before_strategy_packet","uses":"request_user_input","singleChoice":true,"question":"How should we find prospects?","options":["Find prospects for me","Use a CSV of LinkedIn profiles","Use a CSV of company domains"],"copyMustInclude":["prospects","no one added yet","find prospects for me"],"skipIf":"user already supplied a source path or yolo_mode with any reasonable source path"},{"action":"infer_strategy_packet_in_yolo_mode","when":"yolo_mode","skipStructuredSetupQuestions":true,"inferFields":["buyer segment","offer/CTA","proof to use or avoid","lead source","filter choice","message direction"],"sourceOfTruth":"identity/company lookup plus operator directions","mustStateAssumptionsInBrief":true},{"action":"create_watchable_campaign_shell_with_v1_brief","tool":"create_campaign","requiredFields":["name","campaignBrief","clientProspectId or senderLinkedinUrl","currentStep","watchNarration"],"requiredValues":{"currentStep":"create-offer","watchNarration.stage":"brief"},"capture":["campaignId","watchUrl"],"canonicalStateWrites":["campaignId","watchUrl","campaignBrief","currentStep:create-offer"]},{"action":"surface_campaign_shell_watch_link","watchUrlSource":"create_campaign.watchUrl","requiredWatchUrlShape":"direct /campaign-builder/{campaignId}?mode={claude|codex} watch URL with token auto-login and workspace routing","copyMustInclude":["We'll keep building the campaign in this chat.","You can watch it come together in real time here:","I'll ask for your approval whenever I need your expertise or taste before moving forward.","Send changes here in chat."],"immediateNextMainChatLine":"Next, we'll choose where to find buyers. I won't add anyone yet.","codexBrowserHandoff":{"openWhenAvailable":false,"printWatchLinkOnly":true,"tellUserCommandEnterOrClick":false,"mustNotUseBrowserAutomation":true,"fallbackMustNotClaimInspection":true}}],"allowedTools":["get_subskill_prompt","fetch_company","fetch_company_posts","fetch_linkedin_profile","fetch_linkedin_posts","complete_sender_research","create_campaign","AskUserQuestion","request_user_input"],"doNotAllow":["list_senders","save_rubrics","import_leads","confirm_lead_list","queue_cells","start_campaign"],"waitFor":["campaign_shell_created","brief_ready","confirm_with_user"],"transitions":{"campaign_shell_created":"brief-review","brief_ready":"brief-review","confirm_with_user":"brief-review"}},{"id":"brief-review","label":"Brief review","onEnter":[{"action":"render_brief_inline","minimumVisibleDetail":"campaign direction, buyer, offer, proof, source plan, safety gates","copyMustInclude":["compact provenance labels: [from you], [from LinkedIn], [from website], [from case study], or [Sellable recommendation]","This is based on what I found. If your site or LinkedIn is stale, tell me what to update before I build the lead list.","Do you agree with this researched campaign direction?"],"copyMustNotInclude":["quoted first-message copy","Message Angle as final proof"],"messageHintRule":"If any message-shape hint remains, keep it as non-final bullets and say real examples come after leads are found and filtered."},{"action":"ask_brief_choice","uses":"request_user_input","choices":["Approve brief","Revise brief","Pause here"],"skipIf":"yolo_mode; auto_continue after rendering assumptions unless brief quality floor fails"}],"requiredCampaignState":["campaignId","watchUrl","campaignBrief","currentStep"],"allowedTools":["AskUserQuestion","request_user_input","update_campaign"],"doNotAllow":["create_campaign","list_senders","import_leads","confirm_lead_list","queue_cells","start_campaign"],"waitFor":["user_brief_confirmed","revise_brief","auto_continue"],"transitions":{"user_brief_confirmed":"find-leads","revise_brief":"brief-interview","auto_continue":"find-leads"}},{"id":"find-leads","label":"Find leads","sourceSelectionFunnel":{"preScoutRecommendationGate":{"required":true,"label":"Find buyers plan approval","mustHappenBefore":["get_provider_prompt","search_signals","search_sales_nav","search_prospeo","fetch_post_engagers","source-scout dispatch"],"show":["buyer groups or places we could check","best place to start","why the right buyers are likely to be there","what signs the next search will check","where to look next if the first place is too thin","what approval authorizes"],"approvalAuthorizes":"looking for the best places to find buyers; no one added yet","explanationMustInclude":["where the right buyers are already talking","why that place fits this campaign","enough likely prospects","what counts as a good sign","where to look next if thin","I won't add anyone yet","choose where to find buyers"],"yoloMode":{"autoApproveRecommendedLane":true,"mustShowAssumedChoice":true,"pauseIfConfidenceLow":true},"customerLanguage":{"readability":"grade-5","requiredHeading":"Find Buyers Plan","prefer":["place to look","best place to start","people to check","prospects","I won't add anyone yet"],"avoid":["lane","source scouting","provider","precision/scale tradeoff","evidence quality","pilot volume","ICP","workflow pain","lead-source scouting","not importing leads"],"example":"Brief approved. Next, we'll choose where to find buyers. I won't add anyone yet. I recommend starting with people already talking on LinkedIn about [plain topic]. That should help us find people who already care about [plain problem]. If this is too thin, I'll try [plain fallback] next.","termRule":{"buyers":"target market","peopleToCheck":"raw reactions/comments before fit is known","prospects":"likely usable people after fit","leads":"campaign rows"}}},"defaultWhenSourceUnspecified":["signal-discovery","sales-nav-recent-active","sales-nav-general","prospeo"],"userFacingFallbackOrder":["LinkedIn posts where the right buyers are already talking","people with the right titles who recently posted on LinkedIn","people with the right titles from a broader LinkedIn search","a broader company and contact search"],"parallelAllowedOnlyWhen":["user explicitly requested source comparison","resuming already-started parallel scouts","first viable source is borderline and one cheap fallback check is needed"]},"onEnter":[{"tool":"update_campaign","requiredValues":{"currentStep":"pick-provider","watchNarration.stage":"find-leads","watchNarration.headline":"Review where to find buyers","watchNarration.visibleState":"The app is showing source selection before lead finding starts.","watchNarration.agentIntent":"Codex is explaining where it recommends looking first and where it will look next if that is too thin.","watchNarration.nextAction":"Approve where to look first or choose another place","watchNarration.safety":"Only deciding where to look. No one is added yet."},"purpose":"show the visible source-plan approval checkpoint before provider lanes"},{"tool":"get_source_scout_registry","purpose":"load canonical source scout names before optional branch launch"},{"action":"show_pre_scout_source_recommendation","uses":"request_user_input","oneShot":true,"skipIf":"approved or leadSourceProvider or yolo_mode auto-selected source_lane_approved","requiredBeforeTools":["get_provider_prompt","search_signals","search_sales_nav","search_prospeo","fetch_post_engagers"],"requiredInlineFields":["plain-language buyer groups or places for this campaign","best place to start","why the right buyers are likely to be there","what signs the next search will check","where to look next if the first place is too thin","what approval authorizes"],"choices":["Start with LinkedIn posts","Start with active LinkedIn profiles","Start with company/contact search","Choose a different place","Pause here"],"approvalChoiceLabelsByProvider":{"signal-discovery":"Start with LinkedIn posts","sales-nav":"Start with active LinkedIn profiles","prospeo":"Start with company/contact search"},"approvalState":"source_lane_approved","copyMustAvoid":["lane","source scouting","provider","precision/scale tradeoff","evidence quality","pilot volume","ICP","workflow pain","lead-source scouting","not importing leads"],"copyMustInclude":["Find Buyers Plan","best place to start","I won't add anyone","choose where to find buyers"]},{"action":"persist_provider_search_step_before_search","tool":"update_campaign","requiredPrecondition":"source_lane_approved","providerCurrentStepMap":{"signal-discovery":"signal-discovery","sales-nav":"sales-nav","prospeo":"prospeo"},"requiredValues":{"leadSourceType":"new","leadSourceProvider":"approved provider","currentStep":"provider-specific current step","watchNarration.stage":"find-leads","watchNarration.headline":"Searching the approved place","watchNarration.safety":"Looking only. No one is added yet."},"mustRunBefore":["search_signals","search_sales_nav","search_prospeo","fetch_post_engagers"]},{"action":"run_sequential_source_funnel","requiredPrecondition":"source_lane_approved","defaultOrder":["source-scout-linkedin-engagement","source-scout-sales-nav","source-scout-prospeo-contact"],"campaignOfferIdRequired":true,"stopOnFirstViableUnlessComparisonRequested":true}],"requiredCampaignState":["campaignId","campaignBrief","currentStep"],"allowedTools":["get_source_scout_registry","get_provider_prompt","lookup_sales_nav_filter","search_sales_nav","search_prospeo","search_signals","select_promising_posts","fetch_post_engagers","fetch_company","fetch_linkedin_profile","load_csv_linkedin_leads","load_csv_domains","get_rows_minimal","update_campaign","AskUserQuestion","request_user_input"],"doNotAllow":["create_campaign","list_senders","save_rubrics","import_leads","confirm_lead_list","queue_cells","start_campaign"],"waitFor":["lead_review_ready","revise_brief","confirm_with_user"],"transitions":{"lead_review_ready":"lead-review","revise_brief":"brief-interview","confirm_with_user":"lead-review"}},{"id":"lead-review","label":"Source approval","onEnter":[{"action":"show_source_decision_card","requiredInlineFields":["primary source and exact filters/recipe","specific source action awaiting approval","for Signal Discovery: compact Source Recommendation in plain language with selected posts, people to check, likely prospects, first review, and fallback","for Signal Discovery: recommended scrape post count and target people-to-check volume","first campaign review size, clearly separate from source sampling","runner-up and why it lost","raw volume","sampled people","sampled fits as n/N plus percentage/range","estimated usable prospects","cleanup risk","what approval authorizes"],"copyMustAvoid":["lead-source scouting","source scouting","headline-fit","engagers needed","execution slice","ICP","good buyers","real buyers"]},{"action":"ask_source_review_choice","uses":"request_user_input","choices":["Approve scraping N recommended LinkedIn posts","Run the approved source import","Revise source","Pause here"],"approvalChoiceLabelsByProvider":{"signal-discovery":"Approve scraping {scrapePostCount} recommended LinkedIn posts?","sales-nav":"Import the approved Sales Nav source list","prospeo":"Import the approved Prospeo source list"},"postApprovalContract":{"singleUseApproval":true,"doNotRepeatAfterApproval":["Source Recommendation","show_source_decision_card","ask_source_review_choice"],"requiredNextActionAfterApproval":"ack once; call import_leads immediately","signalDiscoveryNextTool":"import_leads({ provider: \"signal-discovery\", targetEngagerCount, maxPostsToScrape, confirmed: true })"},"autoSelectIf":"yolo_mode and projected usable pool clears the source quality floor"}],"requiredCampaignState":["campaignId","campaignBrief","providerSearchAssociation"],"allowedTools":["AskUserQuestion","request_user_input","update_campaign"],"doNotAllow":["create_campaign","list_senders","save_rubrics","import_leads","confirm_lead_list","queue_cells","start_campaign"],"waitFor":["lead_review_confirmed","revise_leads","confirm_with_user","auto_continue"],"transitions":{"lead_review_confirmed":"auto-execute-leads","revise_leads":"find-leads","confirm_with_user":"auto-execute-leads","auto_continue":"auto-execute-leads"}},{"id":"auto-execute-leads","label":"Materialize confirmed source list","currentStepValue":"auto-execute-leads","reference":"references/step-13-import-leads.md","onEnter":[{"tool":"get_subskill_prompt","requiredValues":{"subskillName":"create-campaign-v2-tail"},"purpose":"load tail contract before execution tools"},{"tool":"import_leads","requiredFields":["campaignOfferId","selected source/list","sourceListTarget or targetEngagerCount"],"requiredValues":{"salesNavProspeoDefaultSourceListTarget":1000,"signalDiscoveryDefaultEngagerTarget":1500},"modeAddHandshake":{"firstCallReturns":"needsModeSelection when adding to an existing campaign-attached list","requiredFollowup":"retry with mode=add after explicit user/source-state compatibility is confirmed"},"surfaceDedupRatio":true},{"tool":"wait_for_lead_list_ready"},{"tool":"confirm_lead_list","requiredFields":["campaignOfferId","selectedLeadListId","reviewBatchLimit"],"requiredValues":{"reviewBatchLimit":15},"capture":["workflowTableId","reviewBatchRowIds"]},{"tool":"wait_for_campaign_table_ready"},{"tool":"get_rows_minimal","requiredValues":{"tableId":"{workflowTableId}","limit":15},"capture":["reviewBatchRowHash"]},{"action":"summarize_review_batch_and_advance_to_filter_choice","purpose":"ask filter choice immediately; no post-lead registries, filter refs, or message prompts first","maxCustomerCopyLines":4}],"requiredCampaignState":["campaignId","providerSearchAssociation","selectedLeadListId"],"allowedTools":["get_subskill_prompt","import_leads","wait_for_lead_list_ready","confirm_lead_list","wait_for_campaign_table_ready","get_rows_minimal","update_campaign","AskUserQuestion","request_user_input"],"doNotAllow":["get_post_find_leads_scout_registry","Task","spawn_agent","list_senders","queue_cells","start_campaign","enrich_with_prospeo","bulk_enrich_with_prospeo","save_rubrics"],"waitFor":"source_list_confirmed_and_review_sample_ready","transitions":{"source_list_confirmed_and_review_sample_ready":"filter-choice","escalation_triggered":"escalation"}},{"id":"filter-choice","label":"Filter choice","currentStepValue":"filter-choice","onEnter":[{"tool":"update_campaign","requiredValues":{"currentStep":"filter-choice","watchNarration.stage":"fit-message"}},{"action":"ask_filter_choice","uses":"request_user_input","choices":["Use filters","Skip filters","Revise source"],"autoSelectIf":"yolo_mode; choose filters unless source is tightly curated or user directed otherwise"}],"hardRules":["ask_filter_choice_immediately_after_review_batch_import","do_not_call_get_subskill_prompt_before_filter_choice","do_not_call_get_subskill_asset_before_filter_choice","do_not_call_get_post_find_leads_scout_registry_before_filter_choice","do_not_spawn_post_lead_agents_before_filter_choice"],"requiredCampaignState":["campaignId","campaignBrief","selectedLeadListId","workflowTableId"],"allowedTools":["AskUserQuestion","request_user_input","update_campaign","get_campaign_navigation_state"],"doNotAllow":["get_subskill_prompt","get_subskill_asset","get_post_find_leads_scout_registry","Task","spawn_agent","create_campaign","list_senders","import_leads","confirm_lead_list","queue_cells","start_campaign","generate_messages"],"waitFor":["filters_enabled","filters_skipped","revise_leads"],"transitions":{"filters_enabled":"post-lead-workstreams","filters_skipped":"message-generation","revise_leads":"find-leads"}},{"id":"post-lead-workstreams","label":"Filter workstream","onEnter":[{"action":"persist_add_filters_approval","tool":"update_campaign","when":"filters_enabled","requiredFields":["campaignId","enableICPFilters","currentStep","watchNarration"],"requiredValues":{"enableICPFilters":true,"currentStep":"create-icp-rubric","watchNarration.stage":"fit-message","watchNarration.headline":"Create filter rules","watchNarration.visibleState":"Filters are enabled and the browser is showing Filter Rules while Codex defines the rubric.","watchNarration.nextAction":"Review saved filter rules"},"mustRunBefore":["get_post_find_leads_scout_registry","launch_lead_fit_builder_after_filter_choice","save_rubrics"]},{"tool":"get_post_find_leads_scout_registry","purpose":"load canonical post-lead worker names only after the user has chosen filters"},{"action":"launch_lead_fit_builder_after_filter_choice","mode":"parallel_when_host_supports_subagents","target":"post-find-leads-filter-scout","inputs":["campaignId","campaignBrief","selectedLeadListId","workflowTableId","reviewBatchRowIds/hash","filterChoice"],"stateSource":"live campaign/table","debugFilesOptionalOnly":true},{"action":"launch_message_draft_builder_after_filter_decision","mode":"parallel_when_host_supports_subagents","target":"post-find-leads-message-scout","when":"filters_enabled","doesNotQueueCells":true,"customerNarration":"Say message agent is drafting."},{"action":"save_filter_rubrics_to_campaign","tool":"save_rubrics","when":"filters_enabled and rubrics are production-shaped","requiredFields":["campaignOfferId","leadScoringRubrics"],"writesCampaignState":"leadScoringRubrics","requiredSideEffects":{"enableICPFilters":true,"currentStep":"create-icp-rubric","watchNarration.headline":"Filter rules saved for review"}},{"action":"ask_filter_rubric_review_choice","uses":"request_user_input","choices":["Approve filters","Revise filters","Pause"],"purpose":"let the user read saved rubrics before Filter Leads or enrichment","copyMustInclude":["These rules prevent wasted sends before I score/import the list.","Recommended because of the researched ICP, source sample, and repeated false-positive patterns.","one pass example and one block example when available","Approval here means approving filter rules, not message drafts or campaign launch."],"autoSelectIf":"yolo_mode and rubrics are production-shaped"}],"requiredCampaignState":["campaignId","campaignBrief","selectedLeadListId","workflowTableId"],"allowedTools":["get_subskill_prompt","get_subskill_asset","get_post_find_leads_scout_registry","save_rubrics","get_campaign","get_rows_minimal","update_campaign","get_campaign_navigation_state","AskUserQuestion","request_user_input","Task","spawn_agent"],"doNotAllow":["create_campaign","list_senders","import_leads","confirm_lead_list","queue_cells","start_campaign","check_rubric","generate_messages"],"waitFor":["filter_rubrics_approved","revise_leads","revise_rubric","revise_messaging"],"hardRules":["after_save_rubrics_currentStep_must_stay_create-icp-rubric_until_filter_approval","filter_approval_required_before_apply-icp-rubric_or_queue_cells","do_not_move_browser_to_messages_until_filter_leads_step_is_current_or_filters_are_explicitly_skipped","no_post_lead_worker_or_deep_prompt_before_filter_choice","lead_fit_builder_starts_only_after_filters_enabled","msg_draft_after_filter_choice","msg_draft_no_cells"],"transitions":{"filter_rubrics_approved":"message-generation","revise_leads":"find-leads","revise_rubric":"filter-rubric","revise_messaging":"message-generation","confirm_with_user":"message-review"}},{"id":"filter-rubric","label":"Rubric revision","onEnter":[{"tool":"get_subskill_asset","requiredValues":{"subskillName":"create-campaign-v2","assetPath":"references/filter-leads.md"}},{"tool":"save_rubrics","requiredFields":["campaignOfferId","leadScoringRubrics"],"writesCampaignState":"leadScoringRubrics","requiredSideEffects":{"enableICPFilters":true,"currentStep":"create-icp-rubric","watchNarration.headline":"Filter rules saved for review"}},{"action":"ask_filter_rubric_review_choice","uses":"request_user_input","choices":["Approve filters","Revise filters","Pause"],"copyMustInclude":["These rules prevent wasted sends before I score/import the list.","approval object is filter rules only","not campaign launch"],"autoSelectIf":"yolo_mode and rubrics are production-shaped"}],"requiredCampaignState":["campaignId","workflowTableId"],"allowedTools":["get_subskill_asset","save_rubrics","AskUserQuestion","request_user_input"],"doNotAllow":["create_campaign","list_senders","import_leads","confirm_lead_list","update_campaign","queue_cells","start_campaign","check_rubric"],"waitFor":["filter_rubrics_approved","revise_leads","confirm_with_user","auto_continue"],"transitions":{"filter_rubrics_approved":"message-generation","revise_leads":"find-leads","confirm_with_user":"message-generation","auto_continue":"message-generation"}},{"id":"message-generation","label":"Message generation","onEnter":[{"action":"set_message_review_visible_step_by_filter_choice","tool":"update_campaign","branchRules":["yes after filter approval: currentStep=apply-icp-rubric; wait on Filter Leads","no: currentStep=messages; message review"],"watchNarration.stage":"fit-message"},{"action":"run_or_reconcile_message_draft_builder","target":"post-find-leads-message-scout","toolCallRequiredBeforeDraft":["run background post-find-leads-message-scout when available","get_subskill_prompt({ subskillName: \"generate-messages\", offset, limit }) until hasMore=false"],"stateSource":"campaignBrief, source, selectedLeadListId, workflowTableId, execution-slice row ids/hash","outputState":"messageDraftRecommendation"}],"requiredCampaignState":["campaignId","campaignBrief","selectedLeadListId","workflowTableId"],"allowedTools":["get_subskill_prompt","get_campaign","get_rows_minimal","update_campaign","AskUserQuestion","request_user_input"],"toolRules":["Run post-find-leads-message-scout when available; otherwise parent fallback must load get_subskill_prompt({ subskillName: \"generate-messages\" }) from live state.","brief.md, lead-review.md, and lead-sample.json are optional debug context only.","messageDraftRecommendation returns templateRecommendation, tokenFillRules, renderedSample, concerns, status, basisToken, outputAt, outputHash, and error/retry detail.","If campaign/source/table/execution-slice basis does not match, classify the output stale or blocked."],"doNotAllow":["create_campaign","list_senders","save_rubrics","import_leads","confirm_lead_list","queue_cells","start_campaign","generate_messages"],"waitFor":["message_validation_ready","revise_rubric","revise_messaging","confirm_with_user","auto_continue"],"transitions":{"message_validation_ready":"message-review","revise_rubric":"filter-rubric","revise_messaging":"message-generation","confirm_with_user":"message-review","auto_continue":"message-review"}},{"id":"message-review","label":"Message review","onEnter":[{"action":"render_message_review_from_state","requiredState":["campaignBrief","selectedLeadListId","workflowTableId","messageDraftRecommendation"],"requiredVisibleLabels":["## Message Template","## Rendered Example","Good token fill:","My take:","Question: approve-message or revise-messaging?","Recommendation:"],"doNotShowByDefault":["Token Notes","Good omit / fallback","Bad fill to avoid","Token Adherence Table"],"internalPersistenceOnly":["Token Fill Rules","Token Fill Examples","fallback guidance","bad-fill avoidance notes"],"copyMustInclude":["QA receipt before approval","tokens resolved","company/person match","proof claim","prospect angle","language/tone when known","obvious bad fits","Would I take this call?","weak personalization can burn the sender's reputation","full list remains paused"]},{"action":"ask_message_review_choice","uses":"request_user_input","choices":["approve-message","revise-messaging"],"copyMustInclude":["approve the message direction only","not campaign launch","not LinkedIn sender approval"],"autoSelectIf":"yolo_mode and Recommendation is approve-message; revise autonomously when Recommendation is revise-messaging"},{"action":"sync_approved_message_set_to_campaign_brief","tool":"update_campaign_brief","when":"after approve-message","requiredFields":["campaignId","approvedMessageTemplate","Token Fill Rules","Token Fill Examples"],"writesCampaignState":"approvedMessageTemplate"}],"requiredCampaignState":["campaignId","workflowTableId","messageDraftRecommendation"],"allowedTools":["AskUserQuestion","request_user_input","update_campaign_brief","update_campaign","get_rows_minimal"],"doNotAllow":["create_campaign","list_senders","save_rubrics","import_leads","confirm_lead_list","start_campaign"],"waitFor":["message_approved","revise_messaging"],"transitions":{"message_approved":"validate-sample","revise_messaging":"message-generation"}},{"id":"validate-sample","label":"Validate campaign-table execution slice","currentStepValue":"apply-icp-rubric","reference":"references/sample-validation-loop.md","visibleStepRule":"Filter Leads is reached only after saved-filter approval. On approve-message, save the template, refresh apply-icp-rubric narration, then queue bounded Enrich Prospect cells.","onEnter":[{"tool":"update_campaign","requiredValues":{"currentStep":"apply-icp-rubric","watchNarration.stage":"fit-message"},"purpose":"keep the watched UI on Filter Leads while the approved template starts the bounded cascade","watchNarrationRule":"Say the template is saved and Filter Leads is now running the bounded enrichment and scoring pass."},{"tool":"queue_cells","purpose":"queue ICP/enrichment cells for the internal campaign-table execution slice only","requiredFields":["workflowTableId","reviewBatchRowIds","cellTypes"],"targetCountSource":"reviewBatchRowIds.length (default 15)"},{"tool":"wait_for_campaign_table_ready","purpose":"wait_for_review_batch_scoring_cells"},{"tool":"get_rows_minimal","requiredValues":{"includeRows":false},"purpose":"read compact row status only"},{"tool":"wait_for_rubric_results","requiredFields":["workflowTableId","targetCount","minPassedCount"],"requiredValues":{"includeRows":false,"minPassedCount":1},"minPassedCountSource":"firstPassingRowForMessageStart","readVia":"stats_only_tool_result","customerSummaryPattern":["{checked} leads checked","{passed} passed and have draft messages","{blocked} blocked before sending","full list remains paused until approval"]},{"action":"handle_partial_or_timeout_sample","customerStatus":"sample-needs-revision","rule":"Do not repeat waits indefinitely; surface partial status and route to revision.","copyMustInclude":"completed, passed, pending, blocked before sending, and full list remains paused"}],"requiredCampaignState":["campaignId","workflowTableId","approvedMessageTemplate","filterRubricsApproved when enableICPFilters=true"],"allowedTools":["get_subskill_asset","queue_cells","wait_for_campaign_table_ready","get_rows_minimal","wait_for_rubric_results","update_campaign","AskUserQuestion","request_user_input"],"doNotAllow":["import_leads","list_senders","start_campaign","enrich_with_prospeo","bulk_enrich_with_prospeo","check_rubric"],"hardRules":["wait_for_rubric_results_never_retain_rows_payload_in_tail_context","wait_for_rubric_results_targetCount_always_explicit","timeout_never_repeats_without_customer_handoff","timeout_or_underfloor_sample_never_advances_to_settings"],"waitFor":["sample_validated","sample_revision_required"],"transitions":{"sample_validated":"auto-execute-messaging","sample_revision_required":"lead-review","revise_leads":"find-leads","revise_rubric":"filter-rubric","escalation_triggered":"escalation"}},{"id":"auto-execute-messaging","label":"Generate initial campaign-row messages","currentStepValue":"auto-execute-messaging","references":["references/parallel-critique-protocol.md","references/thomas-variant-selection.md","references/thomas-revision-filters.md","references/step-15-re-cascade.md"],"onEnter":[{"tool":"queue_cells","batchSize":100,"when":"any passing row has pending or empty Generate Message cell","cellSource":"pending_generate_message_cells_for_passing_rows using approved_campaign_brief_template"},{"tool":"wait_for_rubric_results","purpose":"wait_for_first_passing_generated_message","requiredValues":{"includeRows":false,"minPassedCount":1,"minMessagesCount":1},"readVia":"stats_only_tool_result"},{"action":"observe_generate_message_results","reference":"references/step-15-re-cascade.md"},{"action":"enforce_token_contract","modeFromConfig":"messaging.tokenContract"},{"action":"optional_critique_pass","enabledFromConfig":"messaging.critique.enabled","plan":"85-03","protocol":"references/parallel-critique-protocol.md","sampleSizeFromConfig":"messaging.critique.sampleSize","budgetUsdCapFromConfig":"messaging.critique.budgetUsdCap","perCriticTimeoutFromConfig":"messaging.critique.perCriticTimeoutSeconds","totalTimeoutFromConfig":"messaging.critique.totalTimeoutSeconds","criticsFromConfig":"messaging.critique.critics","enforceFinalizerPassFromConfig":"messaging.critique.synthesis.enforceFinalizerPass","rejectOnFakeProofFromConfig":"messaging.critique.rejectOnFakeProof","rejectOnUnsupportedTokenFromConfig":"messaging.critique.rejectOnUnsupportedToken","onBudgetTrip":"halt_critique_continue_plain_tail","onPerCriticTimeout":"drop_critic_proceed_with_remaining","onTotalTimeout":"persist_plain_message_for_row","onTokenContractViolation":"persist_plain_message_for_row","onFakeProof":"persist_plain_message_for_row"},{"action":"optional_opus_subset","enabledFromConfig":"messaging.critique.opus.enabled","selection":"references/thomas-variant-selection.md","maxMessagesPerPassFromConfig":"messaging.critique.opus.maxMessagesPerPass","budgetUsdCapFromConfig":"messaging.critique.opus.budgetUsdCap","onOpusBudgetTrip":"halt_opus_continue_non_opus","onOpusTokenContractViolation":"fallback_to_non_opus_rewrite"},{"tool":"update_campaign","requiredValues":{"currentStep":"auto-execute-messaging","watchNarration.stage":"review-ready"},"watchNarrationRule":"Say the first passing generated message is ready in Messages; next is review before Settings."},{"action":"ask_generated_message_review_choice","uses":"request_user_input","choices":["Approve reviewed draft rows and continue to Settings","Revise filters","Revise message template","Pause here"],"copyMustInclude":["approve these draft rows for the next review step","not approving the full campaign launch","not approving the user's LinkedIn account for sending yet","tokens resolved","company/person match","proof claim","prospect angle","language/tone when known","obvious bad fits","Would I take this call?","weak personalization can burn the sender's reputation","full list remains paused"],"autoSelectIf":"yolo_mode and the first passing generated message clears the quality floor"}],"allowedTools":["get_subskill_asset","get_rows_minimal","queue_cells","wait_for_rubric_results","update_campaign","AskUserQuestion","request_user_input"],"doNotAllow":["import_leads","list_senders","start_campaign","generate_messages"],"hardRules":["critique_failure_never_escalates","critique_sample_size_bounded_by_config","first_passing_generated_message_unblocks_review","critics_fixed_at_targeting_copy_voice","synthesis_enforces_phase_84_token_contract","opus_reserved_for_highest_value_subset","proposed_token_never_persisted_in_rewrite"],"waitFor":["generated_messages_approved","sample_revision_required"],"transitions":{"generated_messages_approved":"awaiting-user-greenlight","revise_filters":"filter-rubric","revise_messaging":"message-generation","escalation_triggered":"escalation"}},{"id":"awaiting-user-greenlight","label":"Settings, sender, sequence, and greenlight","currentStepValue":"awaiting-user-greenlight","reference":"references/final-handoff-contract.md","onEnter":[{"tool":"get_campaign","purpose":"verify senderIds and sequence state before final handoff"},{"tool":"list_senders","purpose":"surface available connected senders only at Settings"},{"tool":"update_campaign","requiredValues":{"currentStep":"settings","watchNarration.stage":"review-ready"},"purpose":"park the watched UI on Settings before sender selection","watchNarrationRule":"Say message review is complete and the browser is now on Settings so the user can connect or choose a sender. Include that lead research and filtering already happened outside the user's LinkedIn account, this sender is only used for approved sending after final launch, and no launch happens from Settings."},{"action":"surface_sender_and_slack_handoff","requiredVisibleContent":["connect or select a LinkedIn sender","Lead research and filtering already happened outside your LinkedIn account.","This account is only used for approved sending after final launch.","Nothing sends until the final Start step.","Slack reply review","recommended sequence","final launch confirmation is still ahead"]},{"action":"ask_sender_selection","uses":"request_user_input","singleChoice":true,"choices":["Use this connected sender","Connect a different sender in Settings","Pause here"],"autoSelectIf":"yolo_mode and exactly one safe connected sender is available"},{"action":"attach_selected_sender","tool":"update_campaign","when":"user selected an available connected sender","requiredValues":{"senderIds":["{selectedSenderId}"],"currentStep":"sequence","watchNarration.stage":"review-ready"},"watchNarrationRule":"Say the sender was attached and the browser is moving to Sequence review. Include that the sequence is editable before launch and still not sending until Start."},{"tool":"attach_recommended_sequence","when":"after senderIds are attached","requiredValues":{"campaignId":"{campaignId}","currentStep":"send","watchNarration.stage":"review-ready"},"watchNarrationRule":"The sequence tool owns the visible beat: say the recommended follow-up sequence is attached, the browser is now on final launch review, and the next action is final launch greenlight. Include that it is still not sending until Start. Do not follow with a separate step-only update_campaign fixup."},{"action":"ask_final_launch_greenlight","uses":"request_user_input","singleChoice":true,"choices":["Start campaign","Review campaign first","Pause here"],"copyMustInclude":["quality confidence means sample messages and prospect angle were checked","launch confidence means sender, sequence, and Start are ready","approved messages begin sending according to the sequence","replies and meetings follow connected settings","you can monitor and pause","no hidden extra approval disappears after clicking Start"],"onUserStart":"claude-greenlight","neverAutoSelectInYolo":true}],"allowedTools":["get_campaign","get_campaign_navigation_state","list_senders","update_campaign","attach_recommended_sequence","AskUserQuestion","request_user_input"],"doNotAllow":["start_campaign","import_leads"],"autoStart":false,"watchRequired":true,"waitFor":["sender_connection_required","sender_attached","sequence_attached","ready_to_launch","user_greenlight","ui_start_detected"],"transitions":{"sender_connection_required":"awaiting-user-greenlight","sender_attached":"awaiting-user-greenlight","sequence_attached":"awaiting-user-greenlight","ready_to_launch":"awaiting-user-greenlight","user_greenlight":"claude-greenlight","ui_start_detected":"running"}},{"id":"claude-greenlight","label":"Explicit launch","reference":"references/final-handoff-contract.md","onEnter":[{"tool":"get_campaign","purpose":"detect_already_running"},{"action":"verify_sequence_and_current_step_before_start","requiredState":["workflowTableId","senderIds","sequenceTemplate","at least one approved generated message","currentStep in awaiting-user-greenlight|claude-greenlight|send"]},{"tool":"start_campaign","requiredFields":["campaignId"],"persistsCurrentStep":"running","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."}],"allowedTools":["get_campaign","attach_recommended_sequence","start_campaign","AskUserQuestion","request_user_input"],"watchRequired":true,"waitFor":"campaign_started","transitions":{"campaign_started":"running"}},{"id":"running","label":"Campaign is live","currentStepValue":"running","onEnter":[{"action":"surface_watch_link"},{"action":"surface_campaign_live_confirmation"}],"allowedTools":["get_campaign","AskUserQuestion","request_user_input"],"doNotAllow":["start_campaign"],"terminal":true},{"id":"escalation","label":"Escalation","reference":"references/escalation-ladder.md","allowedTools":["AskUserQuestion","request_user_input"],"doNotAllow":["start_campaign","import_leads"],"transitions":{"revise_brief":"brief-interview","revise_leads":"find-leads","revise_rubric":"filter-rubric","revise_messaging":"message-generation","abort":"abort"}},{"id":"abort","label":"Abort","allowedTools":["AskUserQuestion","request_user_input"],"terminal":true}]}
|
|
@@ -43,7 +43,7 @@ Send changes here in chat.
|
|
|
43
43
|
|
|
44
44
|
Watch link: [Open campaign]({watchUrl})
|
|
45
45
|
|
|
46
|
-
Next, we'll
|
|
46
|
+
Next, we'll choose where to find buyers. I won't add anyone yet.
|
|
47
47
|
|
|
48
48
|
Now ask the brief approval question. Do not show the approve/revise question
|
|
49
49
|
panel until the full brief content and this `Watch link:` line have appeared in
|