@sellable/mcp 0.1.103 → 0.1.105
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/agents/source-scout-linkedin-engagement.md +4 -4
- package/agents/source-scout-prospeo-contact.md +4 -3
- package/agents/source-scout-sales-nav.md +3 -2
- package/dist/tools/leads.js +3 -3
- package/package.json +1 -1
- package/skills/create-campaign-v2/SKILL.md +29 -17
- package/skills/create-campaign-v2/SOUL.md +1 -1
- package/skills/create-campaign-v2/core/auto-execute.README.md +5 -5
- package/skills/create-campaign-v2/core/auto-execute.yaml +4 -4
- package/skills/create-campaign-v2/core/flow.v2.json +43 -16
- package/skills/create-campaign-v2/references/approval-gate-framing.md +1 -1
- package/skills/create-campaign-v2/references/lead-validation-preview.md +1 -1
- package/skills/create-campaign-v2/references/sample-validation-loop.md +12 -12
- package/skills/create-campaign-v2/references/step-13-import-leads.md +1 -1
- package/skills/create-campaign-v2/references/watch-link-handoff.md +1 -1
|
@@ -7,10 +7,10 @@ Required first step:
|
|
|
7
7
|
- Load the canonical provider prompt before searching. If the parent supplies a
|
|
8
8
|
draft `campaignOfferId`, call `get_provider_prompt({ provider:
|
|
9
9
|
"signal-discovery", campaignOfferId, confirmed: true })` and include that same
|
|
10
|
-
`campaignOfferId`
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
campaignless preview mode.
|
|
10
|
+
`campaignOfferId` plus `currentStep: "signal-discovery"` in `search_signals`
|
|
11
|
+
so the user can watch source work in the campaign UI. Treat that as a
|
|
12
|
+
campaign-attached persisted search; do not run a post-mint search without the
|
|
13
|
+
campaign ID. If no campaign ID is supplied, run campaignless preview mode.
|
|
14
14
|
|
|
15
15
|
Use the inherited Sellable MCP tools when available:
|
|
16
16
|
|
|
@@ -6,9 +6,10 @@ Required first step:
|
|
|
6
6
|
|
|
7
7
|
- Load the canonical provider prompt before searching. If the parent supplies a
|
|
8
8
|
draft `campaignOfferId`, call `get_provider_prompt({ provider: "prospeo",
|
|
9
|
-
campaignOfferId, confirmed: true })` and include that same `campaignOfferId`
|
|
10
|
-
`search_prospeo` so the user can watch source
|
|
11
|
-
campaign ID is supplied, run campaignless
|
|
9
|
+
campaignOfferId, confirmed: true })` and include that same `campaignOfferId`
|
|
10
|
+
plus `currentStep: "prospeo"` in `search_prospeo` so the user can watch source
|
|
11
|
+
work in the campaign UI. If no campaign ID is supplied, run campaignless
|
|
12
|
+
preview mode. Treat post-mint
|
|
12
13
|
searches with `campaignOfferId` as campaign-attached persisted search tabs;
|
|
13
14
|
do not run a live campaign search without the campaign ID.
|
|
14
15
|
|
|
@@ -7,8 +7,9 @@ Required first step:
|
|
|
7
7
|
- Load the canonical provider prompt before searching. If the parent supplies a
|
|
8
8
|
draft `campaignOfferId`, call `get_provider_prompt({ provider: "sales-nav",
|
|
9
9
|
campaignOfferId, confirmed: true })` and include that same `campaignOfferId` in
|
|
10
|
-
`search_sales_nav` so the user can watch
|
|
11
|
-
campaign ID is supplied, run
|
|
10
|
+
`search_sales_nav` with `currentStep: "sales-nav"` so the user can watch
|
|
11
|
+
source work in the campaign UI. If no campaign ID is supplied, run
|
|
12
|
+
campaignless preview mode. Treat post-mint
|
|
12
13
|
searches with `campaignOfferId` as campaign-attached persisted search tabs;
|
|
13
14
|
do not run a live campaign search without the campaign ID.
|
|
14
15
|
|
package/dist/tools/leads.js
CHANGED
|
@@ -594,7 +594,7 @@ export const leadToolDefinitions = [
|
|
|
594
594
|
},
|
|
595
595
|
{
|
|
596
596
|
name: "search_sales_nav",
|
|
597
|
-
description: 'Search LinkedIn Sales Navigator. Requires get_provider_prompt({ provider: "sales-nav" }) first. Returns normalized results with pagination. `campaignOfferId` is optional for directional preview runs; include it only when the search should be associated with a campaign.',
|
|
597
|
+
description: 'Search LinkedIn Sales Navigator. Requires get_provider_prompt({ provider: "sales-nav" }) first. Returns normalized results with pagination. `campaignOfferId` is optional for directional preview runs; include it only when the search should be associated with a campaign. Post-mint create-campaign-v2 watch runs MUST pass campaignOfferId and currentStep: "sales-nav" so the search appears in the watched campaign UI. Omitting campaignOfferId post-mint orphans the search from the UI.',
|
|
598
598
|
inputSchema: {
|
|
599
599
|
type: "object",
|
|
600
600
|
properties: {
|
|
@@ -739,7 +739,7 @@ export const leadToolDefinitions = [
|
|
|
739
739
|
},
|
|
740
740
|
{
|
|
741
741
|
name: "search_prospeo",
|
|
742
|
-
description: 'Search Prospeo for people using filters and optional domainFilterId. Requires get_provider_prompt({ provider: "prospeo" }) first. When targeting known accounts, call load_csv_domains for CSV-on-disk workflows or save_domain_filters for pasted/raw domain lists, then pass domainFilterId. Raw domain inputs and company-name targeting are NOT supported in this MCP tool. Strategy: start with 2-3 high-signal filters (title/seniority + industry or domainFilterId + headcount), then tighten one filter at a time. For security, AppSec, SOC, RevOps, Demand Gen, and similar function-specific lanes, do not widen with bare seniority labels like "Head" or "Director" alone; pair them with explicit function-title keywords and inspect the sample for off-function `Head of X` leakage. Prefer person location over company HQ unless HQ is explicitly needed. `campaignOfferId` routing rule: OMIT campaignOfferId ONLY in pre-mint Phase 84 `find leads` discovery mode (validating ICP before the commit gate). In every other context — post-mint lead additions, operator-driven searches on a live campaign, any search where the intent is to persist results to a specific campaign — you MUST pass campaignOfferId so the search shows up in that campaign\'s Contact Search panel. Omitting campaignOfferId post-mint orphans the search from the UI. Returns normalized results with pagination.',
|
|
742
|
+
description: 'Search Prospeo for people using filters and optional domainFilterId. Requires get_provider_prompt({ provider: "prospeo" }) first. When targeting known accounts, call load_csv_domains for CSV-on-disk workflows or save_domain_filters for pasted/raw domain lists, then pass domainFilterId. Raw domain inputs and company-name targeting are NOT supported in this MCP tool. Strategy: start with 2-3 high-signal filters (title/seniority + industry or domainFilterId + headcount), then tighten one filter at a time. For security, AppSec, SOC, RevOps, Demand Gen, and similar function-specific lanes, do not widen with bare seniority labels like "Head" or "Director" alone; pair them with explicit function-title keywords and inspect the sample for off-function `Head of X` leakage. Prefer person location over company HQ unless HQ is explicitly needed. `campaignOfferId` routing rule: OMIT campaignOfferId ONLY in pre-mint Phase 84 `find leads` discovery mode (validating ICP before the commit gate). In every other context — post-mint lead additions, operator-driven searches on a live campaign, any search where the intent is to persist results to a specific campaign — you MUST pass campaignOfferId so the search shows up in that campaign\'s Contact Search panel. Post-mint create-campaign-v2 watch runs MUST pass currentStep: "prospeo". Omitting campaignOfferId post-mint orphans the search from the UI. Returns normalized results with pagination.',
|
|
743
743
|
inputSchema: {
|
|
744
744
|
type: "object",
|
|
745
745
|
properties: {
|
|
@@ -848,7 +848,7 @@ export const leadToolDefinitions = [
|
|
|
848
848
|
},
|
|
849
849
|
{
|
|
850
850
|
name: "search_signals",
|
|
851
|
-
description: 'Search LinkedIn posts for signal discovery. Requires get_provider_prompt({ provider: "signal-discovery" }) first. Supports keyword search, profile posts, company posts, or a single post URL. Use campaignless preview mode for directional discovery; include `campaignOfferId`
|
|
851
|
+
description: 'Search LinkedIn posts for signal discovery. Requires get_provider_prompt({ provider: "signal-discovery" }) first. Supports keyword search, profile posts, company posts, or a single post URL. Use campaignless preview mode for directional discovery; include `campaignOfferId` when the search should persist into campaign state. Post-mint create-campaign-v2 watch runs MUST pass campaignOfferId and currentStep: "signal-discovery" so the search appears in the watched campaign UI. Omitting campaignOfferId post-mint orphans the search from the UI. Returns a compact summary with recommended posts to avoid context bloat.',
|
|
852
852
|
inputSchema: {
|
|
853
853
|
type: "object",
|
|
854
854
|
properties: {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: create-campaign-v2
|
|
3
|
-
description: Execute the JSON-gated shell-first campaign flow with chained Phase 84 artifacts: create a watchable campaign shell with a v1 brief, attach the source with campaignId, import the first
|
|
3
|
+
description: Execute the JSON-gated shell-first campaign flow with chained Phase 84 artifacts: create a watchable campaign shell with a v1 brief, attach the source with campaignId, import the first 15-row review batch, save rubrics and approved message template, then queue the review cascade and hand off to settings/sequence/start.
|
|
4
4
|
visibility: internal
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -11,7 +11,7 @@ You are the create-campaign-v2 orchestrator. Your job is to execute the
|
|
|
11
11
|
configured `core/flow.v2.json` state machine from scratch: (1) interview the
|
|
12
12
|
user, resolve campaign identity/client-prospect context, and create a watchable
|
|
13
13
|
campaign shell with the v1 brief already attached, (2) use that campaign id for
|
|
14
|
-
source selection, (3) import/confirm the first
|
|
14
|
+
source selection, (3) import/confirm the first 15-row review batch to establish
|
|
15
15
|
`workflowTableId`, (4) create rubrics and message artifacts from that campaign
|
|
16
16
|
table sample, (5) sync the approved message set into the campaign brief, and
|
|
17
17
|
(6) queue/observe the bounded cascade before settings, sequence, and start.
|
|
@@ -23,12 +23,12 @@ Run the configured JSON flow in durable stages:
|
|
|
23
23
|
0. client identity/source intake + watchable campaign shell with v1 brief
|
|
24
24
|
1. create-campaign-brief
|
|
25
25
|
2. find leads with the campaign id
|
|
26
|
-
3. import/confirm the first
|
|
26
|
+
3. import/confirm the first 15-row review batch
|
|
27
27
|
4. filter leads from the campaign table sample
|
|
28
28
|
5. generate message from the same campaign table sample
|
|
29
29
|
6. message review gate
|
|
30
30
|
7. sync approved message set into the campaign brief
|
|
31
|
-
8. queue and validate the
|
|
31
|
+
8. queue and validate the 15-row cascade
|
|
32
32
|
9. settings/sender, sequence, and start handoff
|
|
33
33
|
|
|
34
34
|
The JSON flow is the source of truth for stage order, artifact policy,
|
|
@@ -94,9 +94,9 @@ Optional debug/UAT draft directory, disabled in normal customer runs:
|
|
|
94
94
|
stage, call `update_campaign({ campaignId, currentStep })` with the visible UI
|
|
95
95
|
step the user should be watching, then continue the work from MCP/product
|
|
96
96
|
state. Use `create-offer` for the brief, `pick-provider` or the selected
|
|
97
|
-
provider step while sourcing, `filter-choice` after the
|
|
97
|
+
provider step while sourcing, `filter-choice` after the 15-row review batch is
|
|
98
98
|
imported, `messages`/`auto-execute-messaging` for message work,
|
|
99
|
-
`validate-sample` for the
|
|
99
|
+
`validate-sample` for the 15-row validation cascade, and
|
|
100
100
|
`awaiting-user-greenlight` for Settings. Do not advance `currentStep` backward.
|
|
101
101
|
- After the shell is minted, normal resume paths read the CampaignOffer first.
|
|
102
102
|
Use debug files to explain or reconstruct work, not to decide whether a
|
|
@@ -172,7 +172,7 @@ Optional debug/UAT draft directory, disabled in normal customer runs:
|
|
|
172
172
|
"Sellable token".
|
|
173
173
|
"Quick question panel" is only acceptable when explaining a Codex/Claude setup
|
|
174
174
|
blocker.
|
|
175
|
-
- Before the
|
|
175
|
+
- Before the 15-lead import/test batch, do not call `check_rubric`; Phase 84
|
|
176
176
|
only writes and validates artifacts, saves rubrics, and syncs campaign setup
|
|
177
177
|
state.
|
|
178
178
|
- Never narrate local draft housekeeping to the user. If you create directories,
|
|
@@ -201,7 +201,7 @@ Optional debug/UAT draft directory, disabled in normal customer runs:
|
|
|
201
201
|
- Do not call `list_senders`, `get_sender`, or surface connected/missing sender
|
|
202
202
|
state during setup, brief, source, filter, or message review. Sender
|
|
203
203
|
availability belongs only to the Settings/final launch handoff after
|
|
204
|
-
`approve-message` and the
|
|
204
|
+
`approve-message` and the 15-lead validation sample. The first user-visible
|
|
205
205
|
sender blocker should be at `awaiting-user-greenlight`/Settings.
|
|
206
206
|
- If the invocation or user answer includes an existing `clientProspectId`, keep
|
|
207
207
|
it as the preferred `create_campaign` identity input. If it includes a
|
|
@@ -432,9 +432,16 @@ to be worth a LinkedIn test. I'll compare source paths by expected volume,
|
|
|
432
432
|
sampled ICP fit, activity/warmth signal, cleanup risk, and tradeoffs. This
|
|
433
433
|
usually takes ~3-5 min, and I'll show you the source decision + sample before
|
|
434
434
|
anything goes live.`
|
|
435
|
+
- In watch mode, do not leave the user sitting on only `pick-provider` while
|
|
436
|
+
source scouts run. Move the campaign to the likely primary source lane
|
|
437
|
+
(`signal-discovery`, `sales-nav`, or `prospeo`) before background source
|
|
438
|
+
scouts, then run the first campaign-attached provider prompt + provider search
|
|
439
|
+
in the parent thread with `campaignOfferId`, `confirmed: true`, and
|
|
440
|
+
`currentStep` when the tool accepts it. Background scouts may continue after
|
|
441
|
+
the visible campaign tab exists.
|
|
435
442
|
- After the lead sample/source decision is ready and approved,
|
|
436
443
|
show the next progress line:
|
|
437
|
-
`Lead source is set. I'll import the first
|
|
444
|
+
`Lead source is set. I'll import the first 15-row review batch into the
|
|
438
445
|
campaign table, then use that same batch to tighten the fit filter and draft the
|
|
439
446
|
message we should test.`
|
|
440
447
|
- During long post-intake work, show concise progress checkpoints before the
|
|
@@ -509,7 +516,7 @@ message we should test.`
|
|
|
509
516
|
and `message-validation.md`, then reconciles that the selected message basis
|
|
510
517
|
rows still pass the final filter.
|
|
511
518
|
If a legacy/resume host starts message prep from preview rows before import,
|
|
512
|
-
it still needs at least
|
|
519
|
+
it still needs at least 3 probable good-fit rows and must reconcile the final
|
|
513
520
|
winner against the imported review batch before message review.
|
|
514
521
|
- `lead-sample.json` from `find leads` is always the message sample source.
|
|
515
522
|
`filter leads` must not create a different message sample or cause message
|
|
@@ -542,6 +549,11 @@ message we should test.`
|
|
|
542
549
|
then draft the message from the same sample.` Never say `kicking off two
|
|
543
550
|
workstreams`, `in parallel`, or `background` unless parallel branches were
|
|
544
551
|
actually launched.
|
|
552
|
+
- Source scout parallelism must not hide all provider work from the watch UI.
|
|
553
|
+
The parent thread owns the first visible provider search: call the chosen
|
|
554
|
+
provider prompt/search with the minted CampaignOffer id and provider
|
|
555
|
+
`currentStep` before waiting on source-scout results, so relevant search tabs
|
|
556
|
+
appear in real time.
|
|
545
557
|
- For post-lead workstreams, first call
|
|
546
558
|
`get_post_find_leads_scout_registry` and launch exactly the returned
|
|
547
559
|
`filter-leads` and `message-generation` scouts when real subagents are
|
|
@@ -556,7 +568,7 @@ workstreams`, `in parallel`, or `background` unless parallel branches were
|
|
|
556
568
|
- Never call a tool outside the active step's `allowedTools`, and never call a
|
|
557
569
|
tool listed in the active step's `doNotAllow`.
|
|
558
570
|
- Before the source is approved, `import_leads` and `confirm_lead_list` are
|
|
559
|
-
forbidden. After source approval, import/confirm only the bounded
|
|
571
|
+
forbidden. After source approval, import/confirm only the bounded 15-row
|
|
560
572
|
review batch. Before rubrics and the approved message set are both on the
|
|
561
573
|
campaign, the spend/send-adjacent cascade tools are forbidden: `queue_cells`,
|
|
562
574
|
`enrich_with_prospeo`, `bulk_enrich_with_prospeo`,
|
|
@@ -565,7 +577,7 @@ workstreams`, `in parallel`, or `background` unless parallel branches were
|
|
|
565
577
|
the active `flow.v2.json` step allows them.
|
|
566
578
|
- During pre-import validation, do not call `check_rubric`; use the lead-filter
|
|
567
579
|
artifacts and only use campaign-backed scoring after Step 13 imports the
|
|
568
|
-
|
|
580
|
+
15-lead test batch.
|
|
569
581
|
- Resume state is based on CampaignOffer state first, then the presence and
|
|
570
582
|
completeness of the chained debug artifacts. Never treat file presence as the
|
|
571
583
|
durable source of truth after the campaign exists.
|
|
@@ -574,8 +586,8 @@ workstreams`, `in parallel`, or `background` unless parallel branches were
|
|
|
574
586
|
- Exception: brief-validated may rewrite §3 Campaign Thesis AND §12 Next Steps
|
|
575
587
|
when Phase-84 lead-yield data clearly contradicts a Phase-83 UNVALIDATED
|
|
576
588
|
decision on primary lead-source. "Clearly contradicts" = the Phase-83
|
|
577
|
-
primary source yields 0 FIT while an alternative source yields ≥
|
|
578
|
-
the same
|
|
589
|
+
primary source yields 0 FIT while an alternative source yields ≥ 3 FIT in
|
|
590
|
+
the same 15-row test sample (or an equivalent ≥ 5× ratio at larger samples).
|
|
579
591
|
Every rewrite MUST cite specific yield numbers from `lead-review.md`. All
|
|
580
592
|
other Phase-83 brief sections remain read-only. See
|
|
581
593
|
`references/filter-leads.md` § Brief-Validated Rewrite Authority for the
|
|
@@ -584,14 +596,14 @@ workstreams`, `in parallel`, or `background` unless parallel branches were
|
|
|
584
596
|
artifact intact.
|
|
585
597
|
- If a required upstream artifact is missing, stop and route back to the
|
|
586
598
|
missing step instead of guessing.
|
|
587
|
-
- Before importing the
|
|
599
|
+
- Before importing the 15-lead test batch, persist a customer-facing
|
|
588
600
|
`message-review.md` and `message-review-decision.md` so the message decision
|
|
589
601
|
can be inspected later.
|
|
590
602
|
- The message review must show one concrete sample message rendered as the
|
|
591
603
|
recipient would see it, in addition to the approved message template. Do not
|
|
592
604
|
make the user mentally expand `{{tokens}}`.
|
|
593
605
|
- Do not include sequence, cadence, sending-window, mailbox, or campaign
|
|
594
|
-
settings review in `message-review.md`. Those come after the
|
|
606
|
+
settings review in `message-review.md`. Those come after the 15-lead test
|
|
595
607
|
batch, in the settings/sequence handoff.
|
|
596
608
|
- Do not include post-accept DMs, connection-accepted follow-ups, day-2 / day-7
|
|
597
609
|
follow-ups, second touches, reply branches, or any other sequence-shaped copy
|
|
@@ -620,7 +632,7 @@ There are four customer-visible gates in the net-new hosted flow:
|
|
|
620
632
|
only whether to proceed with the selected message (`approve-message`) or run
|
|
621
633
|
one more messaging revision (`revise-messaging`). `approve-message` authorizes
|
|
622
634
|
syncing the approved message set into the campaign brief, saving rubrics,
|
|
623
|
-
importing the bounded
|
|
635
|
+
importing the bounded 15-lead test batch, and queueing/observing that sample.
|
|
624
636
|
- The sender/settings/sequence handoff is the launch gate. After message
|
|
625
637
|
validation, use Settings to help the user connect or select a LinkedIn sender.
|
|
626
638
|
Explain Slack reply review before launch. After sender selection, attach the
|
|
@@ -81,7 +81,7 @@ example, like `omit the noticed... line` or `use "operators"`. Missing data
|
|
|
81
81
|
should never produce robotic or creepy copy.
|
|
82
82
|
|
|
83
83
|
Approved token guidance is part of the campaign, not just the review. Before
|
|
84
|
-
the
|
|
84
|
+
the 15-lead test batch imports, the campaign brief should carry forward the
|
|
85
85
|
token fill rules and examples: good fills, good omits, bad fills, fallback
|
|
86
86
|
rules, and why the bad fills are blocked.
|
|
87
87
|
The campaign brief must carry forward the token fill rules from review into the
|
|
@@ -33,16 +33,16 @@ the imported review batch for validation).
|
|
|
33
33
|
|
|
34
34
|
- **`sampleSize`** — How many rows the validation loop pulls from the
|
|
35
35
|
imported review batch. In v2 this matches the review-batch cap
|
|
36
|
-
(default
|
|
36
|
+
(default 15), so the user sees enough real rows to judge the campaign
|
|
37
37
|
without spending credits on hundreds more.
|
|
38
38
|
- **`minProjectedPass`** — Passing-message floor required before handoff.
|
|
39
|
-
The default requires
|
|
39
|
+
The default requires 3 passing examples from the 15-row review batch.
|
|
40
40
|
Math:
|
|
41
41
|
```text
|
|
42
42
|
projectedPass = round(passInSample / sampleSize * importLimit)
|
|
43
43
|
```
|
|
44
|
-
With defaults (sampleSize=
|
|
45
|
-
the actual review-batch pass count.
|
|
44
|
+
With defaults (sampleSize=15, importLimit=15), `projectedPass` equals
|
|
45
|
+
the actual review-batch pass count. 3 passing rows is exactly at the
|
|
46
46
|
floor.
|
|
47
47
|
- **`maxRevisionRounds`** — Hard cap before escalating to the user. A
|
|
48
48
|
revision round is one full sample pass that triggered brief revision
|
|
@@ -130,7 +130,7 @@ logs and adjust:
|
|
|
130
130
|
batches and credit spend is acceptable.
|
|
131
131
|
- `sampleSize` higher if a 25-row review batch is not enough to judge
|
|
132
132
|
quality for a specific market.
|
|
133
|
-
- `minProjectedPass` lower only if fewer than
|
|
133
|
+
- `minProjectedPass` lower only if fewer than 3 passing examples is still
|
|
134
134
|
enough for a confident user decision.
|
|
135
135
|
- `maxRevisionRounds` should rarely change; 3 is already generous. If
|
|
136
136
|
brief revision is hitting the cap often, the brief template upstream
|
|
@@ -17,7 +17,7 @@ import:
|
|
|
17
17
|
# Initial review batch imported in Step 13 before the sample validation
|
|
18
18
|
# loop. Import + enrichment must never exceed this cap before the user
|
|
19
19
|
# reviews the sample and explicitly asks to expand.
|
|
20
|
-
importLimit:
|
|
20
|
+
importLimit: 15
|
|
21
21
|
# Provider is inherited from the campaign-attached source association created
|
|
22
22
|
# during find-leads. Step 13 may re-run provider preflight in memory, but it
|
|
23
23
|
# does not choose a new source.
|
|
@@ -27,11 +27,11 @@ sample:
|
|
|
27
27
|
# Rows pulled from the imported cohort for the validation loop. In v2 this
|
|
28
28
|
# equals the review-batch cap so the user sees a focused first test batch
|
|
29
29
|
# without spending credits on hundreds of rows.
|
|
30
|
-
sampleSize:
|
|
30
|
+
sampleSize: 15
|
|
31
31
|
# Projected first-batch passing count required to proceed to sample
|
|
32
32
|
# messaging. This is not approval to scale the full source list.
|
|
33
33
|
# projectedPass = round(passInSample / sampleSize * importLimit).
|
|
34
|
-
minProjectedPass:
|
|
34
|
+
minProjectedPass: 3
|
|
35
35
|
# Hard cap on revision loops before escalating to the user. On a stale
|
|
36
36
|
# resume the counter does NOT reset — the 3-round cap holds across
|
|
37
37
|
# sessions.
|
|
@@ -106,7 +106,7 @@ handoff:
|
|
|
106
106
|
# Orientation surfaced in Step 16 ("awaiting-user-greenlight") along with
|
|
107
107
|
# the watch link. Keep this short and user-facing.
|
|
108
108
|
orientation: >-
|
|
109
|
-
Review the first
|
|
109
|
+
Review the first 15 enriched leads and messages. If they look good,
|
|
110
110
|
attach a sender in Settings, accept the recommended sequence, and start
|
|
111
111
|
the campaign without spending credits on more leads first.
|
|
112
112
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": "v2",
|
|
3
3
|
"workflow": "create-campaign-v2",
|
|
4
|
-
"principle": "CampaignOffer state and the watch link are canonical from the first brief onward. Disk artifacts are optional debug/UAT diagnostics, not the normal customer surface. Resume, gating, and handoff read campaign state first. Start from user intake, create a watchable campaign shell with a v1 brief, update currentStep before major visible work, attach source/search state to that CampaignOffer, import the bounded review batch before post-import fit/message scouts, save rubrics and an approved message template, then queue the
|
|
4
|
+
"principle": "CampaignOffer state and the watch link are canonical from the first brief onward. Disk artifacts are optional debug/UAT diagnostics, not the normal customer surface. Resume, gating, and handoff read campaign state first. Start from user intake, create a watchable campaign shell with a v1 brief, update currentStep before major visible work, attach source/search state to that CampaignOffer, import the bounded review batch before post-import fit/message scouts, save rubrics and an approved message template, then queue the 15-row cascade and hand off to settings/sequence/start.",
|
|
5
5
|
"artifactPolicy": {
|
|
6
6
|
"normalCustomerPath": "Use CampaignOffer state, MCP responses, provider search state, and campaign table rows. Do not create, read, link, or surface local draft files unless debug/UAT mode is explicit or the user asks for them.",
|
|
7
7
|
"debugArtifactsAreOptional": true,
|
|
@@ -520,6 +520,33 @@
|
|
|
520
520
|
"when": "before_source_scouts_or_provider_search",
|
|
521
521
|
"chatRenderRule": "Move the campaign watch view to Find Contacts before the main thread starts comparing source paths. Do not mention MCP or local artifacts."
|
|
522
522
|
},
|
|
523
|
+
{
|
|
524
|
+
"action": "advance_watch_to_initial_source_lane",
|
|
525
|
+
"tool": "update_campaign",
|
|
526
|
+
"requiredFields": [
|
|
527
|
+
"campaignId",
|
|
528
|
+
"currentStep"
|
|
529
|
+
],
|
|
530
|
+
"currentStepByPrimaryLane": {
|
|
531
|
+
"signals": "signal-discovery",
|
|
532
|
+
"salesNav": "sales-nav",
|
|
533
|
+
"prospeo": "prospeo",
|
|
534
|
+
"apollo": "apollo-ai",
|
|
535
|
+
"existingList": "saved-lists",
|
|
536
|
+
"uploadedDomains": "prospeo"
|
|
537
|
+
},
|
|
538
|
+
"when": "before_background_source_scouts",
|
|
539
|
+
"rule": "Choose the likely primary visible source lane from source intake, brief preference, or the best first lane the main thread will actually search. Do this before waiting on background scouts so watch mode never sits on only Pick Provider while source work happens elsewhere."
|
|
540
|
+
},
|
|
541
|
+
{
|
|
542
|
+
"action": "run_first_campaign_attached_source_search",
|
|
543
|
+
"requiredFields": [
|
|
544
|
+
"campaignOfferId",
|
|
545
|
+
"currentStep"
|
|
546
|
+
],
|
|
547
|
+
"before": "waiting_on_background_source_scouts",
|
|
548
|
+
"rule": "Before relying on background source scouts, the parent thread must run the first provider prompt + provider search for the chosen visible lane with campaignOfferId and currentStep (signal-discovery, sales-nav, or prospeo) so the campaign UI creates the corresponding live search/tab. Background scouts may continue after that, but the user must see at least one relevant campaign-attached search begin in the watched page."
|
|
549
|
+
},
|
|
523
550
|
{
|
|
524
551
|
"action": "render_find_leads_progress",
|
|
525
552
|
"requiredVisibleContent": [
|
|
@@ -555,7 +582,7 @@
|
|
|
555
582
|
"action": "run_subskill",
|
|
556
583
|
"target": "find-leads",
|
|
557
584
|
"mode": "campaign-attached-required",
|
|
558
|
-
"sourceScoutRule": "Shell-first flow requires the CampaignOffer campaignId from durable state. Pass campaignId as campaignOfferId into every provider prompt/search that can persist source state (`get_provider_prompt({ provider, campaignOfferId, confirmed: true })`, `search_signals`, `search_sales_nav`, `search_prospeo`) so the user can watch the selected source inside the campaign. The later import_leads call must use the same campaignOfferId. When several source lanes are credible, scout them independently, then write one primary source recommendation and any runner-up tradeoffs. Do not import, confirm, enrich, queue, or start leads during source discovery."
|
|
585
|
+
"sourceScoutRule": "Shell-first flow requires the CampaignOffer campaignId from durable state. Pass campaignId as campaignOfferId into every provider prompt/search that can persist source state (`get_provider_prompt({ provider, campaignOfferId, confirmed: true })`, `search_signals`, `search_sales_nav`, `search_prospeo`) and include currentStep for tools that accept it so the user can watch the selected source inside the campaign. In watch mode, run the first campaign-attached provider search in the parent thread before waiting on background scouts; subagents can explore runner-up lanes after the visible campaign tab exists. The later import_leads call must use the same campaignOfferId. When several source lanes are credible, scout them independently, then write one primary source recommendation and any runner-up tradeoffs. Do not import, confirm, enrich, queue, or start leads during source discovery."
|
|
559
586
|
},
|
|
560
587
|
{
|
|
561
588
|
"action": "optional_debug_artifacts",
|
|
@@ -725,11 +752,11 @@
|
|
|
725
752
|
"approved brief and real sample leads",
|
|
726
753
|
"no import or enrichment until rubrics and message set are ready",
|
|
727
754
|
"parallel only if real parallel branches were launched",
|
|
728
|
-
"import the first
|
|
755
|
+
"import the first 15-row review batch before fit/message work",
|
|
729
756
|
"selectedLeadListId stays the source list and workflowTableId is the campaign table"
|
|
730
757
|
],
|
|
731
758
|
"timeEstimate": "~2-3 min",
|
|
732
|
-
"chatRenderRule": "Lead source is set. Next import and confirm only the first
|
|
759
|
+
"chatRenderRule": "Lead source is set. Next import and confirm only the first 15-row review batch into the campaign table. After workflowTableId exists, run the fit-filter and message-generation workstreams from the campaign table sample. If real parallel MCP/tool branches or host subagents were actually launched, say so; otherwise say the branches will run sequentially. Never claim parallelism unless parallel execution actually started."
|
|
733
760
|
},
|
|
734
761
|
{
|
|
735
762
|
"action": "ask_continue_revise_or_confirm_only_if_needed",
|
|
@@ -808,7 +835,7 @@
|
|
|
808
835
|
},
|
|
809
836
|
{
|
|
810
837
|
"id": "auto-execute-leads",
|
|
811
|
-
"label": "Step 13: Confirm source and import
|
|
838
|
+
"label": "Step 13: Confirm source and import 15 lead review batch",
|
|
812
839
|
"currentStepValue": "confirm-lead-list",
|
|
813
840
|
"reference": "references/step-13-import-leads.md",
|
|
814
841
|
"onEnter": [
|
|
@@ -848,7 +875,7 @@
|
|
|
848
875
|
"campaignOfferId",
|
|
849
876
|
"targetLeadCount"
|
|
850
877
|
],
|
|
851
|
-
"targetLeadCountFromConfig": "import.importLimit (
|
|
878
|
+
"targetLeadCountFromConfig": "import.importLimit (15)",
|
|
852
879
|
"onZeroLeads": "escalate_hard_fail",
|
|
853
880
|
"modeAddHandshake": {
|
|
854
881
|
"firstCallReturns": "needsModeSelection=true + existingLeadListId",
|
|
@@ -872,7 +899,7 @@
|
|
|
872
899
|
},
|
|
873
900
|
{
|
|
874
901
|
"tool": "confirm_lead_list",
|
|
875
|
-
"targetLeadCountFromConfig": "import.importLimit (
|
|
902
|
+
"targetLeadCountFromConfig": "import.importLimit (15)",
|
|
876
903
|
"sourceBranches": [
|
|
877
904
|
"normal-discovery",
|
|
878
905
|
"supplied-domains",
|
|
@@ -896,7 +923,7 @@
|
|
|
896
923
|
"tool": "get_rows_minimal",
|
|
897
924
|
"purpose": "read_imported_review_batch_from_workflowTableId",
|
|
898
925
|
"stripCarryData": true,
|
|
899
|
-
"sampleSizeFromConfig": "sample.sampleSize (
|
|
926
|
+
"sampleSizeFromConfig": "sample.sampleSize (15)"
|
|
900
927
|
},
|
|
901
928
|
{
|
|
902
929
|
"tool": "update_campaign",
|
|
@@ -999,7 +1026,7 @@
|
|
|
999
1026
|
"ownership": "proof inventory, token strategy, angle drafting, skeptical-prospect review, and selected winner only"
|
|
1000
1027
|
}
|
|
1001
1028
|
],
|
|
1002
|
-
"earlyMessageStartRule": "After auto-execute-leads confirms the
|
|
1029
|
+
"earlyMessageStartRule": "After auto-execute-leads confirms the 15-row review batch and workflowTableId is ready, launch message-generation from brief.md, lead-review.md, lead-sample.json, and the imported campaign table sample. The message worker carries the campaign-launch message rules in its agent prompt; parent-thread fallback loads references/message-review-safety-gate.md. Legacy/resume preview prep still requires at least 3 probable good-fit rows. It may run beside filter-leads, but the cascade cannot queue until save_rubrics and update_campaign_brief both succeed.",
|
|
1003
1030
|
"finalMessageReconcileRule": "message-validation.md may start before lead-filter.md exists, but before message-review it must cite only imported review-batch rows that still pass lead-filter.md. If the selected winner depends on a row later excluded by lead-filter.md, revise message-generation before message review.",
|
|
1004
1031
|
"claudeRule": "In Claude Code, launch both returned post-find-leads scouts with Task/Agent subagents in the same assistant message only when the current session lists those agent names. Do not run filter first and then message generation unless subagents/background work are unavailable.",
|
|
1005
1032
|
"codexRule": "In Codex, launch both returned post-find-leads scout names as disjoint subagents in the same assistant turn only when the host exposes those custom agents for this run. If the host cannot spawn them, run the same branches sequentially and say so.",
|
|
@@ -1879,7 +1906,7 @@
|
|
|
1879
1906
|
},
|
|
1880
1907
|
{
|
|
1881
1908
|
"id": "validate-sample",
|
|
1882
|
-
"label": "Step 14: Validate
|
|
1909
|
+
"label": "Step 14: Validate 15-lead test batch",
|
|
1883
1910
|
"currentStepValue": "validate-sample",
|
|
1884
1911
|
"reference": "references/sample-validation-loop.md",
|
|
1885
1912
|
"counter": {
|
|
@@ -1895,13 +1922,13 @@
|
|
|
1895
1922
|
},
|
|
1896
1923
|
{
|
|
1897
1924
|
"tool": "queue_cells",
|
|
1898
|
-
"cellSource": "
|
|
1899
|
-
"batchSize":
|
|
1925
|
+
"cellSource": "fifteen_lead_review_batch_after_rubrics_and_message_set",
|
|
1926
|
+
"batchSize": 15,
|
|
1900
1927
|
"requiredBeforeCall": [
|
|
1901
1928
|
"rubrics_saved_to_campaign",
|
|
1902
1929
|
"message_set_synced_to_brief"
|
|
1903
1930
|
],
|
|
1904
|
-
"note": "Queue only the
|
|
1931
|
+
"note": "Queue only the 15-lead review batch after rubrics are saved and the approved message set is in campaignBrief. This starts the workflow-table cascade for DNC Check -> ICP Score -> Passes Rubric -> Generate Message."
|
|
1905
1932
|
},
|
|
1906
1933
|
{
|
|
1907
1934
|
"tool": "wait_for_campaign_table_ready",
|
|
@@ -1911,7 +1938,7 @@
|
|
|
1911
1938
|
"tool": "get_rows_minimal",
|
|
1912
1939
|
"purpose": "read_passes_rubric_and_generate_message_status",
|
|
1913
1940
|
"stripCarryData": true,
|
|
1914
|
-
"sampleSizeFromConfig": "sample.sampleSize (
|
|
1941
|
+
"sampleSizeFromConfig": "sample.sampleSize (15)"
|
|
1915
1942
|
},
|
|
1916
1943
|
{
|
|
1917
1944
|
"tool": "wait_for_rubric_results",
|
|
@@ -1922,7 +1949,7 @@
|
|
|
1922
1949
|
"requiredValues": {
|
|
1923
1950
|
"includeRows": false
|
|
1924
1951
|
},
|
|
1925
|
-
"note": "The shell-first flow tests
|
|
1952
|
+
"note": "The shell-first flow tests 15 leads first; always pass cohortSize explicitly instead of relying on default 25 behavior.",
|
|
1926
1953
|
"readVia": "stats_only_tool_result",
|
|
1927
1954
|
"extractFields": [
|
|
1928
1955
|
"ready",
|
|
@@ -2009,7 +2036,7 @@
|
|
|
2009
2036
|
},
|
|
2010
2037
|
{
|
|
2011
2038
|
"id": "auto-execute-messaging",
|
|
2012
|
-
"label": "Step 15: Complete messages for
|
|
2039
|
+
"label": "Step 15: Complete messages for 15-lead test batch",
|
|
2013
2040
|
"currentStepValue": "auto-execute-messaging",
|
|
2014
2041
|
"references": [
|
|
2015
2042
|
"references/parallel-critique-protocol.md",
|
|
@@ -15,7 +15,7 @@ packet.
|
|
|
15
15
|
|
|
16
16
|
The approval gate used to be the place where the user authorized
|
|
17
17
|
spend/send-adjacent mutation. It is now compatibility-only. New runs import the
|
|
18
|
-
bounded
|
|
18
|
+
bounded 15-row review batch after source approval, save rubrics and the approved
|
|
19
19
|
message template after post-import review, and only then queue the cascade.
|
|
20
20
|
|
|
21
21
|
If `campaign-shell.json` exists, a draft `CampaignOffer` row already exists but
|
|
@@ -47,7 +47,7 @@ Supplied-source preview:
|
|
|
47
47
|
- `normal-discovery` is the default when the user does not already know the
|
|
48
48
|
people/accounts.
|
|
49
49
|
- `supplied-linkedin-profiles` uses `load_csv_linkedin_leads` before the
|
|
50
|
-
|
|
50
|
+
15-lead import batch only as preview/source attachment. Pre-import calls
|
|
51
51
|
must omit provider-import parameters. Use `campaignOfferId` only to attach the
|
|
52
52
|
preview/list choice to the campaign UI.
|
|
53
53
|
- `supplied-domains` uses `load_csv_domains` to create or reuse a
|
|
@@ -6,7 +6,7 @@ on every revision round.
|
|
|
6
6
|
|
|
7
7
|
## Principle
|
|
8
8
|
|
|
9
|
-
We spend a bounded review batch (default
|
|
9
|
+
We spend a bounded review batch (default 15 rows) to prove fit before the
|
|
10
10
|
user spends credits on hundreds more leads. The sample loop has one job:
|
|
11
11
|
answer the question "do we have enough real passing examples for the user to
|
|
12
12
|
judge this campaign?"
|
|
@@ -19,7 +19,7 @@ auto-revise leads.
|
|
|
19
19
|
## Inputs
|
|
20
20
|
|
|
21
21
|
- `CampaignOffer.currentStep === "validate-sample"` (set at end of Step 13)
|
|
22
|
-
- Imported review batch from Step 13 (size: `importLimit`, default
|
|
22
|
+
- Imported review batch from Step 13 (size: `importLimit`, default 15)
|
|
23
23
|
- Config from `auto-execute.yaml`: `sample.sampleSize`,
|
|
24
24
|
`sample.minProjectedPass`, `sample.maxRevisionRounds`
|
|
25
25
|
- Persisted counter `revisionRound` (starts at 0 on first entry; persists
|
|
@@ -51,7 +51,7 @@ auto-revise leads.
|
|
|
51
51
|
6. wait_for_rubric_results(sample, targetCount = <cohortSize>)
|
|
52
52
|
- cohortSize = stats.totalRows of the enrichment batch, or the
|
|
53
53
|
imported batch count
|
|
54
|
-
- default targetCount=
|
|
54
|
+
- default targetCount=15 matches the default review batch, but pass the
|
|
55
55
|
explicit batch count anyway so future larger expansion batches do not
|
|
56
56
|
accidentally stop early
|
|
57
57
|
(see §Known Tool Behaviors #3)
|
|
@@ -161,8 +161,8 @@ company, enrichCellId, enrichStatus) over the default shape.
|
|
|
161
161
|
|
|
162
162
|
### 5. `wait_for_rubric_results` can timeout with enough signal to decide
|
|
163
163
|
|
|
164
|
-
Observed: a
|
|
165
|
-
and partial stats such as
|
|
164
|
+
Observed: a 15-row review batch may return `ready=false`, `reason="timeout"`,
|
|
165
|
+
and partial stats such as 13/15 scored, 2 passing, 2 messages generated. That is
|
|
166
166
|
enough to diagnose an underperforming sample. Waiting again without active
|
|
167
167
|
processing makes the experience feel frozen.
|
|
168
168
|
|
|
@@ -178,16 +178,16 @@ or wait once only if active processing is still visible.
|
|
|
178
178
|
projectedPass = round(passInSample / sampleSize * importLimit)
|
|
179
179
|
```
|
|
180
180
|
|
|
181
|
-
Worked examples with defaults (sampleSize=
|
|
181
|
+
Worked examples with defaults (sampleSize=15, importLimit=15):
|
|
182
182
|
|
|
183
|
-
| passInSample | projectedPass | Handoff? (minProjectedPass=
|
|
183
|
+
| passInSample | projectedPass | Handoff? (minProjectedPass=3) |
|
|
184
184
|
| ------------ | ------------- | ----------------------------- |
|
|
185
185
|
| 0 | 0 | No — escalate or revise |
|
|
186
|
-
|
|
|
187
|
-
|
|
|
186
|
+
| 2 | 2 | No — escalate or revise |
|
|
187
|
+
| 3 | 3 | Yes — exactly at floor |
|
|
188
|
+
| 4 | 4 | Yes |
|
|
188
189
|
| 10 | 10 | Yes |
|
|
189
190
|
| 15 | 15 | Yes |
|
|
190
|
-
| 25 | 25 | Yes |
|
|
191
191
|
|
|
192
192
|
When non-default importLimit/sampleSize are configured, the math scales
|
|
193
193
|
the same way. In the default review-batch mode, the sample size equals the
|
|
@@ -251,8 +251,8 @@ rate is zero with no pattern that brief editing could fix.
|
|
|
251
251
|
|
|
252
252
|
Signal examples:
|
|
253
253
|
|
|
254
|
-
-
|
|
255
|
-
-
|
|
254
|
+
- 15/15 rows are wrong function / wrong seniority / wrong geo.
|
|
255
|
+
- 12/15 rows enrich to empty strings for the tokens the brief needs.
|
|
256
256
|
- Rubric passes 0 rows and the failure reasons are all "not ICP."
|
|
257
257
|
- **Marketplace supply-side contamination:** >20% of sample rows match
|
|
258
258
|
the brief §14 forbidden side (e.g. Skillsync sample contains
|
|
@@ -95,7 +95,7 @@ Default case — no existing lead list bound to the campaign:
|
|
|
95
95
|
```text
|
|
96
96
|
import_leads({
|
|
97
97
|
campaignOfferId,
|
|
98
|
-
targetLeadCount: <importLimit from auto-execute.yaml; default
|
|
98
|
+
targetLeadCount: <importLimit from auto-execute.yaml; default 15>
|
|
99
99
|
})
|
|
100
100
|
```
|
|
101
101
|
|
|
@@ -69,7 +69,7 @@ orient the user after a meaningful step change:
|
|
|
69
69
|
- source selected: the campaign should point at the primary provider step
|
|
70
70
|
- rubrics saved: the campaign should show filter/rubric state
|
|
71
71
|
- message set approved: the campaign should show message state
|
|
72
|
-
-
|
|
72
|
+
- 15-lead test batch imported/generated: the campaign should show the test rows
|
|
73
73
|
- settings handoff: the campaign should open to Settings for sender attachment
|
|
74
74
|
- running: the campaign is live
|
|
75
75
|
|