@sellable/mcp 0.1.93 → 0.1.95
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/context.d.ts +16 -0
- package/dist/tools/context.js +16 -0
- package/dist/tools/navigation.d.ts +33 -0
- package/dist/tools/navigation.js +161 -10
- package/dist/tools/readiness.d.ts +48 -0
- package/package.json +1 -1
- package/skills/create-campaign/SKILL.md +58 -13
- package/skills/create-campaign-v2/SKILL.md +79 -63
- package/skills/create-campaign-v2/SOUL.md +9 -7
- package/skills/create-campaign-v2/core/flow.v2.json +78 -36
- package/skills/create-campaign-v2/references/approval-gate-framing.md +5 -4
- package/skills/create-campaign-v2/references/filter-leads.md +4 -3
- package/skills/create-campaign-v2/references/final-handoff-contract.md +5 -4
- package/skills/create-campaign-v2/references/step-13-import-leads.md +3 -3
- package/skills/create-campaign-v2/references/validation-criteria.md +3 -2
- package/skills/create-campaign-v2/references/watch-link-handoff.md +7 -5
- package/skills/create-campaign-v2-tail/SKILL.md +5 -4
package/dist/tools/context.d.ts
CHANGED
|
@@ -68,6 +68,22 @@ export declare function getCampaignContext(input: GetCampaignContextInput): Prom
|
|
|
68
68
|
expectedHeadlessStep: string | null;
|
|
69
69
|
headlessCurrentStep: string | null;
|
|
70
70
|
uiStep: string | null;
|
|
71
|
+
orientation: {
|
|
72
|
+
visibleStep: string | null;
|
|
73
|
+
browserStepLabel: string;
|
|
74
|
+
customerSummary: string;
|
|
75
|
+
nextVisibleMilestone: string;
|
|
76
|
+
blockedReason: string | null;
|
|
77
|
+
agentGuidance: string;
|
|
78
|
+
browserCheckpoint: string;
|
|
79
|
+
senderSelectionAllowed: boolean;
|
|
80
|
+
safeToAskSender: boolean;
|
|
81
|
+
};
|
|
82
|
+
watchUrl: {
|
|
83
|
+
urlPresent: boolean;
|
|
84
|
+
signed: boolean;
|
|
85
|
+
redirectPath: string | null;
|
|
86
|
+
};
|
|
71
87
|
table: {
|
|
72
88
|
workflowTableId: string | null;
|
|
73
89
|
checked: boolean;
|
package/dist/tools/context.js
CHANGED
|
@@ -165,6 +165,22 @@ export function markCampaignContextDirty(campaignId, reason) {
|
|
|
165
165
|
expectedHeadlessStep: "create-offer",
|
|
166
166
|
headlessCurrentStep: null,
|
|
167
167
|
uiStep: "plan",
|
|
168
|
+
orientation: {
|
|
169
|
+
visibleStep: "plan",
|
|
170
|
+
browserStepLabel: "Campaign brief",
|
|
171
|
+
customerSummary: "The campaign view is waiting for saved campaign context.",
|
|
172
|
+
nextVisibleMilestone: "Lead source search",
|
|
173
|
+
blockedReason: "Missing campaignContextNotLoaded",
|
|
174
|
+
agentGuidance: "Refresh campaign navigation state before describing the browser.",
|
|
175
|
+
browserCheckpoint: "brief",
|
|
176
|
+
senderSelectionAllowed: false,
|
|
177
|
+
safeToAskSender: false,
|
|
178
|
+
},
|
|
179
|
+
watchUrl: {
|
|
180
|
+
urlPresent: false,
|
|
181
|
+
signed: false,
|
|
182
|
+
redirectPath: null,
|
|
183
|
+
},
|
|
168
184
|
table: {
|
|
169
185
|
workflowTableId: null,
|
|
170
186
|
checked: false,
|
|
@@ -25,6 +25,7 @@ export type CampaignOfferNavigation = {
|
|
|
25
25
|
campaignStatus?: string | null;
|
|
26
26
|
status?: string | null;
|
|
27
27
|
currentStep?: string | null;
|
|
28
|
+
watchUrl?: string | null;
|
|
28
29
|
};
|
|
29
30
|
type NavigationDebugPayload = Record<string, unknown>;
|
|
30
31
|
export declare function logNavigationDebug(event: string, payload?: NavigationDebugPayload, campaignId?: string | null): void;
|
|
@@ -67,6 +68,22 @@ export declare function computeCampaignNavigationStateFromCampaign(campaign: Cam
|
|
|
67
68
|
expectedHeadlessStep: string | null;
|
|
68
69
|
headlessCurrentStep: string | null;
|
|
69
70
|
uiStep: string | null;
|
|
71
|
+
orientation: {
|
|
72
|
+
visibleStep: string | null;
|
|
73
|
+
browserStepLabel: string;
|
|
74
|
+
customerSummary: string;
|
|
75
|
+
nextVisibleMilestone: string;
|
|
76
|
+
blockedReason: string | null;
|
|
77
|
+
agentGuidance: string;
|
|
78
|
+
browserCheckpoint: string;
|
|
79
|
+
senderSelectionAllowed: boolean;
|
|
80
|
+
safeToAskSender: boolean;
|
|
81
|
+
};
|
|
82
|
+
watchUrl: {
|
|
83
|
+
urlPresent: boolean;
|
|
84
|
+
signed: boolean;
|
|
85
|
+
redirectPath: string | null;
|
|
86
|
+
};
|
|
70
87
|
table: {
|
|
71
88
|
workflowTableId: string | null;
|
|
72
89
|
checked: boolean;
|
|
@@ -95,6 +112,22 @@ export declare function getCampaignNavigationState(input: GetCampaignNavigationS
|
|
|
95
112
|
expectedHeadlessStep: string | null;
|
|
96
113
|
headlessCurrentStep: string | null;
|
|
97
114
|
uiStep: string | null;
|
|
115
|
+
orientation: {
|
|
116
|
+
visibleStep: string | null;
|
|
117
|
+
browserStepLabel: string;
|
|
118
|
+
customerSummary: string;
|
|
119
|
+
nextVisibleMilestone: string;
|
|
120
|
+
blockedReason: string | null;
|
|
121
|
+
agentGuidance: string;
|
|
122
|
+
browserCheckpoint: string;
|
|
123
|
+
senderSelectionAllowed: boolean;
|
|
124
|
+
safeToAskSender: boolean;
|
|
125
|
+
};
|
|
126
|
+
watchUrl: {
|
|
127
|
+
urlPresent: boolean;
|
|
128
|
+
signed: boolean;
|
|
129
|
+
redirectPath: string | null;
|
|
130
|
+
};
|
|
98
131
|
table: {
|
|
99
132
|
workflowTableId: string | null;
|
|
100
133
|
checked: boolean;
|
package/dist/tools/navigation.js
CHANGED
|
@@ -301,6 +301,120 @@ function mapHeadlessToUiStep(step) {
|
|
|
301
301
|
}
|
|
302
302
|
return null;
|
|
303
303
|
}
|
|
304
|
+
function describeVisibleStep(step) {
|
|
305
|
+
switch (step) {
|
|
306
|
+
case "plan":
|
|
307
|
+
return {
|
|
308
|
+
label: "Campaign brief",
|
|
309
|
+
summary: "The campaign brief is visible in the campaign view.",
|
|
310
|
+
nextMilestone: "Lead source search",
|
|
311
|
+
guidance: "Confirm the browser-visible campaign state before moving to lead sourcing.",
|
|
312
|
+
checkpoint: "brief",
|
|
313
|
+
};
|
|
314
|
+
case "pick-provider":
|
|
315
|
+
case "contact-search":
|
|
316
|
+
case "signal-discovery":
|
|
317
|
+
return {
|
|
318
|
+
label: "Lead sourcing",
|
|
319
|
+
summary: "Lead source selection or search is visible.",
|
|
320
|
+
nextMilestone: "Lead preview",
|
|
321
|
+
guidance: "Explain the selected lead-source lane and visible search state before continuing.",
|
|
322
|
+
checkpoint: "lead-source",
|
|
323
|
+
};
|
|
324
|
+
case "leads":
|
|
325
|
+
return {
|
|
326
|
+
label: "Lead import",
|
|
327
|
+
summary: "The lead preview or imported review batch is visible.",
|
|
328
|
+
nextMilestone: "Fit filtering and message review",
|
|
329
|
+
guidance: "Confirm the browser-visible campaign state and row batch before filter/message work.",
|
|
330
|
+
checkpoint: "import",
|
|
331
|
+
};
|
|
332
|
+
case "filter-choice":
|
|
333
|
+
case "filter-rules":
|
|
334
|
+
case "filter-leads":
|
|
335
|
+
return {
|
|
336
|
+
label: "Fit filtering",
|
|
337
|
+
summary: "Fit filtering or sample validation is visible.",
|
|
338
|
+
nextMilestone: "Message review or validation",
|
|
339
|
+
guidance: "Explain the visible filter or validation state and wait for the next approved step.",
|
|
340
|
+
checkpoint: "filters",
|
|
341
|
+
};
|
|
342
|
+
case "messages":
|
|
343
|
+
return {
|
|
344
|
+
label: "Message review",
|
|
345
|
+
summary: "Message review or generation is visible.",
|
|
346
|
+
nextMilestone: "10-row validation",
|
|
347
|
+
guidance: "Explain the visible message state before asking for approval or validation.",
|
|
348
|
+
checkpoint: "messages",
|
|
349
|
+
};
|
|
350
|
+
case "settings":
|
|
351
|
+
return {
|
|
352
|
+
label: "Settings",
|
|
353
|
+
summary: "Settings is visible. Sender selection belongs here.",
|
|
354
|
+
nextMilestone: "Safe sender selection",
|
|
355
|
+
guidance: "Select a safe connected sender here only; stop before sequence/start/live send in UAT.",
|
|
356
|
+
checkpoint: "settings",
|
|
357
|
+
};
|
|
358
|
+
case "sequence":
|
|
359
|
+
return {
|
|
360
|
+
label: "Sequence setup",
|
|
361
|
+
summary: "Sequence setup is visible.",
|
|
362
|
+
nextMilestone: "Explicit launch approval",
|
|
363
|
+
guidance: "Do not attach or change sequence state during watch UAT unless explicitly approved.",
|
|
364
|
+
checkpoint: "sequence",
|
|
365
|
+
};
|
|
366
|
+
case "send":
|
|
367
|
+
return {
|
|
368
|
+
label: "Send review",
|
|
369
|
+
summary: "Send review or running status is visible.",
|
|
370
|
+
nextMilestone: "Explicit launch approval",
|
|
371
|
+
guidance: "Do not start the campaign or trigger live send without explicit user confirmation.",
|
|
372
|
+
checkpoint: "send",
|
|
373
|
+
};
|
|
374
|
+
default:
|
|
375
|
+
return {
|
|
376
|
+
label: "Campaign view",
|
|
377
|
+
summary: "The campaign view is loading or waiting for the next saved step.",
|
|
378
|
+
nextMilestone: "Next saved campaign step",
|
|
379
|
+
guidance: "Inspect the browser-visible campaign state before continuing with more tools.",
|
|
380
|
+
checkpoint: "unknown",
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
function parseSignedWatchUrl(watchUrl, campaignId) {
|
|
385
|
+
if (!watchUrl) {
|
|
386
|
+
return {
|
|
387
|
+
urlPresent: false,
|
|
388
|
+
signed: false,
|
|
389
|
+
redirectPath: null,
|
|
390
|
+
warning: null,
|
|
391
|
+
};
|
|
392
|
+
}
|
|
393
|
+
try {
|
|
394
|
+
const url = new URL(watchUrl);
|
|
395
|
+
const redirect = url.searchParams.get("redirect");
|
|
396
|
+
const decodedRedirect = redirect ? decodeURIComponent(redirect) : null;
|
|
397
|
+
const signed = url.pathname === "/auth/continue" &&
|
|
398
|
+
Boolean(url.searchParams.get("token")) &&
|
|
399
|
+
decodedRedirect === `/campaign-builder/${campaignId}?mode=claude`;
|
|
400
|
+
return {
|
|
401
|
+
urlPresent: true,
|
|
402
|
+
signed,
|
|
403
|
+
redirectPath: decodedRedirect,
|
|
404
|
+
warning: signed
|
|
405
|
+
? null
|
|
406
|
+
: "Watch URL is not a signed /auth/continue watch link for /campaign-builder/{campaignId}?mode=claude.",
|
|
407
|
+
};
|
|
408
|
+
}
|
|
409
|
+
catch {
|
|
410
|
+
return {
|
|
411
|
+
urlPresent: true,
|
|
412
|
+
signed: false,
|
|
413
|
+
redirectPath: null,
|
|
414
|
+
warning: "Watch URL is malformed; recover a fresh signed /auth/continue watch link before browser handoff.",
|
|
415
|
+
};
|
|
416
|
+
}
|
|
417
|
+
}
|
|
304
418
|
export const navigationToolDefinitions = [
|
|
305
419
|
{
|
|
306
420
|
name: "get_campaign_navigation_state",
|
|
@@ -401,30 +515,49 @@ export function computeCampaignNavigationStateFromCampaign(campaign, options) {
|
|
|
401
515
|
if (!blockedAt && evaluateTailState) {
|
|
402
516
|
computedStep = isRunningCampaign(campaign) ? "running" : "send";
|
|
403
517
|
}
|
|
404
|
-
const
|
|
518
|
+
const stepForHeadless = blockedAt ?? computedStep;
|
|
519
|
+
const expectedHeadlessStep = stepForHeadless === "campaign-created"
|
|
405
520
|
? "create-offer"
|
|
406
|
-
:
|
|
521
|
+
: stepForHeadless === "provider-search"
|
|
407
522
|
? providerConfig?.currentStep || campaign.leadSourceProvider || null
|
|
408
|
-
:
|
|
523
|
+
: stepForHeadless === "filter-rules"
|
|
409
524
|
? "create-icp-rubric"
|
|
410
|
-
:
|
|
525
|
+
: stepForHeadless === "messages"
|
|
411
526
|
? "messages"
|
|
412
|
-
:
|
|
527
|
+
: stepForHeadless === "settings"
|
|
413
528
|
? "settings"
|
|
414
|
-
:
|
|
529
|
+
: stepForHeadless === "sequence"
|
|
415
530
|
? "sequence"
|
|
416
|
-
:
|
|
531
|
+
: stepForHeadless === "running"
|
|
417
532
|
? "running"
|
|
418
|
-
:
|
|
533
|
+
: stepForHeadless;
|
|
534
|
+
const currentUiStep = mapHeadlessToUiStep(campaign.currentStep);
|
|
535
|
+
const expectedUiStep = mapHeadlessToUiStep(expectedHeadlessStep);
|
|
419
536
|
const stepAligned = !campaign.currentStep || !expectedHeadlessStep
|
|
420
537
|
? null
|
|
421
|
-
: campaign.currentStep === expectedHeadlessStep
|
|
538
|
+
: campaign.currentStep === expectedHeadlessStep ||
|
|
539
|
+
(currentUiStep !== null && currentUiStep === expectedUiStep);
|
|
422
540
|
if (stepAligned === false && campaign.currentStep && expectedHeadlessStep) {
|
|
423
541
|
warnings.push(`Headless step mismatch: expected "${expectedHeadlessStep}" but campaign.currentStep is "${campaign.currentStep}".`);
|
|
424
542
|
}
|
|
425
543
|
if (!providerConfig && campaign.leadSourceProvider) {
|
|
426
544
|
warnings.push(`Provider config not found for "${campaign.leadSourceProvider}".`);
|
|
427
545
|
}
|
|
546
|
+
const uiStep = currentUiStep;
|
|
547
|
+
const orientationStep = uiStep || mapHeadlessToUiStep(expectedHeadlessStep);
|
|
548
|
+
const orientationSource = isRunningCampaign(campaign)
|
|
549
|
+
? {
|
|
550
|
+
label: "Running campaign",
|
|
551
|
+
summary: "The campaign is running and the send review is visible.",
|
|
552
|
+
nextMilestone: "Explicit launch approval",
|
|
553
|
+
guidance: "Confirm the browser-visible campaign state before describing the running status.",
|
|
554
|
+
checkpoint: "send",
|
|
555
|
+
}
|
|
556
|
+
: describeVisibleStep(orientationStep);
|
|
557
|
+
const watchUrlState = parseSignedWatchUrl(campaign.watchUrl, campaign.id);
|
|
558
|
+
if (watchUrlState.warning) {
|
|
559
|
+
warnings.push(watchUrlState.warning);
|
|
560
|
+
}
|
|
428
561
|
const result = {
|
|
429
562
|
campaignId: campaign.id,
|
|
430
563
|
computedStep,
|
|
@@ -432,7 +565,23 @@ export function computeCampaignNavigationStateFromCampaign(campaign, options) {
|
|
|
432
565
|
missing,
|
|
433
566
|
expectedHeadlessStep,
|
|
434
567
|
headlessCurrentStep: campaign.currentStep ?? null,
|
|
435
|
-
uiStep
|
|
568
|
+
uiStep,
|
|
569
|
+
orientation: {
|
|
570
|
+
visibleStep: orientationStep,
|
|
571
|
+
browserStepLabel: orientationSource.label,
|
|
572
|
+
customerSummary: orientationSource.summary,
|
|
573
|
+
nextVisibleMilestone: orientationSource.nextMilestone,
|
|
574
|
+
blockedReason: missing.length > 0 ? `Missing ${missing.join(", ")}` : null,
|
|
575
|
+
agentGuidance: orientationSource.guidance,
|
|
576
|
+
browserCheckpoint: orientationSource.checkpoint,
|
|
577
|
+
senderSelectionAllowed: orientationStep === "settings",
|
|
578
|
+
safeToAskSender: orientationStep === "settings",
|
|
579
|
+
},
|
|
580
|
+
watchUrl: {
|
|
581
|
+
urlPresent: watchUrlState.urlPresent,
|
|
582
|
+
signed: watchUrlState.signed,
|
|
583
|
+
redirectPath: watchUrlState.redirectPath,
|
|
584
|
+
},
|
|
436
585
|
table: {
|
|
437
586
|
workflowTableId: campaign.workflowTableId ?? null,
|
|
438
587
|
checked: options.tableChecked,
|
|
@@ -461,6 +610,8 @@ export function computeCampaignNavigationStateFromCampaign(campaign, options) {
|
|
|
461
610
|
expectedHeadlessStep: result.expectedHeadlessStep,
|
|
462
611
|
headlessCurrentStep: result.headlessCurrentStep,
|
|
463
612
|
uiStep: result.uiStep,
|
|
613
|
+
orientation: result.orientation,
|
|
614
|
+
watchUrl: result.watchUrl,
|
|
464
615
|
table: result.table,
|
|
465
616
|
context: result.context,
|
|
466
617
|
warnings: result.warnings,
|
|
@@ -121,6 +121,22 @@ export declare function waitForCampaignTableReady(input: WaitForCampaignTableRea
|
|
|
121
121
|
expectedHeadlessStep: string | null;
|
|
122
122
|
headlessCurrentStep: string | null;
|
|
123
123
|
uiStep: string | null;
|
|
124
|
+
orientation: {
|
|
125
|
+
visibleStep: string | null;
|
|
126
|
+
browserStepLabel: string;
|
|
127
|
+
customerSummary: string;
|
|
128
|
+
nextVisibleMilestone: string;
|
|
129
|
+
blockedReason: string | null;
|
|
130
|
+
agentGuidance: string;
|
|
131
|
+
browserCheckpoint: string;
|
|
132
|
+
senderSelectionAllowed: boolean;
|
|
133
|
+
safeToAskSender: boolean;
|
|
134
|
+
};
|
|
135
|
+
watchUrl: {
|
|
136
|
+
urlPresent: boolean;
|
|
137
|
+
signed: boolean;
|
|
138
|
+
redirectPath: string | null;
|
|
139
|
+
};
|
|
124
140
|
table: {
|
|
125
141
|
workflowTableId: string | null;
|
|
126
142
|
checked: boolean;
|
|
@@ -154,6 +170,22 @@ export declare function waitForCampaignTableReady(input: WaitForCampaignTableRea
|
|
|
154
170
|
expectedHeadlessStep: string | null;
|
|
155
171
|
headlessCurrentStep: string | null;
|
|
156
172
|
uiStep: string | null;
|
|
173
|
+
orientation: {
|
|
174
|
+
visibleStep: string | null;
|
|
175
|
+
browserStepLabel: string;
|
|
176
|
+
customerSummary: string;
|
|
177
|
+
nextVisibleMilestone: string;
|
|
178
|
+
blockedReason: string | null;
|
|
179
|
+
agentGuidance: string;
|
|
180
|
+
browserCheckpoint: string;
|
|
181
|
+
senderSelectionAllowed: boolean;
|
|
182
|
+
safeToAskSender: boolean;
|
|
183
|
+
};
|
|
184
|
+
watchUrl: {
|
|
185
|
+
urlPresent: boolean;
|
|
186
|
+
signed: boolean;
|
|
187
|
+
redirectPath: string | null;
|
|
188
|
+
};
|
|
157
189
|
table: {
|
|
158
190
|
workflowTableId: string | null;
|
|
159
191
|
checked: boolean;
|
|
@@ -189,6 +221,22 @@ export declare function waitForCampaignTableReady(input: WaitForCampaignTableRea
|
|
|
189
221
|
expectedHeadlessStep: string | null;
|
|
190
222
|
headlessCurrentStep: string | null;
|
|
191
223
|
uiStep: string | null;
|
|
224
|
+
orientation: {
|
|
225
|
+
visibleStep: string | null;
|
|
226
|
+
browserStepLabel: string;
|
|
227
|
+
customerSummary: string;
|
|
228
|
+
nextVisibleMilestone: string;
|
|
229
|
+
blockedReason: string | null;
|
|
230
|
+
agentGuidance: string;
|
|
231
|
+
browserCheckpoint: string;
|
|
232
|
+
senderSelectionAllowed: boolean;
|
|
233
|
+
safeToAskSender: boolean;
|
|
234
|
+
};
|
|
235
|
+
watchUrl: {
|
|
236
|
+
urlPresent: boolean;
|
|
237
|
+
signed: boolean;
|
|
238
|
+
redirectPath: string | null;
|
|
239
|
+
};
|
|
192
240
|
table: {
|
|
193
241
|
workflowTableId: string | null;
|
|
194
242
|
checked: boolean;
|
package/package.json
CHANGED
|
@@ -66,10 +66,10 @@ allowed-tools:
|
|
|
66
66
|
|
|
67
67
|
Use this as the customer-facing entrypoint for Sellable campaign creation.
|
|
68
68
|
|
|
69
|
-
CampaignOffer state
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
state first. The
|
|
69
|
+
CampaignOffer state and the watch link are the customer-facing source of truth.
|
|
70
|
+
Disk artifacts are optional debug/UAT diagnostics; normal customer runs should
|
|
71
|
+
not create, link, or surface local draft files unless the user explicitly asks
|
|
72
|
+
for them. Resume, gating, and handoff read campaign state first. The
|
|
73
73
|
watchable campaign exists after the short brief; lead import is bounded to the
|
|
74
74
|
first review batch, and enrichment/message generation waits until rubrics and
|
|
75
75
|
the approved message template are saved on the campaign.
|
|
@@ -147,18 +147,56 @@ Use rendered Markdown for user review surfaces, not fenced code blocks. Keep
|
|
|
147
147
|
lines short, use indexed section labels and bullets, and translate internal
|
|
148
148
|
sourcing terms into plain language.
|
|
149
149
|
|
|
150
|
-
Every approval gate must include
|
|
151
|
-
content. Show a short `
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
content in chat.
|
|
150
|
+
Every approval gate must include live campaign access after the readable inline
|
|
151
|
+
content. Show a short `Watch link:` line once the campaign shell exists. In
|
|
152
|
+
normal customer runs, do not show `Open artifact:` lines, raw filesystem paths,
|
|
153
|
+
or local draft filenames. Local artifacts are debug/UAT-only unless the user asks
|
|
154
|
+
for them. The link is for deeper inspection; never use it as a substitute for
|
|
155
|
+
showing the content in chat.
|
|
157
156
|
|
|
158
157
|
Never mention MCP namespaces, prompt chunking, plugin cache paths, missing
|
|
159
158
|
linked skill versions, runbooks, or local skill files in normal customer-facing
|
|
160
159
|
copy.
|
|
161
160
|
|
|
161
|
+
## Codex Watch Browser Handoff
|
|
162
|
+
|
|
163
|
+
When a campaign tool returns `watchUrl`, treat it as an active browser handoff,
|
|
164
|
+
not only a URL to print. A valid handoff link must be a signed
|
|
165
|
+
`/auth/continue?token=...&redirect=/campaign-builder/{campaignId}?mode=claude`
|
|
166
|
+
URL. `create_campaign.watchUrl`, `create_campaign({ campaignId }).watchUrl`,
|
|
167
|
+
and `get_campaign.watchUrl` are all acceptable only when they return that signed
|
|
168
|
+
shape.
|
|
169
|
+
|
|
170
|
+
In Codex Desktop, when in-app browser control is available, open the returned watch link on the user's behalf. After opening it, inspect the browser-visible campaign state, then explain what the user is seeing before continuing with more campaign tools. Use customer language such as:
|
|
171
|
+
|
|
172
|
+
```text
|
|
173
|
+
I’ll open the campaign view and keep it in sync as I build.
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
If browser control is unavailable, say so briefly and provide the watch link.
|
|
177
|
+
In VPS or other off-desktop Codex runs, this fallback is the expected handoff for
|
|
178
|
+
the local headed watch observer: make the signed link easy to copy/open, then
|
|
179
|
+
continue with explicit chat progress and `get_campaign_navigation_state`
|
|
180
|
+
orientation checks.
|
|
181
|
+
Use this fallback shape:
|
|
182
|
+
|
|
183
|
+
```text
|
|
184
|
+
I can’t inspect the campaign browser from this Codex session, so I’ll keep the
|
|
185
|
+
chat steps explicit here. Watch link: {watchUrl}
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
If opening the watch link lands on an auth, 404, permission, blank, or visible
|
|
189
|
+
error state, report only what is visible, recover a fresh signed watch link once
|
|
190
|
+
with `create_campaign({ campaignId })` or `get_campaign`, and try that link. Do
|
|
191
|
+
not claim the browser was opened, inspected, or synchronized unless the visible
|
|
192
|
+
browser state was actually observed. Do not claim the browser was opened unless
|
|
193
|
+
it was opened and observed.
|
|
194
|
+
|
|
195
|
+
After every `update_campaign({ campaignId, currentStep })`, use
|
|
196
|
+
`get_campaign_navigation_state` when available as a compact orientation check:
|
|
197
|
+
match the browser-visible step to the saved campaign state, explain the current
|
|
198
|
+
state in one sentence, and only then continue. Sender selection belongs at Settings after message approval and 10-row validation. Do not attach a sequence, start the campaign, or trigger a live send unless the user explicitly confirms that launch action outside UAT.
|
|
199
|
+
|
|
162
200
|
## Names To Use
|
|
163
201
|
|
|
164
202
|
Use these exact public names so Claude Code and Codex do not drift:
|
|
@@ -503,9 +541,16 @@ updates.
|
|
|
503
541
|
`message-validation.md` proves the message-review safety-gate workflow ran,
|
|
504
542
|
`message-review.md` approves the template, rubrics are saved, and the
|
|
505
543
|
approved message set is synced into the campaign brief.
|
|
506
|
-
6.
|
|
544
|
+
6. The main thread owns watch navigation. Call
|
|
545
|
+
`mcp__sellable__update_campaign({ campaignId, currentStep })` before major
|
|
546
|
+
visible work so the user can watch progress in the app: `create-offer` for
|
|
547
|
+
the brief, `pick-provider` or the selected provider step while sourcing,
|
|
548
|
+
`filter-choice` after the 10-row review batch, `messages` or
|
|
549
|
+
`auto-execute-messaging` for message work, `validate-sample` for validation,
|
|
550
|
+
and `awaiting-user-greenlight` for Settings. Do not advance the step backward.
|
|
551
|
+
7. Keep `selectedLeadListId` as the source list and `workflowTableId` as the
|
|
507
552
|
campaign table. Do not use disk files as the post-mint source of truth.
|
|
508
|
-
|
|
553
|
+
8. Do not ask the user to run another command.
|
|
509
554
|
|
|
510
555
|
## Fallback
|
|
511
556
|
|
|
@@ -31,19 +31,21 @@ Run the configured JSON flow in durable stages:
|
|
|
31
31
|
8. queue and validate the 10-row cascade
|
|
32
32
|
9. settings/sender, sequence, and start handoff
|
|
33
33
|
|
|
34
|
-
The JSON flow is the source of truth for stage order,
|
|
35
|
-
`producesArtifacts`, `allowedTools`, `doNotAllow`,
|
|
36
|
-
`transitions`.
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
34
|
+
The JSON flow is the source of truth for stage order, artifact policy,
|
|
35
|
+
`requiredArtifacts`, `producesArtifacts`, `allowedTools`, `doNotAllow`,
|
|
36
|
+
`waitFor`, and `transitions`. In normal customer runs, obey
|
|
37
|
+
`artifactPolicy.normalCustomerPath`: use campaign state and MCP responses instead
|
|
38
|
+
of local draft files. This prompt explains how to execute those gates; it does
|
|
39
|
+
not replace them.
|
|
40
|
+
|
|
41
|
+
CampaignOffer state and the watch link are the customer-facing source of truth.
|
|
42
|
+
Disk artifacts are optional debug/UAT diagnostics only. In normal customer runs,
|
|
43
|
+
do not create, link, or surface local draft files unless the user explicitly asks
|
|
44
|
+
for debug artifacts or the run is an explicit UAT/rehearsal. Resume, gating, and
|
|
41
45
|
handoff read campaign state first: campaign id, watch URL, campaign brief,
|
|
42
46
|
currentStep, provider/search association, selectedLeadListId as the source
|
|
43
47
|
list, workflowTableId as the campaign table, saved rubrics, approved message
|
|
44
|
-
template, senderIds, sequence template, and running state.
|
|
45
|
-
single customer-facing debug copy of the brief; the campaign brief field is the
|
|
46
|
-
durable downstream campaign thesis input.
|
|
48
|
+
template, senderIds, sequence template, and running state.
|
|
47
49
|
|
|
48
50
|
There is no separate approval-packet, commit-gate, or mint step in the active
|
|
49
51
|
shell-first flow. The campaign exists early for watch mode. The hard boundary
|
|
@@ -54,7 +56,7 @@ are saved, and the approved message set is synced into the campaign brief.
|
|
|
54
56
|
|
|
55
57
|
<files>
|
|
56
58
|
|
|
57
|
-
|
|
59
|
+
Optional debug/UAT draft directory, disabled in normal customer runs:
|
|
58
60
|
|
|
59
61
|
```text
|
|
60
62
|
.sellable/create-campaign-v2/drafts/{workspace-slug}/{campaign-slug}/
|
|
@@ -88,9 +90,17 @@ Validated draft directory:
|
|
|
88
90
|
`watchUrl`; when the user opens it they should see the brief, not an empty
|
|
89
91
|
campaign. If shell creation fails, stop and surface the error. There is no
|
|
90
92
|
no-shell mint fallback in the active shell-first flow.
|
|
93
|
+
- The main thread owns watch navigation. Before expensive work in each major
|
|
94
|
+
stage, call `update_campaign({ campaignId, currentStep })` with the visible UI
|
|
95
|
+
step the user should be watching, then continue the work from MCP/product
|
|
96
|
+
state. Use `create-offer` for the brief, `pick-provider` or the selected
|
|
97
|
+
provider step while sourcing, `filter-choice` after the 10-row review batch is
|
|
98
|
+
imported, `messages`/`auto-execute-messaging` for message work,
|
|
99
|
+
`validate-sample` for the 10-row validation cascade, and
|
|
100
|
+
`awaiting-user-greenlight` for Settings. Do not advance `currentStep` backward.
|
|
91
101
|
- After the shell is minted, normal resume paths read the CampaignOffer first.
|
|
92
102
|
Use debug files to explain or reconstruct work, not to decide whether a
|
|
93
|
-
post-mint gate is allowed.
|
|
103
|
+
post-mint gate is allowed. validation/debug artifacts are not durable campaign
|
|
94
104
|
state.
|
|
95
105
|
- The campaign shell may receive setup state before import: approved brief text,
|
|
96
106
|
campaign-attached provider searches/selections, the bounded review-batch
|
|
@@ -110,7 +120,7 @@ Validated draft directory:
|
|
|
110
120
|
`request_user_input` (enabled in Default mode by
|
|
111
121
|
`[features].default_mode_request_user_input = true`, not available in
|
|
112
122
|
`codex exec`). Treat them as equivalent approval/intake gates and persist the
|
|
113
|
-
same
|
|
123
|
+
same campaign state after the user answers. Use this structured gate only for
|
|
114
124
|
single-choice decisions with fixed options or approval gates. Every campaign
|
|
115
125
|
setup gate must be single-choice in both hosts: set or assume
|
|
116
126
|
`multiSelect: false`, use mutually exclusive option labels, and do not ask
|
|
@@ -163,13 +173,13 @@ Validated draft directory:
|
|
|
163
173
|
or safe launch. Do not say "persist", "local draft folder", "artifact",
|
|
164
174
|
"mkdir", "campaign thesis", or "same approved campaign thesis" in
|
|
165
175
|
customer-facing progress copy.
|
|
166
|
-
- Every approval gate must include
|
|
167
|
-
content. Show a short `
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
176
|
+
- Every approval gate must include live campaign access after the readable inline
|
|
177
|
+
content. Show a short `Watch link:` line with the campaign link when a
|
|
178
|
+
campaign shell exists. In normal customer runs, do not show `Open artifact:`
|
|
179
|
+
lines, raw filesystem paths, or local draft filenames. If the run is explicit
|
|
180
|
+
debug/UAT or the user asks for local artifacts, artifact links may be shown as
|
|
181
|
+
secondary diagnostics. The link is never a substitute for rendering the
|
|
182
|
+
decision in chat.
|
|
173
183
|
- Any time the user is reviewing a list or decision, use rendered Markdown with
|
|
174
184
|
short indexed sections and bullets. Do not use label-plus-paragraph blocks
|
|
175
185
|
like `Key numbers:` followed by one long paragraph. Do not use fenced code
|
|
@@ -274,8 +284,9 @@ Validated draft directory:
|
|
|
274
284
|
Before that first
|
|
275
285
|
structured question gate, do not run source discovery, Sales Nav, Prospeo,
|
|
276
286
|
Signals, Bash, Read, Write, Edit, Glob, Grep, full company research, or
|
|
277
|
-
draft-directory inspection/creation.
|
|
278
|
-
|
|
287
|
+
draft-directory inspection/creation. In normal customer runs, do not create a
|
|
288
|
+
draft directory after the founder answers; create/update the campaign shell and
|
|
289
|
+
campaign state instead. After launch identity and any ambiguous campaign focus are
|
|
279
290
|
confirmed, the setup packet must
|
|
280
291
|
use the structured question gate and ask buyer, offer/CTA, proof, and lead
|
|
281
292
|
source. All four questions must include an `Other / custom` option.
|
|
@@ -314,14 +325,16 @@ Validated draft directory:
|
|
|
314
325
|
Settings/final handoff.
|
|
315
326
|
|
|
316
327
|
- Before asking for brief approval, render a slim approval brief in the chat and
|
|
317
|
-
|
|
328
|
+
write the rich current brief into `CampaignOffer.campaignBrief` through
|
|
329
|
+
`create_campaign`/`update_campaign`. Use rendered Markdown directly:
|
|
318
330
|
`##` heading, bold field labels, numbered section labels, and short bullets.
|
|
319
331
|
Do not wrap the user-facing approval view in a fenced code block because that
|
|
320
332
|
creates horizontal scrolling in Claude Code and Codex. Do not ask the user to
|
|
321
333
|
approve a one-paragraph direction, a risk note, or hidden artifact they cannot
|
|
322
334
|
inspect. The chat approval view should be skimmable in under 45 seconds; full
|
|
323
|
-
reasoning, source notes, and robustness stay in
|
|
324
|
-
sections, with short wrapped
|
|
335
|
+
reasoning, source notes, and robustness can stay in optional debug artifacts
|
|
336
|
+
only during explicit debug/UAT runs. Include these sections, with short wrapped
|
|
337
|
+
lines:
|
|
325
338
|
|
|
326
339
|
```text
|
|
327
340
|
## Campaign brief
|
|
@@ -378,21 +391,19 @@ brief`, `Revise target`, `Revise offer/proof`, and `Other / custom`.
|
|
|
378
391
|
not visible in this session, use `Approve brief + compare source paths`
|
|
379
392
|
instead. The customer approves the sourcing decision, not the host
|
|
380
393
|
implementation detail.
|
|
381
|
-
Include
|
|
382
|
-
The visible brief must come before
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
brief
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
the visible approved brief; if the sidecar is still running, wait quietly or
|
|
395
|
-
finish the write synchronously at that boundary.
|
|
394
|
+
Include a `Watch link:` line before the approval question once
|
|
395
|
+
`create_campaign` has returned `watchUrl`. The visible brief must come before
|
|
396
|
+
any optional debug persistence. After the brief is synthesized, render the
|
|
397
|
+
approval-ready brief in chat and create/update the campaign shell before any
|
|
398
|
+
visible `mkdir`, `Write`, artifact-copy, or similar local draft setup. In
|
|
399
|
+
normal customer runs, skip local draft setup entirely. File/folder creation is
|
|
400
|
+
not the user's value moment; the live campaign brief is.
|
|
401
|
+
|
|
402
|
+
The approval question can be based on the rendered brief in chat and the live
|
|
403
|
+
campaign brief. Do not wait for file-write chrome before asking for approval.
|
|
404
|
+
Before lead sourcing, call `update_campaign({ campaignId, campaignBrief,
|
|
405
|
+
currentStep: "pick-provider" })` after approval so the watch link moves out of
|
|
406
|
+
Plan while the main thread compares source paths.
|
|
396
407
|
|
|
397
408
|
- After the brief is approved, show the next progress line:
|
|
398
409
|
`Cool. Now I'm going to find people who are both a good fit and active enough
|
|
@@ -418,26 +429,30 @@ message we should test.`
|
|
|
418
429
|
show you the draft next so you can approve it or change it.
|
|
419
430
|
```
|
|
420
431
|
|
|
421
|
-
- In
|
|
432
|
+
- In explicit debug/UAT runs, `Bash` is only for safe local draft-directory
|
|
422
433
|
housekeeping before the import/test batch: `mkdir`, `ls`, `find`, `test`, `pwd`, `echo`,
|
|
423
|
-
or `cat` inside the repo.
|
|
434
|
+
or `cat` inside the repo. In normal customer runs, do not use Bash for local
|
|
435
|
+
draft setup. Do not use
|
|
424
436
|
Bash to run interpreters/scripts (`python`, `node`, `npm`, `pnpm`, `yarn`,
|
|
425
437
|
`npx`), call APIs, query databases, inspect prompt dumps, run git, or
|
|
426
438
|
synthesize artifact content. Use `Read`/`Write`/`Edit` for artifact files and
|
|
427
439
|
MCP tools for product actions.
|
|
428
|
-
- `
|
|
429
|
-
`
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
440
|
+
- `CampaignOffer.campaignBrief` is the stable downstream thesis source.
|
|
441
|
+
`brief.md` is an optional debug/UAT copy only; never make it the user-facing
|
|
442
|
+
approval surface in normal customer runs.
|
|
443
|
+
- The lead-source decision and lead sample must be rendered in chat and attached
|
|
444
|
+
to campaign state/search state. `lead-review.md` and `lead-sample.json` are
|
|
445
|
+
optional debug/UAT outputs of `find leads`; when they exist, describe source
|
|
446
|
+
paths as provider lanes checked (Signals, Sales Nav, Prospeo) and never
|
|
447
|
+
include custom agent names, host availability, install state, allowed-tool
|
|
448
|
+
state, or fallback implementation details.
|
|
435
449
|
- `lead-filter.md` is the primary output of `filter leads`.
|
|
436
450
|
- `rubric.json` is optional and secondary to `lead-filter.md`.
|
|
437
451
|
- `message-prep.md` is an optional speed artifact produced by the explicit
|
|
438
452
|
message path after find-leads. It can prepare proof inventory, buyer
|
|
439
453
|
objections, CTA options, gold-standard strategy maps, and candidate angles
|
|
440
|
-
from
|
|
454
|
+
from the live campaign brief, source decision, lead sample, and optional debug
|
|
455
|
+
copies.
|
|
441
456
|
- `message-candidate-drafts.md` is an optional provisional speed artifact from
|
|
442
457
|
sample rows that already look like probable good fits. It can contain rough
|
|
443
458
|
candidate messages and element tests, but it cannot select the final winner
|
|
@@ -448,10 +463,10 @@ message we should test.`
|
|
|
448
463
|
message quality gate outputs between `message-validation.md` and
|
|
449
464
|
`approval-packet.md`.
|
|
450
465
|
- Run the dependency chain as a DAG: `create-campaign-brief` -> `find leads` ->
|
|
451
|
-
bounded import/confirm. Once
|
|
452
|
-
|
|
466
|
+
bounded import/confirm. Once the source decision, lead sample, and campaign
|
|
467
|
+
table `workflowTableId` exist, the normal path is the
|
|
453
468
|
`post-lead-workstreams` step. Launch `filter leads` and `message generation`
|
|
454
|
-
from the same basis (
|
|
469
|
+
from the same basis (live campaign brief, source decision, lead sample, and
|
|
455
470
|
the imported review-batch rows) as separate workstreams when the host exposes
|
|
456
471
|
the named Sellable post-find-leads agents or real background work.
|
|
457
472
|
First call `get_post_find_leads_scout_registry` and use the returned
|
|
@@ -466,8 +481,8 @@ message we should test.`
|
|
|
466
481
|
resume targets.
|
|
467
482
|
- Message generation does not need `lead-filter.md` to start. The moment the
|
|
468
483
|
bounded review batch is imported and `workflowTableId` is ready, start the
|
|
469
|
-
message-generation workstream from
|
|
470
|
-
|
|
484
|
+
message-generation workstream from the live campaign brief, source decision,
|
|
485
|
+
lead sample, and the imported campaign table sample. It can prepare
|
|
471
486
|
proof inventory, token strategy, and candidate angles while filter-leads
|
|
472
487
|
tightens keep/exclude rules. Approval still waits for both `lead-filter.md`
|
|
473
488
|
and `message-validation.md`, then reconciles that the selected message basis
|
|
@@ -512,10 +527,11 @@ workstreams`, `in parallel`, or `background` unless parallel branches were
|
|
|
512
527
|
available. This is the two Task/Agent subagents path for the
|
|
513
528
|
`post-lead-workstreams` step. This is the same registry pattern as source scouting, but the
|
|
514
529
|
trigger is source approval and the join gate is both `lead-filter.md` and
|
|
515
|
-
`message-validation.md` existing from the same
|
|
516
|
-
|
|
517
|
-
- Never run a downstream stage until the active `flow.v2.json` step's
|
|
518
|
-
|
|
530
|
+
`message-validation.md` existing from the same live campaign brief/source
|
|
531
|
+
decision/lead-sample basis.
|
|
532
|
+
- Never run a downstream stage until the active `flow.v2.json` step's required
|
|
533
|
+
campaign state and required normal inputs exist. In normal customer runs, do
|
|
534
|
+
not create optional debug artifacts just to satisfy a file gate.
|
|
519
535
|
- Never call a tool outside the active step's `allowedTools`, and never call a
|
|
520
536
|
tool listed in the active step's `doNotAllow`.
|
|
521
537
|
- Before the source is approved, `import_leads` and `confirm_lead_list` are
|
|
@@ -922,7 +938,7 @@ user-facing lead review. The visible response must include:
|
|
|
922
938
|
usable prospects per post, and use/discard decision.
|
|
923
939
|
- `Sample leads` with 3-5 representative `Name — Title, Company` rows
|
|
924
940
|
- `Tradeoff`
|
|
925
|
-
- `
|
|
941
|
+
- `Watch link: {watchUrl}` when the campaign shell exists
|
|
926
942
|
|
|
927
943
|
The first sentence of the visible decision must make the actual choice clear:
|
|
928
944
|
`I recommend {primary source} using {exact filter/source recipe}. The runner-up
|
|
@@ -948,9 +964,9 @@ fresh, high-density posts when available, sample engagers, show fit rate, then
|
|
|
948
964
|
state how many additional posts could be added/scraped if that first sample is
|
|
949
965
|
good but volume is low.
|
|
950
966
|
|
|
951
|
-
Keep discarded paths
|
|
952
|
-
|
|
953
|
-
the user asks.
|
|
967
|
+
Keep discarded paths and full sample rows in the campaign/source decision
|
|
968
|
+
context. In explicit debug/UAT runs they may also be copied into
|
|
969
|
+
`lead-sample.json`. Do not show raw filesystem paths unless the user asks.
|
|
954
970
|
|
|
955
971
|
For supplied profile CSVs and existing lead lists, `lead-review.md` must not
|
|
956
972
|
describe a generic TAM estimate or pretend the rows came from Sales Nav/Prospeo
|
|
@@ -60,10 +60,11 @@ Approvals only feel safe when the user can see what they are approving. Before
|
|
|
60
60
|
any approve/revise question, show the relevant decision in plain language. For a
|
|
61
61
|
brief approval, render the brief itself, not just a direction summary.
|
|
62
62
|
|
|
63
|
-
Every approval should also give the user a way to inspect the
|
|
64
|
-
After the readable inline content, include
|
|
65
|
-
|
|
66
|
-
|
|
63
|
+
Every approval should also give the user a way to inspect the live campaign.
|
|
64
|
+
After the readable inline content, include a `Watch link:` line when a campaign
|
|
65
|
+
shell exists. Local artifacts are optional debug/UAT diagnostics only; do not
|
|
66
|
+
surface `Open artifact:` links, file paths, or local draft filenames in normal
|
|
67
|
+
customer runs.
|
|
67
68
|
|
|
68
69
|
This applies especially to message approvals. Never ask someone to approve a
|
|
69
70
|
message they cannot see. Show the subject, tokenized template, a filled sample
|
|
@@ -138,9 +139,10 @@ because the campaign state is what the lead source, filters, and messages will
|
|
|
138
139
|
build from. I’ll show you the draft next so you can approve it or change it.
|
|
139
140
|
```
|
|
140
141
|
|
|
141
|
-
CampaignOffer state
|
|
142
|
-
|
|
143
|
-
|
|
142
|
+
CampaignOffer state and the watch link are canonical. Disk artifacts are
|
|
143
|
+
optional debug/UAT diagnostics. Normal customer runs should read, resume, gate,
|
|
144
|
+
and hand off from campaign state first, and should not expose local draft files
|
|
145
|
+
unless the user asks for them.
|
|
144
146
|
|
|
145
147
|
Good opening:
|
|
146
148
|
|
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": "v2",
|
|
3
3
|
"workflow": "create-campaign-v2",
|
|
4
|
-
"principle": "CampaignOffer state
|
|
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 10-row cascade and hand off to settings/sequence/start.",
|
|
5
|
+
"artifactPolicy": {
|
|
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
|
+
"debugArtifactsAreOptional": true,
|
|
8
|
+
"requiredArtifactsApplyWhen": "legacy resume, fixture validation, or explicit debug/UAT mode only",
|
|
9
|
+
"customerFacingAccess": "Watch link and live campaign currentStep"
|
|
10
|
+
},
|
|
5
11
|
"commitGateChoices": [
|
|
6
12
|
"approve",
|
|
7
13
|
"revise-brief",
|
|
@@ -211,7 +217,7 @@
|
|
|
211
217
|
"target": "create-campaign-brief"
|
|
212
218
|
},
|
|
213
219
|
{
|
|
214
|
-
"action": "
|
|
220
|
+
"action": "render_brief_preview_before_debug_artifact_write",
|
|
215
221
|
"requiredVisibleContent": [
|
|
216
222
|
"Campaign brief",
|
|
217
223
|
"Decision",
|
|
@@ -224,20 +230,21 @@
|
|
|
224
230
|
"What happens next"
|
|
225
231
|
],
|
|
226
232
|
"mustRunBefore": [
|
|
227
|
-
"
|
|
233
|
+
"optional_debug_artifact_writer",
|
|
228
234
|
"Bash",
|
|
229
235
|
"mkdir"
|
|
230
236
|
],
|
|
231
|
-
"chatRenderRule": "After synthesizing the brief, render the approval-ready brief in chat before any
|
|
237
|
+
"chatRenderRule": "After synthesizing the brief, render the approval-ready brief in chat before any optional local folder creation, file write, or artifact copy. Normal customer runs should not create local draft files here. The user should see useful brief content and live campaign state, not local persistence chrome. Do not ask the user to approve a hidden artifact."
|
|
232
238
|
},
|
|
233
239
|
{
|
|
234
|
-
"action": "
|
|
240
|
+
"action": "optional_debug_artifact_writer",
|
|
235
241
|
"artifact": "brief.md",
|
|
236
|
-
"mode": "
|
|
242
|
+
"mode": "debug_or_uat_only",
|
|
243
|
+
"shouldNotRunInNormalCustomerPath": true,
|
|
237
244
|
"shouldNotBlockMainThread": true,
|
|
238
245
|
"allowedToRunWhileUserReviewsInlineBrief": true,
|
|
239
|
-
"fallback": "If no background writer is available, write brief.md synchronously only after the approval-ready brief is already visible in chat.",
|
|
240
|
-
"chatRenderRule": "Do not surface folder/file-write progress in chat unless the write fails or the user asks. The main thread should keep the brief review moving
|
|
246
|
+
"fallback": "If debug/UAT artifacts are requested and no background writer is available, write brief.md synchronously only after the approval-ready brief is already visible in chat. In normal customer runs, skip the write.",
|
|
247
|
+
"chatRenderRule": "Do not surface folder/file-write progress in chat unless the write fails or the user asks. The main thread should keep the brief review moving from campaign state."
|
|
241
248
|
},
|
|
242
249
|
{
|
|
243
250
|
"action": "create_watchable_campaign_shell_with_v1_brief",
|
|
@@ -300,14 +307,19 @@
|
|
|
300
307
|
"campaignBrief"
|
|
301
308
|
],
|
|
302
309
|
"watchUrlSource": "create_campaign.watchUrl",
|
|
310
|
+
"requiredWatchUrlShape": "signed /auth/continue?token=...&redirect=/campaign-builder/{campaignId}?mode=claude",
|
|
311
|
+
"codexBrowserHandoff": {
|
|
312
|
+
"openWhenAvailable": true,
|
|
313
|
+
"inspectBeforeContinuing": true,
|
|
314
|
+
"explainVisibleStateBeforeContinuing": true,
|
|
315
|
+
"fallbackMustNotClaimInspection": true,
|
|
316
|
+
"recoverFreshSignedLinkOnceOnOpenFailure": true
|
|
317
|
+
},
|
|
303
318
|
"immediateNextMainChatLine": "Cool, let's find leads."
|
|
304
319
|
}
|
|
305
320
|
],
|
|
306
321
|
"requiredArtifacts": [],
|
|
307
|
-
"producesArtifacts": [
|
|
308
|
-
"brief.md",
|
|
309
|
-
"campaign-shell.json"
|
|
310
|
-
],
|
|
322
|
+
"producesArtifacts": [],
|
|
311
323
|
"allowedTools": [
|
|
312
324
|
"get_subskill_prompt",
|
|
313
325
|
"get_subskill_asset",
|
|
@@ -359,7 +371,8 @@
|
|
|
359
371
|
"onEnter": [
|
|
360
372
|
{
|
|
361
373
|
"action": "show_brief_summary",
|
|
362
|
-
"
|
|
374
|
+
"source": "campaignBrief",
|
|
375
|
+
"fallbackDebugArtifact": "brief.md"
|
|
363
376
|
},
|
|
364
377
|
{
|
|
365
378
|
"action": "render_brief_approval_checkpoint",
|
|
@@ -378,10 +391,15 @@
|
|
|
378
391
|
"then I will find good-fit leads"
|
|
379
392
|
],
|
|
380
393
|
"minimumVisibleBriefDetail": "full_readable_brief_before_question",
|
|
381
|
-
"
|
|
382
|
-
"
|
|
394
|
+
"requiredCampaignLinks": [
|
|
395
|
+
"watchUrl"
|
|
396
|
+
],
|
|
397
|
+
"campaignLinkTiming": "before_approval_question",
|
|
398
|
+
"forbiddenNormalCustomerLinks": [
|
|
399
|
+
"brief.md",
|
|
400
|
+
"local draft path",
|
|
401
|
+
"Open artifact:"
|
|
383
402
|
],
|
|
384
|
-
"artifactLinkTiming": "when_ready_before_downstream",
|
|
385
403
|
"avoidQuestionWhenOnlyUsefulAnswerIs": "looks good"
|
|
386
404
|
},
|
|
387
405
|
{
|
|
@@ -397,13 +415,14 @@
|
|
|
397
415
|
}
|
|
398
416
|
},
|
|
399
417
|
{
|
|
400
|
-
"action": "
|
|
418
|
+
"action": "optional_debug_artifact_sync",
|
|
401
419
|
"artifacts": [
|
|
402
420
|
"brief.md"
|
|
403
421
|
],
|
|
404
|
-
"
|
|
405
|
-
"
|
|
406
|
-
"
|
|
422
|
+
"debugOrUatOnly": true,
|
|
423
|
+
"requiredBeforeTransition": false,
|
|
424
|
+
"reconcileRule": "If debug/UAT artifacts are enabled, verify the sidecar persisted the same brief shown in chat. Normal customer lead sourcing reads CampaignOffer.campaignBrief, not brief.md.",
|
|
425
|
+
"chatRenderRule": "Do not block the brief approval question or find-leads transition on this step. Only mention artifact persistence if debug/UAT mode fails or the user asks."
|
|
407
426
|
},
|
|
408
427
|
{
|
|
409
428
|
"action": "sync_approved_brief_to_campaign_shell",
|
|
@@ -415,7 +434,7 @@
|
|
|
415
434
|
"when": "after_user_brief_confirmed_or_auto_continue",
|
|
416
435
|
"fields": [
|
|
417
436
|
"campaignBrief",
|
|
418
|
-
"currentStep:
|
|
437
|
+
"currentStep:pick-provider"
|
|
419
438
|
],
|
|
420
439
|
"fallback": "If campaignId is missing from CampaignOffer state, stop; shell-first flow requires an existing campaign before sourcing leads.",
|
|
421
440
|
"readsCampaignStateFirst": true,
|
|
@@ -425,9 +444,7 @@
|
|
|
425
444
|
]
|
|
426
445
|
}
|
|
427
446
|
],
|
|
428
|
-
"requiredArtifacts": [
|
|
429
|
-
"brief.md"
|
|
430
|
-
],
|
|
447
|
+
"requiredArtifacts": [],
|
|
431
448
|
"producesArtifacts": [],
|
|
432
449
|
"allowedTools": [
|
|
433
450
|
"AskUserQuestion",
|
|
@@ -467,6 +484,19 @@
|
|
|
467
484
|
"id": "find-leads",
|
|
468
485
|
"label": "Find leads",
|
|
469
486
|
"onEnter": [
|
|
487
|
+
{
|
|
488
|
+
"action": "advance_watch_to_source_selection",
|
|
489
|
+
"tool": "update_campaign",
|
|
490
|
+
"requiredFields": [
|
|
491
|
+
"campaignId",
|
|
492
|
+
"currentStep"
|
|
493
|
+
],
|
|
494
|
+
"requiredValues": {
|
|
495
|
+
"currentStep": "pick-provider"
|
|
496
|
+
},
|
|
497
|
+
"when": "before_source_scouts_or_provider_search",
|
|
498
|
+
"chatRenderRule": "Move the campaign watch view to Find Contacts before the main thread starts comparing source paths. Do not mention MCP or local artifacts."
|
|
499
|
+
},
|
|
470
500
|
{
|
|
471
501
|
"action": "render_find_leads_progress",
|
|
472
502
|
"requiredVisibleContent": [
|
|
@@ -505,20 +535,18 @@
|
|
|
505
535
|
"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."
|
|
506
536
|
},
|
|
507
537
|
{
|
|
508
|
-
"action": "
|
|
538
|
+
"action": "optional_debug_artifacts",
|
|
509
539
|
"artifacts": [
|
|
510
540
|
"lead-review.md",
|
|
511
541
|
"lead-sample.json"
|
|
512
|
-
]
|
|
542
|
+
],
|
|
543
|
+
"debugOrUatOnly": true,
|
|
544
|
+
"shouldNotRunInNormalCustomerPath": true,
|
|
545
|
+
"chatRenderRule": "Do not create or surface local lead-review artifacts in normal customer runs. The main thread renders the source decision in chat and persists provider/source state to the campaign."
|
|
513
546
|
}
|
|
514
547
|
],
|
|
515
|
-
"requiredArtifacts": [
|
|
516
|
-
|
|
517
|
-
],
|
|
518
|
-
"producesArtifacts": [
|
|
519
|
-
"lead-review.md",
|
|
520
|
-
"lead-sample.json"
|
|
521
|
-
],
|
|
548
|
+
"requiredArtifacts": [],
|
|
549
|
+
"producesArtifacts": [],
|
|
522
550
|
"allowedTools": [
|
|
523
551
|
"get_subskill_prompt",
|
|
524
552
|
"get_subskill_asset",
|
|
@@ -1319,7 +1347,7 @@
|
|
|
1319
1347
|
"Question: approve-message or revise-messaging?",
|
|
1320
1348
|
"Recommendation:"
|
|
1321
1349
|
],
|
|
1322
|
-
"chatRenderRule": "Show a slim rendered-Markdown message review, never a fenced code block. Use ## Message review plus indexed sections and short bullets. Show one fully filled sample message with no {{tokens}} before asking for approval. Include subject, sample message, why it should work, one concern or None, recommendation, and
|
|
1350
|
+
"chatRenderRule": "Show a slim rendered-Markdown message review, never a fenced code block. Use ## Message review plus indexed sections and short bullets. Show one fully filled sample message with no {{tokens}} before asking for approval. Include subject, sample message, why it should work, one concern or None, recommendation, and Watch link when a campaign shell exists. Keep tokenized template, token fill basis, rendered examples, good/bad token fill examples, validation notes, and message-validation.md details in campaign state or optional debug artifacts. Do not show plain filesystem paths unless the user asks.",
|
|
1323
1351
|
"allowedRecommendations": [
|
|
1324
1352
|
"approve-message",
|
|
1325
1353
|
"revise-messaging"
|
|
@@ -1395,13 +1423,26 @@
|
|
|
1395
1423
|
"when": "after_message_approved_before_import",
|
|
1396
1424
|
"fields": [
|
|
1397
1425
|
"campaignBrief with ## Approved Message Template, Token Fill Rules, Token Fill Examples",
|
|
1398
|
-
"currentStep:validate-sample",
|
|
1399
1426
|
"useMessagingTemplate:true"
|
|
1400
1427
|
],
|
|
1401
1428
|
"requiredBeforeImport": false,
|
|
1402
1429
|
"onFailure": "stop_before_import_or_enrichment",
|
|
1403
1430
|
"requiredBeforeCascade": true,
|
|
1404
1431
|
"writesCampaignState": "campaignBrief.Approved Message Template"
|
|
1432
|
+
},
|
|
1433
|
+
{
|
|
1434
|
+
"action": "advance_watch_to_validation_after_message_approval",
|
|
1435
|
+
"tool": "update_campaign",
|
|
1436
|
+
"requires": [
|
|
1437
|
+
"campaignId",
|
|
1438
|
+
"message-review-decision.md"
|
|
1439
|
+
],
|
|
1440
|
+
"requiredValues": {
|
|
1441
|
+
"currentStep": "validate-sample"
|
|
1442
|
+
},
|
|
1443
|
+
"when": "after_update_campaign_brief_succeeds",
|
|
1444
|
+
"requiredBeforeCascade": true,
|
|
1445
|
+
"writesCampaignState": "currentStep:validate-sample"
|
|
1405
1446
|
}
|
|
1406
1447
|
],
|
|
1407
1448
|
"requiredArtifacts": [
|
|
@@ -1419,7 +1460,8 @@
|
|
|
1419
1460
|
"AskUserQuestion",
|
|
1420
1461
|
"request_user_input",
|
|
1421
1462
|
"get_subskill_prompt",
|
|
1422
|
-
"update_campaign_brief"
|
|
1463
|
+
"update_campaign_brief",
|
|
1464
|
+
"update_campaign"
|
|
1423
1465
|
],
|
|
1424
1466
|
"doNotAllow": [
|
|
1425
1467
|
"create_campaign",
|
|
@@ -5,10 +5,11 @@ runs do not route through `approval-packet`, `commit-gate`, or `atomic-mint`.
|
|
|
5
5
|
Load this file only when resuming a compatibility run that already has those
|
|
6
6
|
debug artifacts.
|
|
7
7
|
|
|
8
|
-
CampaignOffer state
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
not after this approval
|
|
8
|
+
CampaignOffer state and the watch link are canonical. Disk artifacts are
|
|
9
|
+
optional debug/UAT diagnostics, and normal customer runs should not expose local
|
|
10
|
+
draft files. Resume, gating, and handoff read campaign state first. In new runs,
|
|
11
|
+
the live campaign object appears after the short brief, not after this approval
|
|
12
|
+
packet.
|
|
12
13
|
|
|
13
14
|
## Purpose
|
|
14
15
|
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
# Filter Leads
|
|
2
2
|
|
|
3
|
-
CampaignOffer state
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
CampaignOffer state and the watch link are canonical. Disk artifacts are
|
|
4
|
+
optional debug/UAT diagnostics, and normal customer runs should not expose local
|
|
5
|
+
draft files. Resume, gating, and handoff read campaign state first. Filter design
|
|
6
|
+
may use the live lead sample or optional debug sample, but the active rubric save
|
|
6
7
|
is `save_rubrics({ campaignOfferId, leadScoringRubrics }) after the campaign
|
|
7
8
|
table exists`.
|
|
8
9
|
|
|
@@ -14,10 +14,11 @@ that decision — UI and Claude greenlight — and the skill must handle both
|
|
|
14
14
|
cleanly, including the case where one channel has already started the
|
|
15
15
|
campaign before the other arrives.
|
|
16
16
|
|
|
17
|
-
CampaignOffer state
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
`
|
|
17
|
+
CampaignOffer state and the watch link are canonical. Disk artifacts are
|
|
18
|
+
optional debug/UAT diagnostics, and normal customer runs should not expose local
|
|
19
|
+
draft files. Resume, gating, and handoff read campaign state first. At this
|
|
20
|
+
point, `workflowTableId` is the campaign table, while `selectedLeadListId`
|
|
21
|
+
remains the source list that produced the review batch.
|
|
21
22
|
|
|
22
23
|
## Step 16 Setup
|
|
23
24
|
|
|
@@ -6,9 +6,9 @@ Step 13.
|
|
|
6
6
|
|
|
7
7
|
## Principle
|
|
8
8
|
|
|
9
|
-
CampaignOffer state
|
|
10
|
-
|
|
11
|
-
state first.
|
|
9
|
+
CampaignOffer state and the watch link are canonical. Disk artifacts are
|
|
10
|
+
optional debug/UAT diagnostics, and normal customer runs should not expose local
|
|
11
|
+
draft files. Resume, gating, and handoff read campaign state first.
|
|
12
12
|
|
|
13
13
|
Bounded review-batch sourcing + import happens after the existing campaign
|
|
14
14
|
shell has the selected source attached with `campaignOfferId`. It happens before
|
|
@@ -6,8 +6,9 @@ message direction hold up against real preview evidence.
|
|
|
6
6
|
|
|
7
7
|
## Primary Contract
|
|
8
8
|
|
|
9
|
-
- CampaignOffer state
|
|
10
|
-
- validation/debug artifacts are not durable campaign state.
|
|
9
|
+
- CampaignOffer state and the watch link are canonical.
|
|
10
|
+
- validation/debug artifacts are optional debug/UAT diagnostics, not durable campaign state.
|
|
11
|
+
- normal customer runs should not expose local draft files.
|
|
11
12
|
- resume and handoff read campaign state before debug files.
|
|
12
13
|
- `CampaignOffer.campaignBrief` is the durable Phase 83 thesis input;
|
|
13
14
|
`brief.md` is its debug copy
|
|
@@ -4,9 +4,10 @@ This reference governs how create-campaign-v2 surfaces the watch link in the
|
|
|
4
4
|
shell-first flow. Load it before showing the link and on every resume where the
|
|
5
5
|
link needs to be re-surfaced.
|
|
6
6
|
|
|
7
|
-
CampaignOffer state
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
CampaignOffer state and the watch link are canonical. Disk artifacts are
|
|
8
|
+
optional debug/UAT diagnostics; normal customer runs should not create, link, or
|
|
9
|
+
surface local draft files unless the user explicitly asks for them. Resume,
|
|
10
|
+
gating, and handoff read campaign state first.
|
|
10
11
|
|
|
11
12
|
## Plumbing Reuse
|
|
12
13
|
|
|
@@ -61,8 +62,9 @@ Stop before `save_rubrics` and recover the missing signed URL first.
|
|
|
61
62
|
|
|
62
63
|
## Step Orientation
|
|
63
64
|
|
|
64
|
-
After the shell exists,
|
|
65
|
-
the
|
|
65
|
+
After the shell exists, the main thread must update `currentStep` before each
|
|
66
|
+
meaningful visible stage, then re-surface the same watch link only when it helps
|
|
67
|
+
orient the user after a meaningful step change:
|
|
66
68
|
|
|
67
69
|
- source selected: the campaign should point at the primary provider step
|
|
68
70
|
- rubrics saved: the campaign should show filter/rubric state
|
|
@@ -8,10 +8,11 @@ visibility: internal
|
|
|
8
8
|
|
|
9
9
|
This is the tail detail extracted from the main create-campaign-v2 SKILL.md to keep the entry prompt slim. The agent loads this on-demand BEFORE entering the auto-execute or validate-sample steps. Follow this verbatim.
|
|
10
10
|
|
|
11
|
-
CampaignOffer state
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
campaign
|
|
11
|
+
CampaignOffer state and the watch link are canonical. Disk artifacts are
|
|
12
|
+
optional debug/UAT diagnostics; normal customer runs should not expose local
|
|
13
|
+
draft files. Resume, gating, and handoff read campaign state first.
|
|
14
|
+
selectedLeadListId remains the source list; workflowTableId is the campaign
|
|
15
|
+
table.
|
|
15
16
|
|
|
16
17
|
## MANDATORY TOOL ORDER (read this BEFORE any tail step)
|
|
17
18
|
|