@sellable/mcp 0.1.148 → 0.1.149
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/index-dev.js +0 -0
- package/dist/index.js +0 -0
- package/dist/tools/leads.d.ts +3 -0
- package/dist/tools/leads.js +30 -1
- package/dist/tools/readiness.js +18 -1
- package/dist/tools/registry.d.ts +0 -7
- package/dist/tools/rubrics.d.ts +23 -55
- package/dist/tools/rubrics.js +10 -93
- package/package.json +1 -1
- package/skills/create-campaign-v2/SKILL.md +3 -4
- package/skills/create-campaign-v2/core/flow.v2.json +7 -13
- package/skills/create-campaign-v2/references/sample-validation-loop.md +5 -9
- package/skills/create-campaign-v2/references/step-13-import-leads.md +5 -3
- package/skills/create-campaign-v2/references/step-15-re-cascade.md +7 -12
- package/skills/create-campaign-v2/references/watch-guide-narration.md +4 -5
- package/skills/create-campaign-v2-tail/SKILL.md +16 -24
- package/skills/providers/signal-discovery.md +3 -0
- package/skills/research/config.json +0 -9
package/dist/index-dev.js
CHANGED
|
File without changes
|
package/dist/index.js
CHANGED
|
File without changes
|
package/dist/tools/leads.d.ts
CHANGED
|
@@ -9,6 +9,9 @@ export declare function selectSignalPostsForImport<T extends SignalPostForImport
|
|
|
9
9
|
}): {
|
|
10
10
|
posts: T[];
|
|
11
11
|
estimatedEngagers: number;
|
|
12
|
+
availableEngagers: number;
|
|
13
|
+
targetEngagerCount: number | null;
|
|
14
|
+
targetReached: boolean;
|
|
12
15
|
limited: boolean;
|
|
13
16
|
};
|
|
14
17
|
export type GetProviderPromptInput = {
|
package/dist/tools/leads.js
CHANGED
|
@@ -343,13 +343,18 @@ export function selectSignalPostsForImport(posts, options) {
|
|
|
343
343
|
const normalizedTargetEngagers = normalizePositiveInteger(options.targetEngagerCount);
|
|
344
344
|
const normalizedMaxPosts = normalizePositiveInteger(options.maxPostsToScrape);
|
|
345
345
|
if (!normalizedTargetEngagers && !normalizedMaxPosts) {
|
|
346
|
+
const availableEngagers = posts.reduce((sum, post) => sum + post.likes + post.comments, 0);
|
|
346
347
|
return {
|
|
347
348
|
posts,
|
|
348
|
-
estimatedEngagers:
|
|
349
|
+
estimatedEngagers: availableEngagers,
|
|
350
|
+
availableEngagers,
|
|
351
|
+
targetEngagerCount: null,
|
|
352
|
+
targetReached: true,
|
|
349
353
|
limited: false,
|
|
350
354
|
};
|
|
351
355
|
}
|
|
352
356
|
const ranked = [...posts].sort((a, b) => b.likes + b.comments - (a.likes + a.comments));
|
|
357
|
+
const availableEngagers = ranked.reduce((sum, post) => sum + post.likes + post.comments, 0);
|
|
353
358
|
const selected = [];
|
|
354
359
|
let estimatedEngagers = 0;
|
|
355
360
|
for (const post of ranked) {
|
|
@@ -366,6 +371,10 @@ export function selectSignalPostsForImport(posts, options) {
|
|
|
366
371
|
return {
|
|
367
372
|
posts: selected.length > 0 ? selected : ranked,
|
|
368
373
|
estimatedEngagers,
|
|
374
|
+
availableEngagers,
|
|
375
|
+
targetEngagerCount: normalizedTargetEngagers ?? null,
|
|
376
|
+
targetReached: !normalizedTargetEngagers ||
|
|
377
|
+
estimatedEngagers >= normalizedTargetEngagers,
|
|
369
378
|
limited: selected.length > 0 && selected.length < posts.length,
|
|
370
379
|
};
|
|
371
380
|
}
|
|
@@ -2312,12 +2321,19 @@ export async function importLeads(input) {
|
|
|
2312
2321
|
maxPostsToScrape,
|
|
2313
2322
|
});
|
|
2314
2323
|
const postsToScrape = importSelection.posts;
|
|
2324
|
+
if (!importSelection.targetReached && importSelection.targetEngagerCount) {
|
|
2325
|
+
const capClause = importSelection.availableEngagers >= importSelection.targetEngagerCount
|
|
2326
|
+
? ` The selected posts can cover the target, but maxPostsToScrape=${normalizePositiveInteger(maxPostsToScrape)} prevents reaching it. Increase maxPostsToScrape or remove that cap.`
|
|
2327
|
+
: " Select/promote more right-content posts, run another narrow Signal Discovery search, or switch to Sales Nav recent activity if the lane cannot produce enough source candidates.";
|
|
2328
|
+
throw new Error(`Signal Discovery selected posts only cover about ${importSelection.estimatedEngagers.toLocaleString("en-US")} visible engagers, below the approved ${importSelection.targetEngagerCount.toLocaleString("en-US")} source-candidate target. Do not scrape this under-capacity post set.${capClause}`);
|
|
2329
|
+
}
|
|
2315
2330
|
const effectiveHeadlineICPCriteria = headlineICPCriteria && headlineICPCriteria.length > 0
|
|
2316
2331
|
? headlineICPCriteria
|
|
2317
2332
|
: rubricGuidelines;
|
|
2318
2333
|
// Start the scrape job
|
|
2319
2334
|
const result = await api.post(`/api/v3/campaigns/${campaignOfferId}/signal-leads/create`, {
|
|
2320
2335
|
posts: postsToScrape,
|
|
2336
|
+
targetEngagerCount: importSelection.targetEngagerCount ?? undefined,
|
|
2321
2337
|
...(effectiveHeadlineICPCriteria &&
|
|
2322
2338
|
effectiveHeadlineICPCriteria.length > 0
|
|
2323
2339
|
? {
|
|
@@ -2577,6 +2593,19 @@ export async function confirmLeadList(input) {
|
|
|
2577
2593
|
const leadListConfig = leadListMeta.table?.config ?? null;
|
|
2578
2594
|
const leadListRowCount = leadListMeta.rowCount ?? 0;
|
|
2579
2595
|
const importProgress = leadListConfig?.importProgress ?? null;
|
|
2596
|
+
const signalSourceTargetLeadCount = resolvedProvider === "signal-discovery" &&
|
|
2597
|
+
typeof leadListConfig?.targetLeadCount === "number" &&
|
|
2598
|
+
Number.isFinite(leadListConfig.targetLeadCount) &&
|
|
2599
|
+
leadListConfig.targetLeadCount > 0
|
|
2600
|
+
? leadListConfig.targetLeadCount
|
|
2601
|
+
: null;
|
|
2602
|
+
if (resolvedProvider === "signal-discovery" &&
|
|
2603
|
+
signalSourceTargetLeadCount !== null &&
|
|
2604
|
+
leadListConfig?.importStatus === "complete" &&
|
|
2605
|
+
leadListRowCount > 0 &&
|
|
2606
|
+
leadListRowCount < signalSourceTargetLeadCount) {
|
|
2607
|
+
throw new Error(`Signal Discovery source list is under capacity: it completed with ${leadListRowCount.toLocaleString("en-US")} source candidates, below the approved ${signalSourceTargetLeadCount.toLocaleString("en-US")} source-candidate target. Do not import the bounded review batch from this source. Select more posts, rerun Signal Discovery, or move to Sales Nav/Prospeo.`);
|
|
2608
|
+
}
|
|
2580
2609
|
const progressProcessed = typeof importProgress?.processed === "number"
|
|
2581
2610
|
? importProgress.processed
|
|
2582
2611
|
: null;
|
package/dist/tools/readiness.js
CHANGED
|
@@ -101,7 +101,7 @@ export const readinessToolDefinitions = [
|
|
|
101
101
|
},
|
|
102
102
|
targetLeadCount: {
|
|
103
103
|
type: "number",
|
|
104
|
-
description: "Target number of leads requested. Used as a fallback completion check when status is unavailable.",
|
|
104
|
+
description: "Target number of leads requested. Used as a fallback completion check when status is unavailable. For Signal Discovery, pass the approved source-candidate target; if the completed source list lands below it, the tool returns source_under_capacity instead of ready.",
|
|
105
105
|
},
|
|
106
106
|
timeoutMs: {
|
|
107
107
|
type: "number",
|
|
@@ -472,6 +472,23 @@ export async function waitForLeadListReady(input) {
|
|
|
472
472
|
}
|
|
473
473
|
}
|
|
474
474
|
if ((!requireRows || rowCount > 0) && importComplete) {
|
|
475
|
+
if (provider === "signal-discovery" &&
|
|
476
|
+
typeof targetLeadCount === "number" &&
|
|
477
|
+
rowCount > 0 &&
|
|
478
|
+
rowCount < targetLeadCount) {
|
|
479
|
+
return {
|
|
480
|
+
ready: false,
|
|
481
|
+
reason: "source_under_capacity",
|
|
482
|
+
leadListId,
|
|
483
|
+
provider: provider ?? null,
|
|
484
|
+
attempts,
|
|
485
|
+
elapsedMs: Date.now() - start,
|
|
486
|
+
rowCount,
|
|
487
|
+
status: lastStatus,
|
|
488
|
+
targetLeadCount,
|
|
489
|
+
error: `Signal Discovery completed with ${rowCount.toLocaleString("en-US")} source candidates, below the approved ${targetLeadCount.toLocaleString("en-US")} source-candidate target. Do not confirm this lead list; select more posts, rerun source discovery, or move to Sales Nav/Prospeo.`,
|
|
490
|
+
};
|
|
491
|
+
}
|
|
475
492
|
return {
|
|
476
493
|
ready: true,
|
|
477
494
|
leadListId,
|
package/dist/tools/registry.d.ts
CHANGED
|
@@ -3702,7 +3702,6 @@ export declare const allTools: ({
|
|
|
3702
3702
|
tableId?: undefined;
|
|
3703
3703
|
targetCount?: undefined;
|
|
3704
3704
|
minPassedCount?: undefined;
|
|
3705
|
-
minMessagesCount?: undefined;
|
|
3706
3705
|
timeoutMs?: undefined;
|
|
3707
3706
|
intervalMs?: undefined;
|
|
3708
3707
|
includeRows?: undefined;
|
|
@@ -3736,7 +3735,6 @@ export declare const allTools: ({
|
|
|
3736
3735
|
tableId?: undefined;
|
|
3737
3736
|
targetCount?: undefined;
|
|
3738
3737
|
minPassedCount?: undefined;
|
|
3739
|
-
minMessagesCount?: undefined;
|
|
3740
3738
|
timeoutMs?: undefined;
|
|
3741
3739
|
intervalMs?: undefined;
|
|
3742
3740
|
includeRows?: undefined;
|
|
@@ -3795,7 +3793,6 @@ export declare const allTools: ({
|
|
|
3795
3793
|
tableId?: undefined;
|
|
3796
3794
|
targetCount?: undefined;
|
|
3797
3795
|
minPassedCount?: undefined;
|
|
3798
|
-
minMessagesCount?: undefined;
|
|
3799
3796
|
timeoutMs?: undefined;
|
|
3800
3797
|
intervalMs?: undefined;
|
|
3801
3798
|
includeRows?: undefined;
|
|
@@ -3854,7 +3851,6 @@ export declare const allTools: ({
|
|
|
3854
3851
|
tableId?: undefined;
|
|
3855
3852
|
targetCount?: undefined;
|
|
3856
3853
|
minPassedCount?: undefined;
|
|
3857
|
-
minMessagesCount?: undefined;
|
|
3858
3854
|
timeoutMs?: undefined;
|
|
3859
3855
|
intervalMs?: undefined;
|
|
3860
3856
|
includeRows?: undefined;
|
|
@@ -3915,7 +3911,6 @@ export declare const allTools: ({
|
|
|
3915
3911
|
tableId?: undefined;
|
|
3916
3912
|
targetCount?: undefined;
|
|
3917
3913
|
minPassedCount?: undefined;
|
|
3918
|
-
minMessagesCount?: undefined;
|
|
3919
3914
|
timeoutMs?: undefined;
|
|
3920
3915
|
intervalMs?: undefined;
|
|
3921
3916
|
includeRows?: undefined;
|
|
@@ -3946,7 +3941,6 @@ export declare const allTools: ({
|
|
|
3946
3941
|
tableId?: undefined;
|
|
3947
3942
|
targetCount?: undefined;
|
|
3948
3943
|
minPassedCount?: undefined;
|
|
3949
|
-
minMessagesCount?: undefined;
|
|
3950
3944
|
timeoutMs?: undefined;
|
|
3951
3945
|
intervalMs?: undefined;
|
|
3952
3946
|
includeRows?: undefined;
|
|
@@ -3977,7 +3971,6 @@ export declare const allTools: ({
|
|
|
3977
3971
|
tableId?: undefined;
|
|
3978
3972
|
targetCount?: undefined;
|
|
3979
3973
|
minPassedCount?: undefined;
|
|
3980
|
-
minMessagesCount?: undefined;
|
|
3981
3974
|
timeoutMs?: undefined;
|
|
3982
3975
|
intervalMs?: undefined;
|
|
3983
3976
|
includeRows?: undefined;
|
package/dist/tools/rubrics.d.ts
CHANGED
|
@@ -33,7 +33,6 @@ type WaitForRubricResultsInput = {
|
|
|
33
33
|
tableId?: string;
|
|
34
34
|
targetCount?: number;
|
|
35
35
|
minPassedCount?: number;
|
|
36
|
-
minMessagesCount?: number;
|
|
37
36
|
timeoutMs?: number;
|
|
38
37
|
intervalMs?: number;
|
|
39
38
|
includeRows?: boolean;
|
|
@@ -115,7 +114,6 @@ export declare const rubricToolDefinitions: ({
|
|
|
115
114
|
tableId?: undefined;
|
|
116
115
|
targetCount?: undefined;
|
|
117
116
|
minPassedCount?: undefined;
|
|
118
|
-
minMessagesCount?: undefined;
|
|
119
117
|
timeoutMs?: undefined;
|
|
120
118
|
intervalMs?: undefined;
|
|
121
119
|
includeRows?: undefined;
|
|
@@ -149,7 +147,6 @@ export declare const rubricToolDefinitions: ({
|
|
|
149
147
|
tableId?: undefined;
|
|
150
148
|
targetCount?: undefined;
|
|
151
149
|
minPassedCount?: undefined;
|
|
152
|
-
minMessagesCount?: undefined;
|
|
153
150
|
timeoutMs?: undefined;
|
|
154
151
|
intervalMs?: undefined;
|
|
155
152
|
includeRows?: undefined;
|
|
@@ -208,7 +205,6 @@ export declare const rubricToolDefinitions: ({
|
|
|
208
205
|
tableId?: undefined;
|
|
209
206
|
targetCount?: undefined;
|
|
210
207
|
minPassedCount?: undefined;
|
|
211
|
-
minMessagesCount?: undefined;
|
|
212
208
|
timeoutMs?: undefined;
|
|
213
209
|
intervalMs?: undefined;
|
|
214
210
|
includeRows?: undefined;
|
|
@@ -267,7 +263,6 @@ export declare const rubricToolDefinitions: ({
|
|
|
267
263
|
tableId?: undefined;
|
|
268
264
|
targetCount?: undefined;
|
|
269
265
|
minPassedCount?: undefined;
|
|
270
|
-
minMessagesCount?: undefined;
|
|
271
266
|
timeoutMs?: undefined;
|
|
272
267
|
intervalMs?: undefined;
|
|
273
268
|
includeRows?: undefined;
|
|
@@ -328,7 +323,6 @@ export declare const rubricToolDefinitions: ({
|
|
|
328
323
|
tableId?: undefined;
|
|
329
324
|
targetCount?: undefined;
|
|
330
325
|
minPassedCount?: undefined;
|
|
331
|
-
minMessagesCount?: undefined;
|
|
332
326
|
timeoutMs?: undefined;
|
|
333
327
|
intervalMs?: undefined;
|
|
334
328
|
includeRows?: undefined;
|
|
@@ -359,7 +353,6 @@ export declare const rubricToolDefinitions: ({
|
|
|
359
353
|
tableId?: undefined;
|
|
360
354
|
targetCount?: undefined;
|
|
361
355
|
minPassedCount?: undefined;
|
|
362
|
-
minMessagesCount?: undefined;
|
|
363
356
|
timeoutMs?: undefined;
|
|
364
357
|
intervalMs?: undefined;
|
|
365
358
|
includeRows?: undefined;
|
|
@@ -390,7 +383,6 @@ export declare const rubricToolDefinitions: ({
|
|
|
390
383
|
tableId?: undefined;
|
|
391
384
|
targetCount?: undefined;
|
|
392
385
|
minPassedCount?: undefined;
|
|
393
|
-
minMessagesCount?: undefined;
|
|
394
386
|
timeoutMs?: undefined;
|
|
395
387
|
intervalMs?: undefined;
|
|
396
388
|
includeRows?: undefined;
|
|
@@ -420,10 +412,6 @@ export declare const rubricToolDefinitions: ({
|
|
|
420
412
|
type: string;
|
|
421
413
|
description: string;
|
|
422
414
|
};
|
|
423
|
-
minMessagesCount: {
|
|
424
|
-
type: string;
|
|
425
|
-
description: string;
|
|
426
|
-
};
|
|
427
415
|
timeoutMs: {
|
|
428
416
|
type: string;
|
|
429
417
|
description: string;
|
|
@@ -520,12 +508,6 @@ export declare function checkRubric(input: CheckRubricInput): Promise<{
|
|
|
520
508
|
export declare function waitForRubricResults(input: WaitForRubricResultsInput): Promise<{
|
|
521
509
|
stats: WorkflowTableStats;
|
|
522
510
|
rows?: import("./rows.js").LightweightRow[] | undefined;
|
|
523
|
-
messageGeneration?: {
|
|
524
|
-
completed: number;
|
|
525
|
-
passingGeneratedMessages: number;
|
|
526
|
-
minMessagesCount: number;
|
|
527
|
-
floorMet: boolean;
|
|
528
|
-
} | undefined;
|
|
529
511
|
ready: boolean;
|
|
530
512
|
attempts: number;
|
|
531
513
|
elapsedMs: number;
|
|
@@ -538,14 +520,28 @@ export declare function waitForRubricResults(input: WaitForRubricResultsInput):
|
|
|
538
520
|
targetCount: number;
|
|
539
521
|
pending?: undefined;
|
|
540
522
|
};
|
|
523
|
+
reason?: undefined;
|
|
524
|
+
partialResult?: undefined;
|
|
525
|
+
diagnostic?: undefined;
|
|
526
|
+
guidance?: undefined;
|
|
541
527
|
} | {
|
|
542
528
|
stats: WorkflowTableStats;
|
|
543
529
|
rows?: import("./rows.js").LightweightRow[] | undefined;
|
|
530
|
+
ready: boolean;
|
|
531
|
+
partial: boolean;
|
|
532
|
+
reason: string;
|
|
533
|
+
attempts: number;
|
|
534
|
+
elapsedMs: number;
|
|
535
|
+
tableId: string;
|
|
536
|
+
passRate: {
|
|
537
|
+
completed: number;
|
|
538
|
+
passed: number;
|
|
539
|
+
pending: number;
|
|
540
|
+
percent: number;
|
|
541
|
+
targetCount: number;
|
|
542
|
+
minPassedCount: number;
|
|
543
|
+
};
|
|
544
544
|
partialResult: {
|
|
545
|
-
messagesCount?: number | undefined;
|
|
546
|
-
passingGeneratedMessages?: number | undefined;
|
|
547
|
-
minMessagesCount?: number | undefined;
|
|
548
|
-
messageFloorMet?: boolean | undefined;
|
|
549
545
|
completed: number;
|
|
550
546
|
passed: number;
|
|
551
547
|
pending: number;
|
|
@@ -555,32 +551,23 @@ export declare function waitForRubricResults(input: WaitForRubricResultsInput):
|
|
|
555
551
|
enoughToDiagnose: boolean;
|
|
556
552
|
floorMet: boolean;
|
|
557
553
|
};
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
minMessagesCount: number;
|
|
562
|
-
floorMet: boolean;
|
|
563
|
-
} | undefined;
|
|
554
|
+
diagnostic?: undefined;
|
|
555
|
+
guidance?: undefined;
|
|
556
|
+
} | {
|
|
564
557
|
ready: boolean;
|
|
565
|
-
partial: boolean;
|
|
566
558
|
reason: string;
|
|
567
559
|
attempts: number;
|
|
568
560
|
elapsedMs: number;
|
|
569
561
|
tableId: string;
|
|
570
562
|
passRate: {
|
|
563
|
+
minPassedCount?: number | undefined;
|
|
571
564
|
completed: number;
|
|
572
565
|
passed: number;
|
|
573
566
|
pending: number;
|
|
574
567
|
percent: number;
|
|
575
568
|
targetCount: number;
|
|
576
|
-
minPassedCount: number;
|
|
577
569
|
};
|
|
578
|
-
} | {
|
|
579
570
|
partialResult: {
|
|
580
|
-
messagesCount?: number | undefined;
|
|
581
|
-
passingGeneratedMessages?: number | undefined;
|
|
582
|
-
minMessagesCount?: number | undefined;
|
|
583
|
-
messageFloorMet?: boolean | undefined;
|
|
584
571
|
enoughToDiagnose: boolean;
|
|
585
572
|
floorMet: boolean;
|
|
586
573
|
minPassedCount?: number | undefined;
|
|
@@ -594,31 +581,12 @@ export declare function waitForRubricResults(input: WaitForRubricResultsInput):
|
|
|
594
581
|
totalRows: number;
|
|
595
582
|
enrichedCount: number | undefined;
|
|
596
583
|
needsEnrichCount: number | undefined;
|
|
597
|
-
messagesCount: number;
|
|
584
|
+
messagesCount: number | undefined;
|
|
598
585
|
needsApprovalCount: number | undefined;
|
|
599
586
|
processingCount: number | undefined;
|
|
600
587
|
failedCount: number | undefined;
|
|
601
588
|
};
|
|
602
589
|
guidance: string;
|
|
603
590
|
stats: WorkflowTableStats | null;
|
|
604
|
-
messageGeneration?: {
|
|
605
|
-
completed: number;
|
|
606
|
-
passingGeneratedMessages: number;
|
|
607
|
-
minMessagesCount: number;
|
|
608
|
-
floorMet: boolean;
|
|
609
|
-
} | undefined;
|
|
610
|
-
ready: boolean;
|
|
611
|
-
reason: string;
|
|
612
|
-
attempts: number;
|
|
613
|
-
elapsedMs: number;
|
|
614
|
-
tableId: string;
|
|
615
|
-
passRate: {
|
|
616
|
-
minPassedCount?: number | undefined;
|
|
617
|
-
completed: number;
|
|
618
|
-
passed: number;
|
|
619
|
-
pending: number;
|
|
620
|
-
percent: number;
|
|
621
|
-
targetCount: number;
|
|
622
|
-
};
|
|
623
591
|
}>;
|
|
624
592
|
export {};
|
package/dist/tools/rubrics.js
CHANGED
|
@@ -9,11 +9,6 @@ const DEFAULT_INTERVAL_MS = 2000;
|
|
|
9
9
|
function sleep(ms) {
|
|
10
10
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
11
11
|
}
|
|
12
|
-
function countPassingGeneratedMessages(rowSnapshot) {
|
|
13
|
-
if (!rowSnapshot?.rows?.length)
|
|
14
|
-
return 0;
|
|
15
|
-
return rowSnapshot.rows.filter((row) => row.icpPassed === true && Boolean(row.message?.trim())).length;
|
|
16
|
-
}
|
|
17
12
|
function normalizeRubricItemDefaults(item) {
|
|
18
13
|
return {
|
|
19
14
|
...item,
|
|
@@ -398,10 +393,6 @@ export const rubricToolDefinitions = [
|
|
|
398
393
|
type: "number",
|
|
399
394
|
description: "Optional pass floor for bounded create-campaign samples. When this floor is met, the tool returns ready:true with partial:true instead of waiting for every target row to finish.",
|
|
400
395
|
},
|
|
401
|
-
minMessagesCount: {
|
|
402
|
-
type: "number",
|
|
403
|
-
description: "Optional generated-message floor. When provided with minPassedCount, the tool waits until both floors are met so create-campaign-v2 can review the first passing generated message without waiting for the full batch.",
|
|
404
|
-
},
|
|
405
396
|
timeoutMs: {
|
|
406
397
|
type: "number",
|
|
407
398
|
description: `Max time to wait in ms (default ${DEFAULT_TIMEOUT_MS}).`,
|
|
@@ -697,7 +688,6 @@ export async function waitForRubricResults(input) {
|
|
|
697
688
|
const intervalMs = Math.max(500, input.intervalMs ?? DEFAULT_INTERVAL_MS);
|
|
698
689
|
const targetCount = resolveMaxProspects(input.targetCount);
|
|
699
690
|
const minPassedCount = resolveMinPassedCount(input.minPassedCount);
|
|
700
|
-
const minMessagesCount = resolveMinPassedCount(input.minMessagesCount);
|
|
701
691
|
const includeRows = input.includeRows !== false;
|
|
702
692
|
let tableId = input.tableId;
|
|
703
693
|
if (!tableId && input.campaignOfferId) {
|
|
@@ -719,33 +709,19 @@ export async function waitForRubricResults(input) {
|
|
|
719
709
|
const totalRows = stats.totalRows ?? 0;
|
|
720
710
|
const effectiveTarget = totalRows > 0 ? Math.min(targetCount, totalRows) : targetCount;
|
|
721
711
|
const pending = Math.max(effectiveTarget - completed, 0);
|
|
722
|
-
const messagesCount = stats.messagesCount ?? 0;
|
|
723
712
|
const failedCount = stats.failedCount ?? 0;
|
|
724
713
|
const processingCount = stats.processingCount ?? 0;
|
|
725
714
|
const queuedCount = stats.queuedCount ?? 0;
|
|
726
715
|
const cancellableCount = stats.cancellableCount ?? 0;
|
|
727
716
|
const minPassFloorMet = minPassedCount !== null && passed >= minPassedCount;
|
|
728
|
-
const rawMessageFloorMet = minMessagesCount === null || messagesCount >= minMessagesCount;
|
|
729
|
-
let rowSnapshotForMessageCheck = null;
|
|
730
|
-
let passingGeneratedMessagesCount = null;
|
|
731
|
-
if (minMessagesCount !== null && minPassFloorMet && rawMessageFloorMet) {
|
|
732
|
-
rowSnapshotForMessageCheck = await getTableRowsMinimal(tableId, {
|
|
733
|
-
limit: effectiveTarget,
|
|
734
|
-
});
|
|
735
|
-
passingGeneratedMessagesCount = countPassingGeneratedMessages(rowSnapshotForMessageCheck);
|
|
736
|
-
}
|
|
737
|
-
const messageFloorMet = minMessagesCount === null ||
|
|
738
|
-
(passingGeneratedMessagesCount ?? 0) >= minMessagesCount;
|
|
739
|
-
const earlyFloorMet = minPassFloorMet && messageFloorMet;
|
|
740
717
|
const unresolvedRowsResolvedAsFailures = pending > 0 && completed + failedCount >= effectiveTarget;
|
|
741
718
|
const noActiveProcessing = processingCount === 0 && queuedCount === 0 && cancellableCount === 0;
|
|
742
|
-
if (completed >= effectiveTarget
|
|
719
|
+
if (completed >= effectiveTarget) {
|
|
743
720
|
const percent = completed > 0 ? Math.round((passed / completed) * 100) : 0;
|
|
744
721
|
const rowSnapshot = includeRows
|
|
745
|
-
? (
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
})))
|
|
722
|
+
? await getTableRowsMinimal(tableId, {
|
|
723
|
+
limit: effectiveTarget,
|
|
724
|
+
})
|
|
749
725
|
: null;
|
|
750
726
|
return {
|
|
751
727
|
ready: true,
|
|
@@ -759,21 +735,11 @@ export async function waitForRubricResults(input) {
|
|
|
759
735
|
targetCount: effectiveTarget,
|
|
760
736
|
...(minPassedCount !== null ? { minPassedCount } : {}),
|
|
761
737
|
},
|
|
762
|
-
...(minMessagesCount !== null
|
|
763
|
-
? {
|
|
764
|
-
messageGeneration: {
|
|
765
|
-
completed: messagesCount,
|
|
766
|
-
passingGeneratedMessages: passingGeneratedMessagesCount ?? 0,
|
|
767
|
-
minMessagesCount,
|
|
768
|
-
floorMet: true,
|
|
769
|
-
},
|
|
770
|
-
}
|
|
771
|
-
: {}),
|
|
772
738
|
...(rowSnapshot ? { rows: rowSnapshot.rows } : {}),
|
|
773
739
|
stats,
|
|
774
740
|
};
|
|
775
741
|
}
|
|
776
|
-
if (
|
|
742
|
+
if (minPassFloorMet) {
|
|
777
743
|
const percent = completed > 0 ? Math.round((passed / completed) * 100) : 0;
|
|
778
744
|
const reason = !noActiveProcessing
|
|
779
745
|
? "min_passed_count_met_with_active_processing"
|
|
@@ -781,10 +747,9 @@ export async function waitForRubricResults(input) {
|
|
|
781
747
|
? "min_passed_count_met_with_resolved_failures"
|
|
782
748
|
: "min_passed_count_met_no_active_processing";
|
|
783
749
|
const rowSnapshot = includeRows
|
|
784
|
-
? (
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
})))
|
|
750
|
+
? await getTableRowsMinimal(tableId, {
|
|
751
|
+
limit: effectiveTarget,
|
|
752
|
+
})
|
|
788
753
|
: null;
|
|
789
754
|
return {
|
|
790
755
|
ready: true,
|
|
@@ -801,16 +766,6 @@ export async function waitForRubricResults(input) {
|
|
|
801
766
|
targetCount: effectiveTarget,
|
|
802
767
|
minPassedCount,
|
|
803
768
|
},
|
|
804
|
-
...(minMessagesCount !== null
|
|
805
|
-
? {
|
|
806
|
-
messageGeneration: {
|
|
807
|
-
completed: messagesCount,
|
|
808
|
-
passingGeneratedMessages: passingGeneratedMessagesCount ?? 0,
|
|
809
|
-
minMessagesCount,
|
|
810
|
-
floorMet: true,
|
|
811
|
-
},
|
|
812
|
-
}
|
|
813
|
-
: {}),
|
|
814
769
|
partialResult: {
|
|
815
770
|
completed,
|
|
816
771
|
passed,
|
|
@@ -820,14 +775,6 @@ export async function waitForRubricResults(input) {
|
|
|
820
775
|
minPassedCount,
|
|
821
776
|
enoughToDiagnose: true,
|
|
822
777
|
floorMet: true,
|
|
823
|
-
...(minMessagesCount !== null
|
|
824
|
-
? {
|
|
825
|
-
messagesCount,
|
|
826
|
-
passingGeneratedMessages: passingGeneratedMessagesCount ?? 0,
|
|
827
|
-
minMessagesCount,
|
|
828
|
-
messageFloorMet: true,
|
|
829
|
-
}
|
|
830
|
-
: {}),
|
|
831
778
|
},
|
|
832
779
|
...(rowSnapshot ? { rows: rowSnapshot.rows } : {}),
|
|
833
780
|
stats,
|
|
@@ -838,21 +785,9 @@ export async function waitForRubricResults(input) {
|
|
|
838
785
|
const completed = lastStats?.passRate?.completed ?? 0;
|
|
839
786
|
const passed = lastStats?.passRate?.passed ?? 0;
|
|
840
787
|
const percent = completed > 0 ? Math.round((passed / completed) * 100) : 0;
|
|
841
|
-
const messagesCount = lastStats?.messagesCount ?? 0;
|
|
842
788
|
const totalRows = lastStats?.totalRows ?? 0;
|
|
843
789
|
const effectiveTarget = totalRows > 0 ? Math.min(targetCount, totalRows) : targetCount;
|
|
844
790
|
const pending = Math.max(effectiveTarget - completed, 0);
|
|
845
|
-
let timeoutPassingGeneratedMessagesCount = null;
|
|
846
|
-
if (minMessagesCount !== null) {
|
|
847
|
-
try {
|
|
848
|
-
timeoutPassingGeneratedMessagesCount = countPassingGeneratedMessages(await getTableRowsMinimal(tableId, {
|
|
849
|
-
limit: effectiveTarget,
|
|
850
|
-
}));
|
|
851
|
-
}
|
|
852
|
-
catch {
|
|
853
|
-
timeoutPassingGeneratedMessagesCount = null;
|
|
854
|
-
}
|
|
855
|
-
}
|
|
856
791
|
return {
|
|
857
792
|
ready: false,
|
|
858
793
|
reason: "timeout",
|
|
@@ -867,16 +802,6 @@ export async function waitForRubricResults(input) {
|
|
|
867
802
|
targetCount: effectiveTarget,
|
|
868
803
|
...(minPassedCount !== null ? { minPassedCount } : {}),
|
|
869
804
|
},
|
|
870
|
-
...(minMessagesCount !== null
|
|
871
|
-
? {
|
|
872
|
-
messageGeneration: {
|
|
873
|
-
completed: messagesCount,
|
|
874
|
-
passingGeneratedMessages: timeoutPassingGeneratedMessagesCount ?? 0,
|
|
875
|
-
minMessagesCount,
|
|
876
|
-
floorMet: (timeoutPassingGeneratedMessagesCount ?? 0) >= minMessagesCount,
|
|
877
|
-
},
|
|
878
|
-
}
|
|
879
|
-
: {}),
|
|
880
805
|
partialResult: {
|
|
881
806
|
completed,
|
|
882
807
|
passed,
|
|
@@ -886,25 +811,17 @@ export async function waitForRubricResults(input) {
|
|
|
886
811
|
...(minPassedCount !== null ? { minPassedCount } : {}),
|
|
887
812
|
enoughToDiagnose: completed > 0,
|
|
888
813
|
floorMet: minPassedCount !== null && passed >= minPassedCount,
|
|
889
|
-
...(minMessagesCount !== null
|
|
890
|
-
? {
|
|
891
|
-
messagesCount,
|
|
892
|
-
passingGeneratedMessages: timeoutPassingGeneratedMessagesCount ?? 0,
|
|
893
|
-
minMessagesCount,
|
|
894
|
-
messageFloorMet: (timeoutPassingGeneratedMessagesCount ?? 0) >= minMessagesCount,
|
|
895
|
-
}
|
|
896
|
-
: {}),
|
|
897
814
|
},
|
|
898
815
|
diagnostic: {
|
|
899
816
|
totalRows,
|
|
900
817
|
enrichedCount: lastStats?.enrichedCount,
|
|
901
818
|
needsEnrichCount: lastStats?.needsEnrichCount,
|
|
902
|
-
messagesCount,
|
|
819
|
+
messagesCount: lastStats?.messagesCount,
|
|
903
820
|
needsApprovalCount: lastStats?.needsApprovalCount,
|
|
904
821
|
processingCount: lastStats?.processingCount,
|
|
905
822
|
failedCount: lastStats?.failedCount,
|
|
906
823
|
},
|
|
907
|
-
guidance: "If this is create-campaign-v2 validate-sample, do not repeat waits indefinitely. Use passRate/stats to diagnose and surface sample_revision_required before Settings when the sample is under the pass floor
|
|
824
|
+
guidance: "If this is create-campaign-v2 validate-sample, do not repeat waits indefinitely. Use passRate/stats to diagnose and surface sample_revision_required before Settings when the sample is under the pass floor or messages are incomplete.",
|
|
908
825
|
stats: lastStats,
|
|
909
826
|
};
|
|
910
827
|
}
|
package/package.json
CHANGED
|
@@ -49,10 +49,9 @@ for debug output.
|
|
|
49
49
|
12. Sync the approved template into the campaign brief.
|
|
50
50
|
13. After message approval, keep the watched app on Filter Leads while the
|
|
51
51
|
bounded enrichment/filter cascade starts.
|
|
52
|
-
14. Move to Messages only after at least one review row passes and
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
sequence, and explicit launch greenlight.
|
|
52
|
+
14. Move to Messages only after at least one review row passes, generate and
|
|
53
|
+
review bounded messages, then hand off to Settings, sender, sequence, and
|
|
54
|
+
explicit launch greenlight.
|
|
56
55
|
|
|
57
56
|
There is no normal approval-packet, commit-gate, atomic-mint, or local
|
|
58
57
|
artifact-validation step. Those belong only to legacy validation/rehearsal
|
|
@@ -1124,14 +1124,8 @@
|
|
|
1124
1124
|
"cellSource": "pending_generate_message_cells_for_passing_rows using approved_campaign_brief_template"
|
|
1125
1125
|
},
|
|
1126
1126
|
{
|
|
1127
|
-
"tool": "
|
|
1128
|
-
"purpose": "
|
|
1129
|
-
"requiredValues": {
|
|
1130
|
-
"includeRows": false,
|
|
1131
|
-
"minPassedCount": 1,
|
|
1132
|
-
"minMessagesCount": 1
|
|
1133
|
-
},
|
|
1134
|
-
"readVia": "stats_only_tool_result"
|
|
1127
|
+
"tool": "wait_for_campaign_table_ready",
|
|
1128
|
+
"purpose": "wait_for_generate_message_cells_to_complete"
|
|
1135
1129
|
},
|
|
1136
1130
|
{
|
|
1137
1131
|
"action": "observe_generate_message_results",
|
|
@@ -1175,24 +1169,25 @@
|
|
|
1175
1169
|
"currentStep": "auto-execute-messaging",
|
|
1176
1170
|
"watchNarration.stage": "review-ready"
|
|
1177
1171
|
},
|
|
1178
|
-
"watchNarrationRule": "Say
|
|
1172
|
+
"watchNarrationRule": "Say what just happened, that review-batch messages are ready in Messages, and that the next action is reviewing or approving generated messages before Settings."
|
|
1179
1173
|
},
|
|
1180
1174
|
{
|
|
1181
1175
|
"action": "ask_generated_message_review_choice",
|
|
1182
1176
|
"uses": "request_user_input",
|
|
1183
1177
|
"choices": [
|
|
1184
|
-
"Approve generated
|
|
1178
|
+
"Approve generated messages",
|
|
1185
1179
|
"Revise filters",
|
|
1186
1180
|
"Revise message template",
|
|
1187
1181
|
"Pause here"
|
|
1188
|
-
]
|
|
1182
|
+
],
|
|
1183
|
+
"rule": "Do not advance to Settings until the generated review-batch messages have been reviewed and approved."
|
|
1189
1184
|
}
|
|
1190
1185
|
],
|
|
1191
1186
|
"allowedTools": [
|
|
1192
1187
|
"get_subskill_asset",
|
|
1193
1188
|
"get_rows_minimal",
|
|
1194
1189
|
"queue_cells",
|
|
1195
|
-
"
|
|
1190
|
+
"wait_for_campaign_table_ready",
|
|
1196
1191
|
"update_campaign",
|
|
1197
1192
|
"AskUserQuestion",
|
|
1198
1193
|
"request_user_input"
|
|
@@ -1206,7 +1201,6 @@
|
|
|
1206
1201
|
"hardRules": [
|
|
1207
1202
|
"critique_failure_never_escalates",
|
|
1208
1203
|
"critique_sample_size_bounded_by_config",
|
|
1209
|
-
"first_passing_generated_message_unblocks_review",
|
|
1210
1204
|
"critics_fixed_at_targeting_copy_voice",
|
|
1211
1205
|
"synthesis_enforces_phase_84_token_contract",
|
|
1212
1206
|
"opus_reserved_for_highest_value_subset",
|
|
@@ -60,10 +60,9 @@ auto-revise leads.
|
|
|
60
60
|
explicit batch count anyway so future larger expansion batches do not
|
|
61
61
|
accidentally stop early
|
|
62
62
|
(see §Known Tool Behaviors #3)
|
|
63
|
-
- minPassedCount=1 means one passing filtered row unblocks Step 15
|
|
64
|
-
Message observation.
|
|
65
|
-
|
|
66
|
-
finish before messages start.
|
|
63
|
+
- minPassedCount=1 means one passing filtered row unblocks Step 15
|
|
64
|
+
Generate Message observation. Do not wait for all sample rows to finish
|
|
65
|
+
before messages start.
|
|
67
66
|
|
|
68
67
|
7. call `wait_for_rubric_results` with `includeRows=false`; extract ONLY:
|
|
69
68
|
- ready: boolean
|
|
@@ -102,9 +101,7 @@ auto-revise leads.
|
|
|
102
101
|
10. branch:
|
|
103
102
|
if passInSample >= 1:
|
|
104
103
|
proceed to Step 15 (auto-execute-messaging) with currently passing rows
|
|
105
|
-
so Generate Message can start without waiting for the full sample
|
|
106
|
-
one passing generated message is ready, stop for user review instead of
|
|
107
|
-
waiting for a stronger sample.
|
|
104
|
+
so Generate Message can start without waiting for the full sample
|
|
108
105
|
else:
|
|
109
106
|
diagnose (see Brief-vs-List Diagnosis below)
|
|
110
107
|
revisionRound += 1
|
|
@@ -182,8 +179,7 @@ processing makes the experience feel frozen.
|
|
|
182
179
|
|
|
183
180
|
Workaround: treat timeout stats as a partial sample. If at least one row has
|
|
184
181
|
passed, move to Step 15 and observe or queue Generate Message for the passing
|
|
185
|
-
rows
|
|
186
|
-
passed and no active processing is visible, stop at
|
|
182
|
+
rows. If zero rows have passed and no active processing is visible, stop at
|
|
187
183
|
`Status: sample-needs-revision` before Settings. Show the completed / passed /
|
|
188
184
|
pending counts and ask whether to revise source, revise filter/rubric, or wait
|
|
189
185
|
once only if active processing is still visible.
|
|
@@ -127,9 +127,11 @@ rate with a conservative cleanup factor, cap the source candidates at the
|
|
|
127
127
|
approved provider limit, and select only enough posts to reach that engager
|
|
128
128
|
count. The planning floor is 10% projected fit after cleanup; if Signal
|
|
129
129
|
Discovery falls below that floor, do not import the source list. Route back to
|
|
130
|
-
find-leads and move to Sales Nav recent activity instead.
|
|
131
|
-
`
|
|
132
|
-
|
|
130
|
+
find-leads and move to Sales Nav recent activity instead. After the scrape,
|
|
131
|
+
`wait_for_lead_list_ready` must clear the approved source-candidate target; if
|
|
132
|
+
it reports `source_under_capacity`, do not call `confirm_lead_list`. The
|
|
133
|
+
subsequent `confirm_lead_list` call still uses `targetLeadCount: <importLimit>`
|
|
134
|
+
so only the bounded review batch enters the campaign table.
|
|
133
135
|
|
|
134
136
|
For supplied direct lists, `confirm_lead_list` must receive
|
|
135
137
|
`targetLeadCount: <importLimit>` or explicit `sourceRowIds` so a 100-row source
|
|
@@ -4,16 +4,15 @@ This reference governs Step 15 (`auto-execute-messaging`) re-cascade
|
|
|
4
4
|
behavior when Step 14's validate-sample loop graduates additional rows
|
|
5
5
|
from pending → passed after the first Generate Message cells have already run.
|
|
6
6
|
|
|
7
|
-
Load whenever Step 15 is about to
|
|
8
|
-
|
|
7
|
+
Load whenever Step 15 is about to transition to
|
|
8
|
+
`awaiting-user-greenlight`, and on every resume into Step 15.
|
|
9
9
|
|
|
10
10
|
## Principle
|
|
11
11
|
|
|
12
|
-
Generate Message cells are cascade-scoped. If Step 14's rubric flips rows
|
|
13
|
-
pending → passed AFTER Step 15 first observes messages for the review
|
|
14
|
-
new rows can sit pending with no message.
|
|
15
|
-
|
|
16
|
-
late-passed rows can be re-cascaded after explicit user continuation or resume.
|
|
12
|
+
Generate Message cells are cascade-scoped. If Step 14's rubric flips rows
|
|
13
|
+
from pending → passed AFTER Step 15 first observes messages for the review
|
|
14
|
+
batch, the new rows can sit pending with no message. Step 15 must explicitly
|
|
15
|
+
queue the newly-pending Generate Message cells for the review-batch subset.
|
|
17
16
|
|
|
18
17
|
Observed on manual Phase-85 signal-discovery run (2026-04-20): 15 new
|
|
19
18
|
rows graduated pending → passed mid-tail while `messagesCount` stayed
|
|
@@ -23,17 +22,13 @@ flat at 257. The new rows never got messages.
|
|
|
23
22
|
|
|
24
23
|
Re-cascade runs whenever ALL of the following are true:
|
|
25
24
|
|
|
26
|
-
1.
|
|
27
|
-
to continue processing more review-batch rows.
|
|
25
|
+
1. Step 15 has already observed the initial review-batch message cascade.
|
|
28
26
|
2. A subsequent check of rubric state shows rows that were pending at
|
|
29
27
|
first message-generation pass are now passed.
|
|
30
28
|
3. Those newly-passed rows do NOT yet have generated messages
|
|
31
29
|
(`messagesCount` flat relative to pre-cascade).
|
|
32
30
|
4. Step 15 has NOT yet transitioned to `awaiting-user-greenlight`.
|
|
33
31
|
|
|
34
|
-
Before that first generated-message approval, do not run this loop just to wait
|
|
35
|
-
for a bigger sample. One passing generated message is enough for review.
|
|
36
|
-
|
|
37
32
|
If condition 4 already fired (i.e. we're in Step 16), the re-cascade
|
|
38
33
|
runs on resume into Step 15 if validate-sample was re-entered and new
|
|
39
34
|
rows graduated.
|
|
@@ -252,17 +252,16 @@ Template approved, bounded filter test running:
|
|
|
252
252
|
"stage": "fit-message",
|
|
253
253
|
"headline": "Template saved",
|
|
254
254
|
"visibleState": "The browser stays on Filter Leads while the bounded enrichment and filter test runs.",
|
|
255
|
-
"agentIntent": "Codex saved the approved message template, queued the review-batch Enrich Prospect cells, and is waiting for one
|
|
256
|
-
"nextAction": "
|
|
255
|
+
"agentIntent": "Codex saved the approved message template, queued the review-batch Enrich Prospect cells, and is waiting for at least one row to pass before moving to Messages.",
|
|
256
|
+
"nextAction": "Move to Messages after a passing row"
|
|
257
257
|
}
|
|
258
258
|
```
|
|
259
259
|
|
|
260
260
|
Do not move to Messages immediately after `approve-message`. The visible route
|
|
261
261
|
is already Filter Leads after `save_rubrics`; approving the message only unlocks
|
|
262
262
|
the bounded cascade from that screen. Move to Messages only once at least one
|
|
263
|
-
review-batch row passes and
|
|
264
|
-
|
|
265
|
-
user to approve the generated message.
|
|
263
|
+
review-batch row passes and Generate Message cells are ready/running for the
|
|
264
|
+
passing rows.
|
|
266
265
|
|
|
267
266
|
Messages waiting for template:
|
|
268
267
|
|
|
@@ -58,12 +58,11 @@ Step 14 — kick bounded cascade + observe sample
|
|
|
58
58
|
passing filtered row exists.)
|
|
59
59
|
|
|
60
60
|
Step 15 — observe messaging
|
|
61
|
-
|
|
62
|
-
get_rows_minimal # confirm the first passing generated message exists
|
|
61
|
+
get_rows_minimal # confirm passing rows have completed Generate Message cells
|
|
63
62
|
(rare) queue_cells on any pending Generate Message cells
|
|
64
63
|
token-contract spot check via get_rows
|
|
65
64
|
update_campaign(currentStep=auto-execute-messaging) with review-ready narration
|
|
66
|
-
ask the user to approve
|
|
65
|
+
ask the user to approve generated review-batch messages before Settings
|
|
67
66
|
only after approval: update_campaign(currentStep=awaiting-user-greenlight)
|
|
68
67
|
(generate_messages is NOT an MCP tool; messages come from the cascade)
|
|
69
68
|
|
|
@@ -292,10 +291,8 @@ Do not route to a visible `validate-sample` step. Full decision tree lives in
|
|
|
292
291
|
review batch only. After `save_rubrics` and the approved message template are
|
|
293
292
|
persisted, Step 14 queues the review-batch Enrich Prospect cells, waits until
|
|
294
293
|
filter results start landing, then moves to message observation as soon as one
|
|
295
|
-
row passes.
|
|
296
|
-
|
|
297
|
-
message is ready. It does NOT call `check_rubric`,
|
|
298
|
-
`bulk_enrich_with_prospeo`, or any other direct enrichment/scoring tool.
|
|
294
|
+
row passes. It does NOT call `check_rubric`, `bulk_enrich_with_prospeo`, or any
|
|
295
|
+
other direct enrichment/scoring tool.
|
|
299
296
|
|
|
300
297
|
Shape:
|
|
301
298
|
|
|
@@ -310,7 +307,6 @@ projectedPass = round(passInSample / sampleSize * importLimit)
|
|
|
310
307
|
if wait_for_rubric_results.ready === true and passRate.passed >= 1:
|
|
311
308
|
advance to Step 15 to observe or queue Generate Message for currently passing rows
|
|
312
309
|
do not wait for every sample row to finish before message generation starts
|
|
313
|
-
stop for review as soon as one passing generated message is ready
|
|
314
310
|
else if wait_for_rubric_results.ready === false and reason === "timeout":
|
|
315
311
|
use the partial passRate/stats as the sample diagnostic
|
|
316
312
|
if passRate.passed >= 1:
|
|
@@ -386,20 +382,17 @@ Template`. If it does not, fail before the cascade runs. Do not repair
|
|
|
386
382
|
already (cascade auto-fired it). If it is still `pending`, queue
|
|
387
383
|
it explicitly: `queue_cells({ tableId, cellIds:
|
|
388
384
|
<generateMessageCellIds> })`.
|
|
389
|
-
3. `
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
Remaining review-batch rows can continue processing in the background.
|
|
394
|
-
4. Read the first ready generated message back via `get_rows` (full) and
|
|
395
|
-
sanity-check that sample against the Phase 84 token contract: no unresolved
|
|
385
|
+
3. `wait_for_campaign_table_ready` (or poll) until every passing
|
|
386
|
+
row's Generate Message cell is `completed`.
|
|
387
|
+
4. Read the results back via `get_rows` (full) and sanity-check a
|
|
388
|
+
sample against the Phase 84 token contract: no unresolved
|
|
396
389
|
`{{tokens}}`, no invented proof, one sentence per line, etc.
|
|
397
390
|
5. If the sample fails the token contract, diagnose brief-vs-list
|
|
398
391
|
(same revision loop as Step 14) and escalate if over
|
|
399
392
|
`maxRevisionRounds`.
|
|
400
|
-
6. On success, keep `currentStep: "auto-execute-messaging"` and ask the user
|
|
401
|
-
approve the generated
|
|
402
|
-
|
|
393
|
+
6. On success, keep `currentStep: "auto-execute-messaging"` and ask the user
|
|
394
|
+
to approve the generated review-batch messages. Only that approval may move
|
|
395
|
+
the campaign to Settings / `awaiting-user-greenlight`.
|
|
403
396
|
|
|
404
397
|
**Do NOT hand-write message bodies via `update_cell`.** `update_cell`
|
|
405
398
|
is reachable for legitimate operator overrides AFTER the tail hands
|
|
@@ -439,16 +432,15 @@ strings, which is why Step 16 requires Step 15 to be complete.
|
|
|
439
432
|
4. Check `messaging.tokenContract`. When `strict`, reject any sample
|
|
440
433
|
message that contains unresolved or unsupported tokens (including
|
|
441
434
|
any critique rewrite that tried to introduce one).
|
|
442
|
-
5. If the
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
explicit user expansion approval.
|
|
435
|
+
5. If the sample passes the token contract (and critique when enabled),
|
|
436
|
+
stop at the review-batch handoff. Do NOT scale to the full source list
|
|
437
|
+
before explicit user expansion approval.
|
|
446
438
|
6. If the sample fails the token contract or critique, diagnose +
|
|
447
439
|
loop the same way Step 14 does (brief-vs-list), subject to the same
|
|
448
440
|
`maxRevisionRounds` cap.
|
|
449
441
|
7. On success, keep `currentStep: "auto-execute-messaging"`, show that the
|
|
450
|
-
|
|
451
|
-
|
|
442
|
+
review-batch messages are ready in Messages, and ask for approval before
|
|
443
|
+
Settings. Only after the user approves those generated messages should
|
|
452
444
|
`update_campaign({ campaignId, currentStep: "awaiting-user-greenlight" })`
|
|
453
445
|
run.
|
|
454
446
|
|
|
@@ -459,6 +459,9 @@ targetLeadCount, targetEngagerCount, maxPostsToScrape })` for the approved
|
|
|
459
459
|
source-capacity plan without asking for another yes/no gate. Then
|
|
460
460
|
`confirm_lead_list` imports only the bounded review batch into the campaign
|
|
461
461
|
table. Do not confuse the source-candidate target with the review-batch size.
|
|
462
|
+
If the completed source scrape comes back below the approved source-candidate
|
|
463
|
+
target, do not call `confirm_lead_list`; select more posts, rerun source
|
|
464
|
+
discovery, or move to Sales Nav.
|
|
462
465
|
|
|
463
466
|
The promotion/select step is required campaign state. Use post IDs from the
|
|
464
467
|
current campaign-scoped search result, not stale IDs copied from a source review
|