@sellable/mcp 0.1.237 → 0.1.238
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/engage-memory.d.ts +1 -0
- package/dist/engage-memory.js +18 -2
- package/dist/identity-memory.d.ts +1 -0
- package/dist/identity-memory.js +27 -0
- package/dist/tools/engage-memory.js +1 -1
- package/package.json +1 -1
- package/skills/create-campaign-v2/SKILL.md +5 -4
- package/skills/create-campaign-v2/SOUL.md +4 -3
- package/skills/create-campaign-v2/core/flow.v2.json +1 -1
- package/skills/create-campaign-v2/references/approval-gate-framing.md +1 -1
- package/skills/create-post/SKILL.md +74 -10
- package/skills/create-post/references/gold-standard-post-pack.md +203 -0
- package/skills/create-post/references/hook-research-playbook.md +61 -3
- package/skills/create-post/references/post-validation.md +55 -1
- package/skills/interview/references/reference-curation.md +27 -0
package/dist/engage-memory.d.ts
CHANGED
package/dist/engage-memory.js
CHANGED
|
@@ -116,8 +116,18 @@ function buildMarkdownTable(headers, rows) {
|
|
|
116
116
|
// ── Style guide ────────────────────────────────────────────────────────────
|
|
117
117
|
const FLAT_STYLE_CORE_PATH = "writing/styleguide-core.md";
|
|
118
118
|
const STYLE_COMMENTS_PATH = "writing/comments.md";
|
|
119
|
+
const STYLE_POSTS_PATH = "writing/posts.md";
|
|
119
120
|
const LEGACY_AUDIENCE_ICP_PATH = "audience/icp.md";
|
|
120
121
|
const LEGACY_FAQS_CORE_PATH = "faqs/core.md";
|
|
122
|
+
function readPostWritingRules() {
|
|
123
|
+
const markdown = readFile(STYLE_POSTS_PATH);
|
|
124
|
+
if (!markdown)
|
|
125
|
+
return undefined;
|
|
126
|
+
return {
|
|
127
|
+
markdown,
|
|
128
|
+
updatedAt: latestUpdatedAt([STYLE_POSTS_PATH]),
|
|
129
|
+
};
|
|
130
|
+
}
|
|
121
131
|
function readStyleGuide(senderId, identityMemory) {
|
|
122
132
|
const corePath = senderPath(senderId, "styleguide-core.md", FLAT_STYLE_CORE_PATH);
|
|
123
133
|
const core = readFile(corePath);
|
|
@@ -200,7 +210,8 @@ function hasCoreIdentityMemory(identityMemory) {
|
|
|
200
210
|
identityMemory.decisionRules ||
|
|
201
211
|
identityMemory.changeLog ||
|
|
202
212
|
identityMemory.transcripts?.index ||
|
|
203
|
-
identityMemory.references?.index
|
|
213
|
+
identityMemory.references?.index ||
|
|
214
|
+
(identityMemory.references?.childIndexes?.length ?? 0) > 0);
|
|
204
215
|
}
|
|
205
216
|
function appendCoreSection(parts, sourcePaths, title, chunk) {
|
|
206
217
|
if (!chunk)
|
|
@@ -209,9 +220,12 @@ function appendCoreSection(parts, sourcePaths, title, chunk) {
|
|
|
209
220
|
sourcePaths.push(chunk.source.relativePath);
|
|
210
221
|
}
|
|
211
222
|
function appendCoreDirectoryIndex(parts, sourcePaths, title, directory) {
|
|
212
|
-
if (!directory
|
|
223
|
+
if (!directory)
|
|
213
224
|
return;
|
|
214
225
|
appendCoreSection(parts, sourcePaths, title, directory.index);
|
|
226
|
+
for (const childIndex of directory.childIndexes ?? []) {
|
|
227
|
+
appendCoreSection(parts, sourcePaths, title, childIndex);
|
|
228
|
+
}
|
|
215
229
|
}
|
|
216
230
|
function appendCompatibilitySection(parts, sourcePaths, section) {
|
|
217
231
|
parts.push(`## ${section.title} (${section.relativePath})\n\n${section.markdown.trim()}`);
|
|
@@ -229,11 +243,13 @@ function latestUpdatedAt(relativePaths) {
|
|
|
229
243
|
export function getEngageMemory(senderId) {
|
|
230
244
|
const identityMemory = readIdentityMemory({ connectedSenderId: senderId });
|
|
231
245
|
const styleGuide = readStyleGuide(senderId, identityMemory);
|
|
246
|
+
const postWritingRules = readPostWritingRules();
|
|
232
247
|
const provenSearches = readProvenSearches(senderId);
|
|
233
248
|
const trackedPeople = readTrackedPeople(senderId);
|
|
234
249
|
return {
|
|
235
250
|
memory: {
|
|
236
251
|
styleGuide,
|
|
252
|
+
postWritingRules,
|
|
237
253
|
provenSearches,
|
|
238
254
|
trackedPeople,
|
|
239
255
|
...identityMemory,
|
|
@@ -23,6 +23,7 @@ export interface IdentityMemoryChunk {
|
|
|
23
23
|
export interface IdentityMemoryDirectory {
|
|
24
24
|
source: IdentityMemorySource;
|
|
25
25
|
index?: IdentityMemoryChunk;
|
|
26
|
+
childIndexes?: IdentityMemoryChunk[];
|
|
26
27
|
}
|
|
27
28
|
export interface IdentityMemory {
|
|
28
29
|
identity?: IdentityMemoryChunk;
|
package/dist/identity-memory.js
CHANGED
|
@@ -81,5 +81,32 @@ function readDirectory(configsDir, relativePath) {
|
|
|
81
81
|
if (index) {
|
|
82
82
|
directory.index = index;
|
|
83
83
|
}
|
|
84
|
+
const childIndexes = readChildIndexes(configsDir, relativePath);
|
|
85
|
+
if (childIndexes.length > 0) {
|
|
86
|
+
directory.childIndexes = childIndexes;
|
|
87
|
+
}
|
|
84
88
|
return directory;
|
|
85
89
|
}
|
|
90
|
+
function readChildIndexes(configsDir, relativePath) {
|
|
91
|
+
const root = path.join(configsDir, relativePath);
|
|
92
|
+
const indexes = [];
|
|
93
|
+
function walk(currentFullPath, currentRelativePath) {
|
|
94
|
+
for (const entry of fs.readdirSync(currentFullPath).sort()) {
|
|
95
|
+
const entryFullPath = path.join(currentFullPath, entry);
|
|
96
|
+
const entryRelativePath = `${currentRelativePath}/${entry}`;
|
|
97
|
+
const stat = fs.statSync(entryFullPath);
|
|
98
|
+
if (stat.isDirectory()) {
|
|
99
|
+
walk(entryFullPath, entryRelativePath);
|
|
100
|
+
}
|
|
101
|
+
else if (stat.isFile() &&
|
|
102
|
+
entry === "INDEX.md" &&
|
|
103
|
+
entryRelativePath !== `${relativePath}/INDEX.md`) {
|
|
104
|
+
const chunk = readMarkdownChunk(configsDir, entryRelativePath);
|
|
105
|
+
if (chunk)
|
|
106
|
+
indexes.push(chunk);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
walk(root, relativePath);
|
|
111
|
+
return indexes;
|
|
112
|
+
}
|
|
@@ -2,7 +2,7 @@ import { copySenderConfig, getEngageMemory, migrateFlatConfigs, recordProvenSear
|
|
|
2
2
|
export const engageMemoryToolDefinitions = [
|
|
3
3
|
{
|
|
4
4
|
name: "get_engage_memory",
|
|
5
|
-
description: "Load backward-compatible engage memory from ~/.sellable/configs/: style guide, proven search keywords, tracked people, plus optional core identity/company memory. All data lives in readable markdown files the user can also edit directly. When senderId is provided, reads compatibility overrides from senders/{senderId}/ with flat-path fallback.",
|
|
5
|
+
description: "Load backward-compatible engage memory from ~/.sellable/configs/: style guide, post writing rules, proven search keywords, tracked people, plus optional core identity/company memory. All data lives in readable markdown files the user can also edit directly. When senderId is provided, reads compatibility overrides from senders/{senderId}/ with flat-path fallback.",
|
|
6
6
|
inputSchema: {
|
|
7
7
|
type: "object",
|
|
8
8
|
properties: {
|
package/package.json
CHANGED
|
@@ -107,8 +107,8 @@ This is based on what I found. If your site or LinkedIn is stale, tell me what
|
|
|
107
107
|
to update before I build the lead list.
|
|
108
108
|
```
|
|
109
109
|
|
|
110
|
-
Ask: "
|
|
111
|
-
must not include quoted first-message copy. If a message-shape hint is useful,
|
|
110
|
+
Ask: "Does this brief look right, and how should Sellable continue?" The early
|
|
111
|
+
brief must not include quoted first-message copy. If a message-shape hint is useful,
|
|
112
112
|
keep it as non-final bullets and say: "This is not the message yet; I will write
|
|
113
113
|
real examples after we find and filter leads."
|
|
114
114
|
|
|
@@ -120,8 +120,9 @@ watch-link handoff, then ask approve/revise.
|
|
|
120
120
|
## YOLO Mode
|
|
121
121
|
|
|
122
122
|
Enable YOLO when the user asks for yolo/autopilot, passes `--yolo` or
|
|
123
|
-
`mode=yolo`, selects `Approve + YOLO
|
|
124
|
-
guesses. If identity is missing, ask only for the LinkedIn
|
|
123
|
+
`mode=yolo`, selects `Approve brief + activate YOLO mode (Recommended)`, or asks
|
|
124
|
+
you to use best guesses. If identity is missing, ask only for the LinkedIn
|
|
125
|
+
profile URL; never
|
|
125
126
|
continue from a website/domain alone. Infer buyer, offer/CTA, proof, source,
|
|
126
127
|
filters, and message direction from evidence plus user directions; newest wins.
|
|
127
128
|
Set `interactionMode: "autonomous"` once `campaignId` exists. Auto-select
|
|
@@ -364,9 +364,10 @@ wants to "sell first." Say "sender and company" for the person/company context,
|
|
|
364
364
|
and ask what we should "pitch to prospects first" for the offer/CTA choice.
|
|
365
365
|
|
|
366
366
|
At the brief approval gate, do not add a negative list of future steps that are
|
|
367
|
-
not approved. Ask
|
|
368
|
-
|
|
369
|
-
|
|
367
|
+
not approved. Ask whether the brief looks right and how Sellable should continue:
|
|
368
|
+
activate YOLO mode, build the campaign with AI step by step, or revise the
|
|
369
|
+
brief. The YOLO option approves the brief, auto-decides the remaining setup
|
|
370
|
+
choices with best guesses, and still stops before final launch.
|
|
370
371
|
|
|
371
372
|
At message review, if the user asks for edits, write the revised template and
|
|
372
373
|
examples before asking approval again. Route the feedback back to Message
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":"v2.1-compact","workflow":"create-campaign-v2","principle":"CampaignOffer state/watch link are canonical; require brief/source/import, filters/message, Settings, explicit Start.","normalCustomerPath":"Use campaign state/MCP/watchNarration. Do not create, read, link, or surface local files. Print first watch link once.","legacyCompatibility":{"validationSubskill":"create-campaign-v2-validation","tailSubskill":"create-campaign-v2-tail","rule":"Legacy diagnostics are opt-in only, not active flow gates."},"commitGateChoices":["approve","revise-brief","revise-leads","revise-rubric","revise-messaging","abort"],"canonicalStateFields":["campaignId","watchUrl","campaignBrief","currentStep","watchNarration","interactionMode","providerSearchAssociation","selectedLeadListId","workflowTableId","filterChoice","leadScoringRubrics","messageDraftRecommendation","approvedMessageTemplate","senderIds","sequenceTemplate","runningState"],"watchNarrationTransitionContract":{"rule":"Every watched currentStep switch must include fresh watchNarration.","requiredFields":["stage","headline","visibleState","agentIntent","nextAction"],"copyMustInclude":["what just happened","current visible page/state","next user action"],"appliesToTools":["create_campaign","update_campaign","attach_recommended_sequence","start_campaign"],"oneTimeWatchLinkPolicy":"After initial brief handoff, must not print another watch-link handoff unless user asks or link recovery is needed."},"lazyReferences":{"watch":["references/watch-link-handoff.md","references/watch-guide-narration.md"],"source":["references/lead-validation-preview.md","references/step-13-import-leads.md"],"filter":["references/filter-leads.md"],"message":[],"tail":["references/sample-validation-loop.md","references/step-15-re-cascade.md","references/final-handoff-contract.md"]},"safetyBoundaries":["Do not call list_senders before Settings after message approval.","Do not import leads until Start Import is approved.","Do not queue cells until confirmed source rows exist in the campaign and the message/filter gates are satisfied.","Do not call start_campaign until the user explicitly confirms launch.","Do not use local files as durable state in normal customer runs."],"yoloMode":{"triggerPhrases":["yolo","--yolo","mode=yolo","autopilot","use best guesses","use best estimates","answer for me","just run it","yolo autopilot"],"identityRequestOnlyWhenMissing":true,"operatorDirectionsRule":"Treat freeform directions supplied at invocation or later as durable operator directions; newest conflicting direction wins.","setInteractionModeAfterCampaignCreate":"autonomous","autoSelectsPreLaunchChoices":["campaign focus","brief approval","source plan","Start Import approval","filters vs skip filters","filter rubric approval","message template approval when recommendation is approve-message","generated message review when quality floor passes","sender selection when exactly one connected sender is safe"],"mustPauseFor":["missing LinkedIn profile URL or handle","missing credentials or required data","ambiguous choice with no reasonable estimate","source/message/filter quality floor failure","final live launch confirmation"],"neverAutoStart":true},"steps":[{"id":"bootstrap","label":"Bootstrap","onEnter":[{"tool":"bootstrap_create_campaign","requiredValues":{"flowVersion":"v2"}},{"tool":"get_subskill_prompt","requiredValues":{"subskillName":"create-campaign-v2"}}],"allowedTools":["bootstrap_create_campaign","get_auth_status","get_active_workspace","get_subskill_prompt","AskUserQuestion","request_user_input"],"doNotAllow":["create_campaign","list_senders","save_rubrics","import_leads","confirm_lead_list","update_campaign","queue_cells","start_campaign"],"waitFor":"bootstrap_complete","transitions":{"bootstrap_complete":"brief-interview"}},{"id":"brief-interview","label":"Client, offer, research, and brief","onEnter":[{"action":"resolve_campaign_identity_before_strategy_packet","allowedTools":["fetch_company","fetch_linkedin_profile","WebFetch","WebSearch"],"mustNotInferFromNameOnly":true,"doNotPresentSenderPickerBeforeIdentityInference":true,"fallback":"Ask only: \"What is your LinkedIn profile URL or handle?\" Normalize handles to LinkedIn profile URLs. If not a profile, ask again for the person profile URL before strategy questions.","requiresLinkedInProfileUrl":true,"doNotAcceptAsIdentityInput":["non-profile URL","company page","company/name-only input without a profile handle"],"acceptsLinkedInProfileHandle":true,"normalizeLinkedInProfileHandleToUrl":true},{"action":"run_campaign_identity_research_before_strategy_packet","target":"research-sender","requiredCompletion":"complete_sender_research","allowedTools":["get_subskill_prompt","fetch_linkedin_profile","fetch_company","fetch_linkedin_posts","fetch_company_posts","WebFetch","WebSearch","complete_sender_research"],"requiredInput":"LinkedIn profile URL or handle"},{"action":"confirm_target_before_strategy_packet","uses":"request_user_input","singleChoice":true,"question":"Who should we target first?","options":["Use Sellable's researched recommendation","Choose a different buyer","I'm not sure yet"],"copyMustInclude":["research basis","recommendation is editable","who to target"],"requiredOption":"Other / custom","neverCollectOpenTextWithStructuredQuestion":true,"skipIf":"user already supplied a clear target or yolo_mode with any reasonable best-estimate target"},{"action":"confirm_current_offer_before_strategy_packet","uses":"request_user_input","question":"What should we pitch to prospects first?","options":["Use Sellable's researched recommendation","Use my current pitch","Shape the pitch together"],"copyMustInclude":["research basis","public LinkedIn research may be stale","recommendation is editable","pitch to prospects"],"skipIf":"user already supplied a clear current offer or yolo_mode with any reasonable researched recommendation"},{"action":"confirm_trust_proof_before_strategy_packet","uses":"request_user_input","singleChoice":true,"question":"What is the strongest credibility signal we can lead with?","options":["Use Sellable's recommended proof","Use customer proof or a case study","Use founder or company credibility"],"copyMustInclude":["build trust with prospects","recommendation is editable","proof to use or avoid"],"requiredOption":"Other / custom","skipIf":"user already supplied clear proof to use or avoid, or yolo_mode with any reasonable researched proof"},{"action":"choose_prospect_source_path_before_strategy_packet","uses":"request_user_input","singleChoice":true,"question":"How should we find prospects?","options":["Find prospects for me","Use a CSV of LinkedIn profiles","Use a CSV of company domains"],"copyMustInclude":["prospects","no one added yet","find prospects for me"],"skipIf":"user already supplied a source path or yolo_mode with any reasonable source path"},{"action":"infer_strategy_packet_in_yolo_mode","when":"yolo_mode","skipStructuredSetupQuestions":true,"inferFields":["buyer segment","offer/CTA","proof to use or avoid","lead source","filter choice","message direction"],"sourceOfTruth":"identity/company lookup plus operator directions","mustStateAssumptionsInBrief":true},{"action":"render_supplied_setup_receipt_before_brief_when_setup_questions_skip","when":"any setup question is skipped because the user already supplied or yolo mode inferred identity, target, offer, proof, or source","output":"normal chat receipt before the campaign brief or watch-link handoff","copyMustInclude":["Accepted LinkedIn input: normalized to the required LinkedIn profile URL.","Offer path: supplied current offer or Sellable's researched recommendation; you can replace it with your current offer."],"copyMustNotInclude":["Campaign Identity"]},{"action":"create_watchable_campaign_shell_with_v1_brief","tool":"create_campaign","requiredFields":["name","campaignBrief","clientProspectId or senderLinkedinUrl","currentStep","watchNarration"],"requiredValues":{"currentStep":"create-offer","watchNarration.stage":"brief"},"capture":["campaignId","watchUrl"],"canonicalStateWrites":["campaignId","watchUrl","campaignBrief","currentStep:create-offer"]},{"action":"surface_campaign_shell_watch_link","watchUrlSource":"create_campaign.watchUrl","requiredWatchUrlShape":"exact create_campaign.watchUrl; direct /campaign-builder/{campaignId}?mode={claude|codex} URL with workspaceId and token","copyMustInclude":["WATCH CODEX BUILD","Open live campaign builder","Keep this chat open.","approval questions here"],"immediateNextMainChatLine":"Ask for brief approval now. After approval, say: \"Brief approved. Next, we'll choose where to find buyers. I won't add anyone yet.\"","codexBrowserHandoff":{"openWhenAvailable":false,"printWatchLinkOnly":true,"tellUserCommandEnterOrClick":false,"mustNotUseBrowserAutomation":true,"fallbackMustNotClaimInspection":true},"copyMustAvoid":["bare /campaign-builder/{campaignId} URL","derived watch URL","second watch-link handoff","shell open command","Next, we'll choose where to find buyers"],"oneTimeOnly":true}],"allowedTools":["get_subskill_prompt","fetch_company","fetch_company_posts","fetch_linkedin_profile","fetch_linkedin_posts","complete_sender_research","create_campaign","AskUserQuestion","request_user_input"],"doNotAllow":["list_senders","save_rubrics","import_leads","confirm_lead_list","queue_cells","start_campaign"],"waitFor":["campaign_shell_created","brief_ready","confirm_with_user"],"transitions":{"campaign_shell_created":"brief-review","brief_ready":"brief-review","confirm_with_user":"brief-review"}},{"id":"brief-review","label":"Brief review","onEnter":[{"action":"render_brief_inline","minimumVisibleDetail":"campaign direction, buyer, offer, proof, source plan, safety gates","copyMustInclude":["This brief is based on Sellable's research and your answers.","If your site or LinkedIn is stale","Do you agree with this researched campaign direction?"],"copyMustNotInclude":["quoted first-message copy","Message Angle as final proof","This approval covers","does not approve adding people","does not approve scraping posts","selecting a sender","attaching a sequence","Campaign Identity","what should this campaign sell first","sell first","[from you]","[from LinkedIn]","[from website]","[from case study]","[Sellable recommendation]"],"messageHintRule":"If any message-shape hint remains, keep it as non-final bullets and say real examples come after leads are found and filtered.","approvalBoundaryRule":"Keep this gate positive and short. Do not list later steps this approval does not cover; ask approve/revise and then move to choosing where to find buyers.","sectionLabelRule":"Use ## Sender and Company for the person/company context; never render ## Campaign Identity.","skipIf":"the full brief was already rendered in the current shell-creation handoff before create_campaign returned","renderOnceRule":"The brief must be visible exactly once before approval. Do not render it again immediately after shell creation; append the one watch-link handoff and ask the approval question.","copyMustAvoid":["duplicate brief render after shell creation","second Watch link:"]},{"action":"ask_brief_choice","uses":"request_user_input","choices":["Approve brief","Revise brief","Approve + YOLO autopilot setup"],"skipIf":"yolo_mode; auto_continue after rendering assumptions unless brief quality floor fails","question":"Do you agree with this researched campaign direction?","autonomousChoice":"Approve + YOLO autopilot setup","choiceDescriptions":{"Approve + YOLO autopilot setup":"Use best guesses to finish setup and stop before final launch."}}],"requiredCampaignState":["campaignId","watchUrl","campaignBrief","currentStep"],"allowedTools":["AskUserQuestion","request_user_input","update_campaign"],"doNotAllow":["create_campaign","list_senders","import_leads","confirm_lead_list","queue_cells","start_campaign"],"waitFor":["user_brief_confirmed","revise_brief","auto_continue"],"transitions":{"user_brief_confirmed":"find-leads","revise_brief":"brief-interview","auto_continue":"find-leads"}},{"id":"find-leads","label":"Find leads","sourceSelectionFunnel":{"preScoutRecommendationGate":{"required":true,"label":"Find buyers plan approval","mustHappenBefore":["get_provider_prompt","search_signals","search_sales_nav","search_prospeo_companies","confirm_prospeo_company_accounts","search_prospeo","fetch_post_engagers"],"show":["buyer groups or places we could check","best place to start","why the right buyers are likely to be there","what signs the next search will check","where to look next if the first place is too thin","what approval authorizes"],"approvalAuthorizes":"looking for the best places to find buyers; no one added yet","explanationMustInclude":["where the right buyers are already talking","why that place fits this campaign","enough likely prospects","what counts as a good sign","where to look next if thin","I won't add anyone yet","choose where to find buyers"],"yoloMode":{"autoApproveRecommendedLane":true,"mustShowAssumedChoice":true,"pauseIfConfidenceLow":true},"customerLanguage":{"readability":"grade-5","requiredHeading":"Find Buyers Plan","prefer":["place to look","best place to start","people to check","prospects","I won't add anyone yet"],"avoid":["lane","source scouting","provider","precision/scale tradeoff","evidence quality","pilot volume","ICP","workflow pain","lead-source scouting","not importing leads"],"termRule":{"buyers":"target market","peopleToCheck":"raw reactions/comments before fit is known","prospects":"likely usable people after fit","leads":"campaign rows"}},"questionOrder":"update_campaign to pick-provider, render ## Find Buyers Plan in normal chat without repeating the watch link, then open the structured approval question; never ask first or show the plan after approval","copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL","watch-link handoff"],"approvalFreshnessRule":"Only source approval opened after the visible Find Buyers Plan, or explicit source choice after it, sets source_lane_approved. Do not reuse brief approval."},"defaultWhenSourceUnspecified":["signal-discovery","sales-nav-recent-active","sales-nav-general","prospeo"],"parallelAllowedOnlyWhen":["user explicitly requested source comparison"]},"onEnter":[{"tool":"update_campaign","requiredValues":{"currentStep":"pick-provider","watchNarration.stage":"find-leads","watchNarration.headline":"Review where to find buyers","watchNarration.visibleState":"The app is showing source selection before lead finding starts.","watchNarration.agentIntent":"Codex is explaining where it recommends looking first and where it will look next if that is too thin.","watchNarration.nextAction":"Approve where to look first or choose another place","watchNarration.safety":"Only deciding where to look. No one is added yet."}},{"action":"render_find_buyers_plan_inline","oneShot":true,"requiredPrecondition":"currentStep=pick-provider has been saved with update_campaign","mustHappenAfter":["update_campaign currentStep=pick-provider"],"mustHappenBefore":["ask_find_buyers_plan_choice","get_provider_prompt","search_signals","search_sales_nav","search_prospeo_companies","confirm_prospeo_company_accounts","search_prospeo","fetch_post_engagers"],"output":"normal chat text before any request_user_input approval panel","requiredHeading":"Find Buyers Plan","requiredInlineFields":["plain-language buyer groups or places for this campaign","best place to start","why the right buyers are likely to be there","what signs the next search will check","where to look next if the first place is too thin","what approval authorizes"],"copyMustInclude":["Find Buyers Plan","best place to start","I won't add anyone","choose where to find buyers","I won't add anyone yet"],"copyMustAvoid":["lane","source scouting","provider","precision/scale tradeoff","evidence quality","pilot volume","ICP","workflow pain","lead-source scouting","not importing leads","Watch link:","campaign-builder URL","repeat the watch URL","watch-link handoff"],"approvalBoundary":"Explain what approval authorizes before asking: looking for the best places to find buyers; no one is added yet.","linkPolicy":"Do not include another watch link. The first brief handoff already gave the user the live app URL."},{"action":"ask_find_buyers_plan_choice","uses":"request_user_input","oneShot":true,"requiredPrecondition":"render_find_buyers_plan_inline completed in normal chat after pick-provider state was saved; no earlier approval can satisfy this","skipIf":"source_lane_approved was set by ask_find_buyers_plan_choice after the visible Find Buyers Plan, leadSourceProvider was explicitly chosen by the user after that plan, or yolo_mode auto-selected source_lane_approved after showing the assumed choice","choices":["Start with LinkedIn posts","Start with active LinkedIn profiles","Start with company/contact search","Choose a different place","Pause here"],"approvalChoiceLabelsByProvider":{"signal-discovery":"Start with LinkedIn posts","sales-nav":"Start with active LinkedIn profiles","prospeo":"Start with company/contact search"},"approvalState":"source_lane_approved","copyMustInclude":["Approve this plan for where to find buyers?","Start with LinkedIn posts"],"mustNotHappenBefore":["render_find_buyers_plan_inline"],"approvalStateRule":"Set source_lane_approved here after the visible plan; never from brief approval, generic approval, pre-plan provider state, or artifacts."},{"action":"persist_provider_search_step_before_search","tool":"update_campaign","requiredPrecondition":"source_lane_approved set after the visible Find Buyers Plan approval question","providerCurrentStepMap":{"signal-discovery":"signal-discovery","sales-nav":"sales-nav","prospeo":"prospeo"},"requiredValues":{"leadSourceType":"new","leadSourceProvider":"approved provider","currentStep":"provider-specific current step","watchNarration.stage":"find-leads","watchNarration.headline":"Searching the approved place","watchNarration.safety":"Looking only. No one is added yet."},"mustRunBefore":["search_signals","search_sales_nav","search_prospeo_companies","confirm_prospeo_company_accounts","search_prospeo","fetch_post_engagers"]},{"action":"run_parent_thread_source_funnel","requiredPrecondition":"source_lane_approved after visible Find Buyers Plan approval","mode":"parent_thread_inline_sequential","sourceAgents":"forbidden in normal create-campaign","loadsOnlyActiveProviderPrompt":true,"defaultOrder":["signal-discovery","sales-nav-recent-active","sales-nav-general","prospeo"],"stopOnFirstViableUnlessComparisonRequested":true}],"requiredCampaignState":["campaignId","campaignBrief","currentStep"],"allowedTools":["get_provider_prompt","lookup_sales_nav_filter","search_sales_nav","search_prospeo_companies","confirm_prospeo_company_accounts","search_prospeo","search_signals","select_promising_posts","fetch_post_engagers","fetch_company","fetch_linkedin_profile","load_csv_linkedin_leads","load_csv_domains","get_rows_minimal","update_campaign","AskUserQuestion","request_user_input","get_campaign_navigation_state"],"doNotAllow":["create_campaign","list_senders","save_rubrics","import_leads","confirm_lead_list","queue_cells","start_campaign","Task","spawn_agent"],"waitFor":["lead_review_ready","revise_brief","confirm_with_user"],"transitions":{"lead_review_ready":"lead-review","revise_brief":"brief-interview","confirm_with_user":"lead-review"}},{"id":"lead-review","label":"Start Import approval","onEnter":[{"action":"show_source_decision_card","requiredInlineFields":["primary source and exact filters/recipe","Start Import action awaiting approval","for Signal Discovery: compact Source Recommendation in plain language with selected posts, people to check, likely prospects, first review, and fallback","for Signal Discovery: recommended scrape post count and target people-to-check volume","first campaign review size, clearly separate from source sampling","runner-up and why it lost","raw volume","sampled people","sampled fits as n/N plus percentage/range","estimated usable prospects","cleanup risk","what happens after Start Import"],"copyMustAvoid":["lead-source scouting","source scouting","headline-fit","engagers needed","execution slice","ICP","good buyers","real buyers","This approval covers","It does not approve","does not approve filters","filters, messages, sender selection","sender selection, sequence, or sending","Watch link:","campaign-builder URL","repeat the watch URL"],"approvalBoundaryRule":"Use positive Start Import copy only. Include: \"After approval, I will build the source list, add it to the campaign, and review the first 15 leads before we scale.\" Never list future non-approvals.","linkPolicy":"Do not include another watch link in Source Recommendation; describe the Start Import decision only."},{"action":"ask_source_review_choice","uses":"request_user_input","choices":["Approve scraping N recommended LinkedIn posts","Start Import for the approved source list","Revise source","Pause here"],"approvalChoiceLabelsByProvider":{"signal-discovery":"Approve scraping {scrapePostCount} recommended LinkedIn posts?","sales-nav":"Start Import for the approved Sales Nav source list","prospeo":"Start Import for the approved Prospeo source list"},"postApprovalContract":{"singleUseApproval":true,"doNotRepeatAfterApproval":["Source Recommendation","show_source_decision_card","ask_source_review_choice"],"requiredNextActionAfterApproval":"ack once; call import_leads immediately","signalDiscoveryNextTool":"import_leads({ provider: \"signal-discovery\", targetEngagerCount, maxPostsToScrape, confirmed: true })"},"autoSelectIf":"yolo_mode and projected usable pool clears the source quality floor","copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"]}],"requiredCampaignState":["campaignId","campaignBrief","providerSearchAssociation"],"allowedTools":["AskUserQuestion","request_user_input","update_campaign"],"doNotAllow":["create_campaign","list_senders","save_rubrics","import_leads","confirm_lead_list","queue_cells","start_campaign"],"waitFor":["lead_review_confirmed","revise_leads","confirm_with_user","auto_continue"],"transitions":{"lead_review_confirmed":"auto-execute-leads","revise_leads":"find-leads","confirm_with_user":"auto-execute-leads","auto_continue":"auto-execute-leads"}},{"id":"auto-execute-leads","label":"Materialize confirmed source list","currentStepValue":"auto-execute-leads","reference":"references/step-13-import-leads.md","onEnter":[{"tool":"get_subskill_prompt","requiredValues":{"subskillName":"create-campaign-v2-tail"}},{"tool":"import_leads","requiredFields":["campaignOfferId","selected source/list","sourceListTarget or targetEngagerCount"],"requiredValues":{"salesNavProspeoDefaultSourceListTarget":1000,"signalDiscoveryDefaultEngagerTarget":1500},"modeAddHandshake":{"firstCallReturns":"needsModeSelection when adding to an existing campaign-attached list","requiredFollowup":"retry with mode=add after explicit user/source-state compatibility is confirmed"},"surfaceDedupRatio":true},{"tool":"wait_for_lead_list_ready","repeatUntil":"ready_true_or_cancelled_or_import_failed","requiredValues":{"requireComplete":true},"onImportStillRunning":"re-run wait_for_lead_list_ready; do not call confirm_lead_list just because rows exist","partialOverrideRule":"Only if the user explicitly asks to keep going early may the next confirm_lead_list call pass allowPartialSourceList: true."},{"tool":"confirm_lead_list","requiredFields":["campaignOfferId","selectedLeadListId","reviewBatchLimit"],"requiredValues":{"reviewBatchLimit":15},"capture":["workflowTableId","reviewBatchRowIds"],"requiredPrecondition":"wait_for_lead_list_ready returned ready:true, unless the user explicitly asked to keep going early with a partial list","partialOverrideField":"allowPartialSourceList:true only after explicit user early-continue instruction","mustNotRunWhen":["wait_for_lead_list_ready reason=import_still_running and no explicit early-continue instruction","wait_for_lead_list_ready reason=cancelled","wait_for_lead_list_ready reason=import_failed","missing import job metadata"]},{"tool":"wait_for_campaign_table_ready"},{"tool":"get_rows_minimal","requiredValues":{"tableId":"{workflowTableId}","limit":15},"capture":["reviewBatchRowHash"]},{"action":"summarize_review_batch_and_advance_to_filter_choice","maxCustomerCopyLines":4,"copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"],"linkPolicy":"Ask add filters vs skip filters without repeating the watch link.","purpose":"ask filter choice immediately"}],"requiredCampaignState":["campaignId","providerSearchAssociation","selectedLeadListId"],"allowedTools":["get_subskill_prompt","import_leads","wait_for_lead_list_ready","confirm_lead_list","wait_for_campaign_table_ready","get_rows_minimal","update_campaign","AskUserQuestion","request_user_input"],"doNotAllow":["get_post_find_leads_scout_registry","Task","spawn_agent","list_senders","queue_cells","start_campaign","enrich_with_prospeo","bulk_enrich_with_prospeo","save_rubrics"],"waitFor":"source_list_confirmed_and_review_sample_ready","transitions":{"source_list_confirmed_and_review_sample_ready":"filter-choice","escalation_triggered":"escalation"},"hardRules":["import_leads_then_repeated_wait_for_lead_list_ready_then_confirm_lead_list_then_wait_for_campaign_table_ready","partial_source_rows_do_not_unblock_confirm_lead_list_without_explicit_user_early_continue","cancelled_or_failed_source_import_stops_before_campaign_table_copy"]},{"id":"filter-choice","label":"Filter choice","currentStepValue":"filter-choice","onEnter":[{"tool":"update_campaign","requiredValues":{"currentStep":"filter-choice","watchNarration.stage":"fit-message"}},{"action":"ask_filter_choice","uses":"request_user_input","choices":["Use filters","Skip filters","Revise source"],"autoSelectIf":"yolo_mode; choose filters unless source is tightly curated or user directed otherwise","copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"],"copyMustInclude":["source rows are in the campaign","add fit filters before message drafting or skip filters"]}],"hardRules":["ask_filter_choice_immediately_after_review_batch_import","do_not_call_get_subskill_prompt_before_filter_choice","do_not_call_get_subskill_asset_before_filter_choice","do_not_call_get_post_find_leads_scout_registry_before_filter_choice","do_not_spawn_prospect_setup_before_filter_choice"],"requiredCampaignState":["campaignId","campaignBrief","selectedLeadListId","workflowTableId"],"allowedTools":["AskUserQuestion","request_user_input","update_campaign","get_campaign_navigation_state"],"doNotAllow":["get_subskill_prompt","get_subskill_asset","get_post_find_leads_scout_registry","Task","spawn_agent","create_campaign","list_senders","import_leads","confirm_lead_list","queue_cells","start_campaign","generate_messages"],"waitFor":["filters_enabled","filters_skipped","revise_leads"],"transitions":{"filters_enabled":"prospect-setup","filters_skipped":"message-generation","revise_leads":"find-leads"}},{"id":"prospect-setup","label":"Filter setup + message drafting","onEnter":[{"action":"persist_add_filters_approval","tool":"update_campaign","when":"filters_enabled","requiredFields":["campaignId","enableICPFilters","currentStep","watchNarration"],"requiredValues":{"enableICPFilters":true,"currentStep":"create-icp-rubric","watchNarration.stage":"fit-message","watchNarration.headline":"Create filter rules","watchNarration.visibleState":"Filter Rules is visible while Codex drafts and saves filters in the parent thread.","watchNarration.nextAction":"Review saved filter rules"},"mustRunBefore":["get_post_find_leads_scout_registry","launch_message_drafting_after_filter_choice","load_filter_reference_in_parent","save_rubrics","ask_filter_rubric_review_choice"]},{"tool":"get_post_find_leads_scout_registry","returns":["post-find-leads-message-scout"],"rule":"normal post-import registry exposes only Message Drafting; filters are parent-thread MCP work","mustRunBefore":["launch_message_drafting_after_filter_choice","get_subskill_asset:references/filter-leads.md","save_rubrics","ask_filter_rubric_review_choice"],"registryOnlyIsNotLaunch":true},{"action":"launch_message_drafting_after_filter_choice","mode":"background_agent_when_host_supports_subagents","target":"post-find-leads-message-scout","when":"filters_enabled or filters_skipped after filter-choice answer","doesNotQueueCells":true,"customerNarration":"Message Drafting is preparing.","inputs":["campaignId","workflowTableId","brief/source summary","3-5 sample workflow-table rows","optional campaignName/selectedLeadListId/filterChoice"],"forbiddenSiblings":["source/filter workers"],"handoffCompression":"lean_message_drafting_handoff_no_hashes_no_counts_no_full_row_data","requiredConcreteToolCall":"Task or spawn_agent when host supports background agents","registryOnlyIsNotLaunch":true,"mustRunBefore":["get_subskill_asset:references/filter-leads.md","load_filter_reference_in_parent","save_rubrics","ask_filter_rubric_review_choice"],"yoloPermissionRule":"do not ask the user for worker permission in YOLO.","startProof":"Message Drafting counts as started only after Task/spawn_agent.","noBackgroundFallback":"Start the same full message branch inline before filter reference or block.","blockedIfSkipped":"Do not proceed to get_subskill_asset(filter-leads), save_rubrics."},{"tool":"get_subskill_asset","action":"load_filter_reference_in_parent","when":"filters_enabled","requiredValues":{"subskillName":"create-campaign-v2","assetPath":"references/filter-leads.md"},"rule":"Parent thread uses this reference to draft production rubrics; do not spawn a filter worker."},{"tool":"save_rubrics","when":"filters_enabled","requiredFields":["campaignOfferId","leadScoringRubrics"],"writesCampaignState":"leadScoringRubrics","requiredSideEffects":{"enableICPFilters":true,"currentStep":"create-icp-rubric","watchNarration.headline":"Filter rules saved for review"}},{"action":"ask_filter_rubric_review_choice","uses":"request_user_input","choices":["Approve filters","Revise filters","Pause"],"copyMustInclude":["Filter rules saved for review","These rules prevent wasted sends before I score/import the list.","Recommended because of the researched ICP, source sample, and repeated false-positive patterns.","one pass example and one block example when available","approval is for the saved filter rules","Approve these filter rules."],"autoSelectIf":"yolo_mode and parent saved production-shaped rubrics","copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"],"requiredPrecondition":"save_rubrics succeeded or active campaign rubrics verified"}],"requiredCampaignState":["campaignId","campaignBrief","selectedLeadListId","workflowTableId"],"allowedTools":["get_post_find_leads_scout_registry","get_subskill_asset","save_rubrics","get_campaign","get_rows_minimal","update_campaign","get_campaign_navigation_state","AskUserQuestion","request_user_input","Task","spawn_agent"],"doNotAllow":["create_campaign","list_senders","import_leads","confirm_lead_list","queue_cells","start_campaign","check_rubric","generate_messages"],"waitFor":["filter_rubrics_approved","revise_leads","revise_rubric","revise_messaging"],"hardRules":["after_save_rubrics_currentStep_must_stay_create-icp-rubric_until_filter_approval","filter_approval_required_before_apply-icp-rubric_or_queue_campaign_cells","do_not_move_browser_to_messages_until_filter_leads_step_is_current_or_filters_are_explicitly_skipped","no_prospect_setup_worker_or_deep_prompt_before_filter_choice","message_drafting_after_filter_choice","message_drafting_no_cells","only_message_drafting_may_spawn_background_agent","filters_are_parent_thread_mcp_work","no_post_find_leads_filter_scout_in_normal_path","registry_call_is_not_message_drafting_launch","yolo_must_not_ask_permission_for_message_drafting_worker","message_drafting_must_start_before_filter_reference_or_save_rubrics","if_background_agent_unavailable_start_full_message_branch_inline_before_filters_or_block"],"transitions":{"filter_rubrics_approved":"message-generation","revise_leads":"find-leads","revise_rubric":"filter-rubric","revise_messaging":"message-generation","confirm_with_user":"message-review"}},{"id":"filter-rubric","label":"Rubric revision","onEnter":[{"tool":"get_subskill_asset","requiredValues":{"subskillName":"create-campaign-v2","assetPath":"references/filter-leads.md"}},{"tool":"save_rubrics","requiredFields":["campaignOfferId","leadScoringRubrics"],"writesCampaignState":"leadScoringRubrics","requiredSideEffects":{"enableICPFilters":true,"currentStep":"create-icp-rubric","watchNarration.headline":"Filter rules saved for review"}},{"action":"ask_filter_rubric_review_choice","uses":"request_user_input","choices":["Approve filters","Revise filters","Pause"],"copyMustInclude":["These rules prevent wasted sends before I score/import the list.","approval object is filter rules only","approval is for the filter rules","Approve these filter rules."],"autoSelectIf":"yolo_mode and rubrics are production-shaped","copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"]}],"requiredCampaignState":["campaignId","workflowTableId"],"allowedTools":["get_subskill_asset","save_rubrics","AskUserQuestion","request_user_input"],"doNotAllow":["create_campaign","list_senders","import_leads","confirm_lead_list","update_campaign","queue_cells","start_campaign","check_rubric"],"waitFor":["filter_rubrics_approved","revise_leads","confirm_with_user","auto_continue"],"transitions":{"filter_rubrics_approved":"message-generation","revise_leads":"find-leads","confirm_with_user":"message-generation","auto_continue":"message-generation"}},{"id":"message-generation","label":"Message drafting","onEnter":[{"action":"set_message_review_visible_step_by_filter_choice","tool":"update_campaign","branchRules":["yes after filter approval: currentStep=apply-icp-rubric; wait on Filter Leads","no: currentStep=messages; message review"],"watchNarration.stage":"fit-message"},{"action":"run_or_reconcile_message_draft_builder","target":"post-find-leads-message-scout","toolCallRequiredBeforeDraft":["join already-running Message Drafting when prospect-setup launched it","run Message Drafting via post-find-leads-message-scout only if missing; yolo counts","handoff lean basis only: campaignId, workflowTableId, brief summary, source summary/source-use rule, 3-5 sample rows","do not paste copied row counts, brief hashes, review-batch hashes, full reviewBatchRowIds, broad row data, or local debug artifacts","branch loads current brief/context, full generate-messages prompt, and every required message asset referenced by generate-messages Mode 0","return labeled Markdown only: templateRecommendation, tokenFillRules, renderedGoodSample, status, approveOrReviseRecommendation, validationStatus, outputAt/outputHash, blocked/retry detail","do not return renderedFallbackSample, risk notes, or qaReceipt on the normal happy path","if no yolo and permission missing, ask once before parent inline fallback","use generic gpt-5.5 xhigh Message Drafting agent if named unavailable"],"stateSource":"message branch-loaded live campaign/table state plus parent-provided sample rows","outputState":"messageDraftRecommendation with internal validation passed","revisionMode":{"when":"revise_messaging, message QA request, or latest user message requests template changes before approve-message","route":"send feedback/QA/rewrite request to post-find-leads-message-scout or generic gpt-5.5 xhigh Message Drafting branch","requiredInputs":["latest user feedback","current messageDraftRecommendation and basisToken/outputHash","lean campaign/table basis and brief summary; branch reloads live state"],"forbiddenParentActions":["rewrite template in parent","QA template in parent from memory","update_campaign_brief before approve-message"],"output":"revised messageDraftRecommendation rendered before request_user_input"},"onlyMessageBackgroundAgent":true,"forbiddenSiblingAgents":["source-scout-*","any filter background worker"],"handoffCompression":"lean_message_drafting_handoff_no_hashes_no_counts_no_full_row_data","finalGateRequiredBeforeReady":"Post-generation final gate: After candidate generation/revision, load get_subskill_prompt({ subskillName: \"create-campaign-v2-validation\" })."}],"requiredCampaignState":["campaignId","campaignBrief","selectedLeadListId","workflowTableId"],"allowedTools":["get_subskill_prompt","get_campaign","get_rows_minimal","update_campaign","Task","spawn_agent"],"toolRules":["message_drafting_agent_first_no_silent_parent_fallback; yolo counts","on_filters_enabled_path_parent_must_not_enter_message_review_until_save_rubrics_success_and_filter_approval","brief.md, lead-review.md, and lead-sample.json are optional debug context only.","messageDraftRecommendation returns templateRecommendation, tokenFillRules, renderedGoodSample, status, approveOrReviseRecommendation, validationStatus, outputAt, outputHash, and error/retry detail.","messageDraftRecommendation does not return renderedFallbackSample, risk notes, or qaReceipt on the normal happy path.","If campaign/table/sample basis does not match, classify the output stale or blocked.","message_revision_feedback_routes_to_message_drafting_agent_not_parent","message_qa_routes_to_message_drafting_agent_not_parent","message_drafting_loads_full_generate_messages_prompt_all_chunks","message_drafting_loads_all_reference_assets_via_get_subskill_asset","message_drafting_loads_create_campaign_v2_validation_prompt_before_return","message_drafting_handoff_uses_lean_basis_no_hashes_no_counts_no_full_row_id_list","message_drafting_loads_current_campaign_brief_before_drafting","parent_may_load_generate_messages_only_for_explicitly_approved_inline_fallback","before return branch loads create-campaign-v2-validation prompt and runs internal validation"],"doNotAllow":["create_campaign","list_senders","save_rubrics","import_leads","confirm_lead_list","queue_cells","start_campaign","generate_messages","AskUserQuestion","request_user_input"],"waitFor":["message_validation_ready","revise_rubric","revise_messaging"],"transitions":{"message_validation_ready":"message-review","revise_rubric":"filter-rubric","revise_messaging":"message-generation"},"revisionLoop":{"trigger":"user gives copy feedback, asks for QA/update, or chooses revise-messaging","required":["route latest user feedback or QA request to Message Drafting with current recommendation and live campaign/table state","produce a new messageDraftRecommendation with revisionNotes or validationStatus","render revised ## Message Template, ## Rendered Example, and What changed before asking approval"],"blocked":["asking whether an unseen new version is better","reusing the old template after acknowledging feedback","rewriting the template in the parent thread","update_campaign_brief before approve-message","request_user_input or AskUserQuestion during message-generation","QAing the template in the parent thread"],"outputState":"messageDraftRecommendation"},"hardRules":["message_revision_must_render_new_template_before_approval_question","message_revision_saves_only_after_approve_message","message_generation_must_not_call_request_user_input","message_generation_must_transition_to_message_review_only_after_renderable_draft_state","message_drafting_agent_first_no_silent_parent_fallback","message_revision_routes_to_message_drafting_agent","message_qa_routes_to_message_drafting_agent","filter_path_requires_saved_filters_before_message_review","only_message_drafting_may_spawn_background_agent"]},{"id":"message-review","label":"Message review","onEnter":[{"action":"render_message_review_from_state","requiredState":["campaignBrief","selectedLeadListId","workflowTableId","messageDraftRecommendation"],"requiredVisibleLabels":["## Message Template","## Rendered Example","Good token fill:","My take:","Question: approve-message or revise-messaging?","Recommendation:"],"doNotShowByDefault":["Rendered fallback sample","Concerns","QA receipt","Token Notes","Good omit / fallback","Bad fill to avoid","Token Adherence Table"],"internalPersistenceOnly":["Token Fill Rules","Token Fill Examples","fallback guidance","bad-fill avoidance notes","validation notes","QA details"],"copyMustInclude":["one recommended template","one rendered sample","token rules and fallbacks","full list remains paused"],"copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"],"revisionRenderRule":"After revise_messaging, QA, or copy feedback, route feedback to Message Drafting and show revised template, rendered example, and What changed before ask_message_review_choice; never ask if an unseen version is better.","parentMustNot":["rewrite template from memory","invent validation summary without Message Drafting output","render qaReceipt, risk notes, or renderedFallbackSample on the normal happy path"]},{"action":"ask_message_review_choice","uses":"request_user_input","choices":["approve-message","revise-messaging"],"copyMustInclude":["approve this message template and continue","template approval only locks the draft direction; final Start still controls sending"],"autoSelectIf":"yolo_mode and Recommendation is approve-message; revise autonomously when Recommendation is revise-messaging","copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"],"requiredPrecondition":"current or revised template and rendered example are visible in this turn","revisionPrecondition":"after revise_messaging, revised template is visible before this question"},{"action":"sync_approved_message_set_to_campaign_brief","tool":"update_campaign_brief","when":"after approve-message","requiredFields":["campaignId","approvedMessageTemplate","Token Fill Rules","Token Fill Examples"],"writesCampaignState":"approvedMessageTemplate","revisionApprovalRule":"If the user requested revisions, write only the latest Message Drafting-approved revised template and token rules after approve-message.","forbiddenBefore":"approve-message"}],"requiredCampaignState":["campaignId","workflowTableId","messageDraftRecommendation"],"allowedTools":["AskUserQuestion","request_user_input","update_campaign_brief","update_campaign","get_rows_minimal"],"doNotAllow":["create_campaign","list_senders","save_rubrics","import_leads","confirm_lead_list","start_campaign"],"waitFor":["message_approved","revise_messaging"],"transitions":{"message_approved":"validate-sample","revise_messaging":"message-generation"}},{"id":"validate-sample","label":"Validate campaign-table execution slice","currentStepValue":"apply-icp-rubric","reference":"references/sample-validation-loop.md","visibleStepRule":"Filter Leads is reached only after saved-filter approval; on approve-message, save the template and queue bounded Enrich Prospect cells through selector-based campaign processing.","onEnter":[{"tool":"update_campaign","requiredValues":{"currentStep":"apply-icp-rubric","watchNarration.stage":"fit-message"},"watchNarrationRule":"Say the template is saved and Filter Leads is now running the bounded enrichment and scoring pass."},{"tool":"queue_campaign_cells","requiredFields":["workflowTableId","columnRole","rowSelector"],"requiredValues":{"columnRole":"enrich","rowSelector":{"type":"reviewBatch"},"forceRerun":false},"targetCountSource":"WorkflowTable.config.mcp.reviewBatch.rowCount (default 15)"},{"tool":"wait_for_campaign_processing","requiredFields":["workflowTableId","minPassedCount"],"requiredValues":{"minPassedCount":1},"readVia":"stats_only_tool_result","timeoutGuidance":"If this times out, do not repoll identical args; use partial diagnostics to revise filters or surface blocked sample state.","customerSummaryPattern":["{checked} leads checked","{passed} passed fit scoring","{blocked} blocked before sending","full list remains paused until approval"]},{"action":"handle_partial_or_timeout_sample","customerStatus":"sample-needs-revision","rule":"Do not repeat waits indefinitely; surface partial status and route to revision.","copyMustInclude":"completed, passed, pending, blocked before sending, and full list remains paused"}],"requiredCampaignState":["campaignId","workflowTableId","approvedMessageTemplate","filterRubricsApproved when enableICPFilters=true"],"allowedTools":["get_subskill_asset","queue_campaign_cells","select_campaign_cells","get_campaign_table_schema","wait_for_campaign_processing","update_campaign","AskUserQuestion","request_user_input"],"doNotAllow":["import_leads","list_senders","start_campaign","enrich_with_prospeo","bulk_enrich_with_prospeo","check_rubric"],"hardRules":["campaign_processing_waits_are_stats_only_by_default","queue_review_batch_by_selector_never_fetch_rows_for_cell_ids","timeout_never_repeats_without_customer_handoff","timeout_or_underfloor_sample_never_advances_to_settings"],"waitFor":["sample_validated","sample_revision_required"],"transitions":{"sample_validated":"auto-execute-messaging","sample_revision_required":"lead-review","revise_leads":"find-leads","revise_rubric":"filter-rubric","escalation_triggered":"escalation"}},{"id":"auto-execute-messaging","label":"Generate initial campaign-row messages","currentStepValue":"auto-execute-messaging","references":["references/parallel-critique-protocol.md","references/thomas-variant-selection.md","references/sellable-cleanup-rules.md","references/step-15-re-cascade.md"],"onEnter":[{"tool":"queue_campaign_cells","batchSize":100,"when":"any passing row has pending, empty, or stale Generate Message cell","requiredFields":["workflowTableId","columnRole","rowSelector"],"requiredValues":{"columnRole":"generateMessage","rowSelector":{"type":"needsGeneratedMessage"},"forceRerun":false},"cellSource":"selector-engine needsGeneratedMessage rows using approved campaign brief template"},{"tool":"wait_for_campaign_processing","purpose":"wait_for_first_current_revision_generated_message","requiredValues":{"minGeneratedMessages":1,"templateRevision":"current"},"readVia":"stats_only_tool_result","invariant":"A generated message implies the pass gate; stale or wrong-revision messages are excluded from readiness."},{"action":"observe_generate_message_results","reference":"references/step-15-re-cascade.md"},{"action":"enforce_token_contract","modeFromConfig":"messaging.tokenContract"},{"action":"optional_critique_pass","enabledFromConfig":"messaging.critique.enabled","protocol":"references/parallel-critique-protocol.md","sampleSizeFromConfig":"messaging.critique.sampleSize","budgetUsdCapFromConfig":"messaging.critique.budgetUsdCap","perCriticTimeoutFromConfig":"messaging.critique.perCriticTimeoutSeconds","totalTimeoutFromConfig":"messaging.critique.totalTimeoutSeconds","criticsFromConfig":"messaging.critique.critics","enforceFinalizerPassFromConfig":"messaging.critique.synthesis.enforceFinalizerPass","rejectOnFakeProofFromConfig":"messaging.critique.rejectOnFakeProof","rejectOnUnsupportedTokenFromConfig":"messaging.critique.rejectOnUnsupportedToken","onBudgetTrip":"halt_critique_continue_plain_tail","onPerCriticTimeout":"drop_critic_proceed_with_remaining","onTotalTimeout":"persist_plain_message_for_row","onTokenContractViolation":"persist_plain_message_for_row","onFakeProof":"persist_plain_message_for_row"},{"action":"optional_opus_subset","enabledFromConfig":"messaging.critique.opus.enabled","selection":"references/thomas-variant-selection.md","maxMessagesPerPassFromConfig":"messaging.critique.opus.maxMessagesPerPass","budgetUsdCapFromConfig":"messaging.critique.opus.budgetUsdCap","onOpusBudgetTrip":"halt_opus_continue_non_opus","onOpusTokenContractViolation":"fallback_to_non_opus_rewrite"},{"tool":"update_campaign","requiredValues":{"currentStep":"auto-execute-messaging","watchNarration.stage":"review-ready"},"watchNarrationRule":"Say the first passing generated message is ready in Messages; next is review before Settings."},{"action":"ask_generated_message_review_choice","uses":"request_user_input","choices":["Approve reviewed draft rows and continue to Settings","Revise filters","Revise message template","Pause here"],"copyMustInclude":["approve the reviewed draft rows and continue to Settings","tokens resolved","company/person match","proof claim","prospect angle","language/tone when known","obvious bad fits","Would I take this call?","weak personalization can burn the sender's reputation","full list remains paused","this approves reviewed draft rows for setup; final Start still controls sending"],"autoSelectIf":"yolo_mode and the first passing generated message clears the quality floor","copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"]}],"allowedTools":["get_subskill_asset","get_campaign_table_schema","select_campaign_cells","queue_campaign_cells","wait_for_campaign_processing","revise_message_template_and_rerun","update_campaign","AskUserQuestion","request_user_input"],"doNotAllow":["import_leads","list_senders","start_campaign","generate_messages"],"hardRules":["generated_message_count_excludes_stale_or_wrong_revision_messages","message_template_revision_uses_brief_update_then_generate_message_rerun","do_not_overwrite_generated_message_cells_directly","critique_failure_never_escalates","critique_sample_size_bounded_by_config","first_passing_generated_message_unblocks_review","critics_fixed_at_targeting_copy_voice","synthesis_enforces_message_token_contract","opus_reserved_for_highest_value_subset","proposed_token_never_persisted_in_rewrite"],"waitFor":["generated_messages_approved","sample_revision_required"],"transitions":{"generated_messages_approved":"awaiting-user-greenlight","revise_filters":"filter-rubric","revise_messaging":"message-generation","escalation_triggered":"escalation"}},{"id":"awaiting-user-greenlight","label":"Settings, sender, sequence, and greenlight","currentStepValue":"awaiting-user-greenlight","reference":"references/final-handoff-contract.md","onEnter":[{"tool":"get_campaign"},{"tool":"list_senders","purpose":"surface available connected senders only at Settings"},{"tool":"update_campaign","requiredValues":{"currentStep":"settings","watchNarration.stage":"review-ready"},"watchNarrationRule":"Say message review is complete and the app is on Settings for sender choice. Mention lead research/filtering already happened outside the user's LinkedIn account and nothing sends until Start."},{"action":"surface_sender_and_slack_handoff","requiredVisibleContent":["connect or select a LinkedIn sender","Lead research and filtering already happened outside your LinkedIn account.","This account is only used for approved sending after final launch.","Nothing sends until the final Start step.","Slack reply review","recommended sequence","final launch confirmation is still ahead"],"copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"]},{"action":"ask_sender_selection","uses":"request_user_input","singleChoice":true,"choices":["Use this connected sender","Connect a different sender in Settings","Pause here"],"autoSelectIf":"yolo_mode and exactly one safe connected sender is available","copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"]},{"action":"attach_selected_sender","tool":"update_campaign","when":"user selected an available connected sender","requiredValues":{"senderIds":["{selectedSenderId}"],"currentStep":"sequence","watchNarration.stage":"review-ready"},"watchNarrationRule":"Say the sender was attached and the app is moving to Sequence review; sequence remains editable and nothing sends until Start."},{"tool":"attach_recommended_sequence","when":"after senderIds are attached","requiredValues":{"campaignId":"{campaignId}","currentStep":"send","watchNarration.stage":"review-ready"},"watchNarrationRule":"The sequence tool owns this visible beat: say the recommended follow-up sequence is attached, the app is on final launch review, and it is still not sending until Start."},{"action":"ask_final_launch_greenlight","uses":"request_user_input","singleChoice":true,"choices":["Start campaign","Review campaign first","Pause here"],"copyMustInclude":["quality confidence means sample messages and prospect angle were checked","launch confidence means sender, sequence, and Start are ready","approved messages begin sending according to the sequence","replies and meetings follow connected settings","you can monitor and pause","no hidden extra approval disappears after clicking Start"],"onUserStart":"claude-greenlight","neverAutoSelectInYolo":true,"copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"]}],"allowedTools":["get_campaign","get_campaign_navigation_state","list_senders","update_campaign","attach_recommended_sequence","AskUserQuestion","request_user_input"],"doNotAllow":["start_campaign","import_leads"],"autoStart":false,"watchRequired":true,"waitFor":["sender_connection_required","sender_attached","sequence_attached","ready_to_launch","user_greenlight","ui_start_detected"],"transitions":{"sender_connection_required":"awaiting-user-greenlight","sender_attached":"awaiting-user-greenlight","sequence_attached":"awaiting-user-greenlight","ready_to_launch":"awaiting-user-greenlight","user_greenlight":"claude-greenlight","ui_start_detected":"running"}},{"id":"claude-greenlight","label":"Explicit launch","reference":"references/final-handoff-contract.md","onEnter":[{"tool":"get_campaign"},{"action":"verify_sequence_and_current_step_before_start","requiredState":["workflowTableId","senderIds","sequenceTemplate","at least one approved generated message","currentStep in awaiting-user-greenlight|claude-greenlight|send"]},{"tool":"start_campaign","requiredFields":["campaignId"],"persistsCurrentStep":"running","watchNarrationRule":"After start_campaign succeeds, the running state must say the final greenlight was accepted, the campaign is now live/running, and the user can watch progress from the campaign."}],"allowedTools":["get_campaign","attach_recommended_sequence","start_campaign","AskUserQuestion","request_user_input"],"watchRequired":true,"waitFor":"campaign_started","transitions":{"campaign_started":"running"}},{"id":"running","label":"Campaign is live","currentStepValue":"running","onEnter":[{"action":"surface_campaign_live_confirmation_without_watch_link","copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"]}],"allowedTools":["get_campaign","AskUserQuestion","request_user_input"],"doNotAllow":["start_campaign"],"terminal":true},{"id":"escalation","label":"Escalation","reference":"references/escalation-ladder.md","allowedTools":["AskUserQuestion","request_user_input"],"doNotAllow":["start_campaign","import_leads"],"transitions":{"revise_brief":"brief-interview","revise_leads":"find-leads","revise_rubric":"filter-rubric","revise_messaging":"message-generation","abort":"abort"}},{"id":"abort","label":"Abort","allowedTools":["AskUserQuestion","request_user_input"],"terminal":true}]}
|
|
1
|
+
{"version":"v2.1-compact","workflow":"create-campaign-v2","principle":"CampaignOffer state/watch link are canonical; require brief/source/import, filters/message, Settings, explicit Start.","normalCustomerPath":"Use campaign state/MCP/watchNarration. Do not create, read, link, or surface local files. Print first watch link once.","legacyCompatibility":{"validationSubskill":"create-campaign-v2-validation","tailSubskill":"create-campaign-v2-tail","rule":"Legacy diagnostics are opt-in only, not active flow gates."},"commitGateChoices":["approve","revise-brief","revise-leads","revise-rubric","revise-messaging","abort"],"canonicalStateFields":["campaignId","watchUrl","campaignBrief","currentStep","watchNarration","interactionMode","providerSearchAssociation","selectedLeadListId","workflowTableId","filterChoice","leadScoringRubrics","messageDraftRecommendation","approvedMessageTemplate","senderIds","sequenceTemplate","runningState"],"watchNarrationTransitionContract":{"rule":"Every watched currentStep switch must include fresh watchNarration.","requiredFields":["stage","headline","visibleState","agentIntent","nextAction"],"copyMustInclude":["what just happened","current visible page/state","next user action"],"appliesToTools":["create_campaign","update_campaign","attach_recommended_sequence","start_campaign"],"oneTimeWatchLinkPolicy":"After initial brief handoff, must not print another watch-link handoff unless user asks or link recovery is needed."},"lazyReferences":{"watch":["references/watch-link-handoff.md","references/watch-guide-narration.md"],"source":["references/lead-validation-preview.md","references/step-13-import-leads.md"],"filter":["references/filter-leads.md"],"message":[],"tail":["references/sample-validation-loop.md","references/step-15-re-cascade.md","references/final-handoff-contract.md"]},"safetyBoundaries":["Do not call list_senders before Settings after message approval.","Do not import leads until Start Import is approved.","Do not queue cells until confirmed source rows exist in the campaign and the message/filter gates are satisfied.","Do not call start_campaign until the user explicitly confirms launch.","Do not use local files as durable state in normal customer runs."],"yoloMode":{"triggerPhrases":["yolo","--yolo","mode=yolo","autopilot","use best guesses","use best estimates","answer for me","just run it","yolo autopilot"],"identityRequestOnlyWhenMissing":true,"operatorDirectionsRule":"Treat freeform directions supplied at invocation or later as durable operator directions; newest conflicting direction wins.","setInteractionModeAfterCampaignCreate":"autonomous","autoSelectsPreLaunchChoices":["campaign focus","brief approval","source plan","Start Import approval","filters vs skip filters","filter rubric approval","message template approval when recommendation is approve-message","generated message review when quality floor passes","sender selection when exactly one connected sender is safe"],"mustPauseFor":["missing LinkedIn profile URL or handle","missing credentials or required data","ambiguous choice with no reasonable estimate","source/message/filter quality floor failure","final live launch confirmation"],"neverAutoStart":true},"steps":[{"id":"bootstrap","label":"Bootstrap","onEnter":[{"tool":"bootstrap_create_campaign","requiredValues":{"flowVersion":"v2"}},{"tool":"get_subskill_prompt","requiredValues":{"subskillName":"create-campaign-v2"}}],"allowedTools":["bootstrap_create_campaign","get_auth_status","get_active_workspace","get_subskill_prompt","AskUserQuestion","request_user_input"],"doNotAllow":["create_campaign","list_senders","save_rubrics","import_leads","confirm_lead_list","update_campaign","queue_cells","start_campaign"],"waitFor":"bootstrap_complete","transitions":{"bootstrap_complete":"brief-interview"}},{"id":"brief-interview","label":"Client, offer, research, and brief","onEnter":[{"action":"resolve_campaign_identity_before_strategy_packet","allowedTools":["fetch_company","fetch_linkedin_profile","WebFetch","WebSearch"],"mustNotInferFromNameOnly":true,"doNotPresentSenderPickerBeforeIdentityInference":true,"fallback":"Ask only: \"What is your LinkedIn profile URL or handle?\" Normalize handles to profile URLs; if not a profile, ask again before strategy questions.","requiresLinkedInProfileUrl":true,"doNotAcceptAsIdentityInput":["non-profile URL","company page","company/name-only input without a profile handle"],"acceptsLinkedInProfileHandle":true,"normalizeLinkedInProfileHandleToUrl":true},{"action":"run_campaign_identity_research_before_strategy_packet","target":"research-sender","requiredCompletion":"complete_sender_research","allowedTools":["get_subskill_prompt","fetch_linkedin_profile","fetch_company","fetch_linkedin_posts","fetch_company_posts","WebFetch","WebSearch","complete_sender_research"],"requiredInput":"LinkedIn profile URL or handle"},{"action":"confirm_target_before_strategy_packet","uses":"request_user_input","singleChoice":true,"question":"Who should we target first?","options":["Use Sellable's researched recommendation","Choose a different buyer","I'm not sure yet"],"copyMustInclude":["research basis","recommendation is editable","who to target"],"requiredOption":"Other / custom","neverCollectOpenTextWithStructuredQuestion":true,"skipIf":"user already supplied a clear target or yolo_mode with any reasonable best-estimate target"},{"action":"confirm_current_offer_before_strategy_packet","uses":"request_user_input","question":"What should we pitch to prospects first?","options":["Use Sellable's researched recommendation","Use my current pitch","Shape the pitch together"],"copyMustInclude":["research basis","public LinkedIn research may be stale","recommendation is editable","pitch to prospects"],"skipIf":"user already supplied a clear current offer or yolo_mode with any reasonable researched recommendation"},{"action":"confirm_trust_proof_before_strategy_packet","uses":"request_user_input","singleChoice":true,"question":"What is the strongest credibility signal we can lead with?","options":["Use Sellable's recommended proof","Use customer proof or a case study","Use founder or company credibility"],"copyMustInclude":["build trust with prospects","recommendation is editable","proof to use or avoid"],"requiredOption":"Other / custom","skipIf":"user already supplied clear proof to use or avoid, or yolo_mode with any reasonable researched proof"},{"action":"choose_prospect_source_path_before_strategy_packet","uses":"request_user_input","singleChoice":true,"question":"How should we find prospects?","options":["Find prospects for me","Use a CSV of LinkedIn profiles","Use a CSV of company domains"],"copyMustInclude":["prospects","no one added yet","find prospects for me"],"skipIf":"user already supplied a source path or yolo_mode with any reasonable source path"},{"action":"infer_strategy_packet_in_yolo_mode","when":"yolo_mode","skipStructuredSetupQuestions":true,"inferFields":["buyer segment","offer/CTA","proof to use or avoid","lead source","filter choice","message direction"],"sourceOfTruth":"identity/company lookup plus operator directions","mustStateAssumptionsInBrief":true},{"action":"render_supplied_setup_receipt_before_brief_when_setup_questions_skip","when":"any setup question is skipped because the user already supplied or yolo mode inferred identity, target, offer, proof, or source","output":"normal chat receipt before the campaign brief or watch-link handoff","copyMustInclude":["Accepted LinkedIn input: normalized to the required LinkedIn profile URL.","Offer path: supplied current offer or Sellable's researched recommendation; you can replace it with your current offer."],"copyMustNotInclude":["Campaign Identity"]},{"action":"create_watchable_campaign_shell_with_v1_brief","tool":"create_campaign","requiredFields":["name","campaignBrief","clientProspectId or senderLinkedinUrl","currentStep","watchNarration"],"requiredValues":{"currentStep":"create-offer","watchNarration.stage":"brief"},"capture":["campaignId","watchUrl"],"canonicalStateWrites":["campaignId","watchUrl","campaignBrief","currentStep:create-offer"]},{"action":"surface_campaign_shell_watch_link","watchUrlSource":"create_campaign.watchUrl","requiredWatchUrlShape":"exact create_campaign.watchUrl; direct /campaign-builder/{campaignId}?mode={claude|codex} URL with workspaceId and token","copyMustInclude":["WATCH CODEX BUILD","Open live campaign builder","Keep this chat open.","approval questions here"],"immediateNextMainChatLine":"Ask for brief approval now. After approval, say: \"Brief approved. Next, we'll choose where to find buyers. I won't add anyone yet.\"","codexBrowserHandoff":{"openWhenAvailable":false,"printWatchLinkOnly":true,"tellUserCommandEnterOrClick":false,"mustNotUseBrowserAutomation":true,"fallbackMustNotClaimInspection":true},"copyMustAvoid":["bare /campaign-builder/{campaignId} URL","derived watch URL","second watch-link handoff","shell open command","Next, we'll choose where to find buyers"],"oneTimeOnly":true}],"allowedTools":["get_subskill_prompt","fetch_company","fetch_company_posts","fetch_linkedin_profile","fetch_linkedin_posts","complete_sender_research","create_campaign","AskUserQuestion","request_user_input"],"doNotAllow":["list_senders","save_rubrics","import_leads","confirm_lead_list","queue_cells","start_campaign"],"waitFor":["campaign_shell_created","brief_ready","confirm_with_user"],"transitions":{"campaign_shell_created":"brief-review","brief_ready":"brief-review","confirm_with_user":"brief-review"}},{"id":"brief-review","label":"Brief review","onEnter":[{"action":"render_brief_inline","minimumVisibleDetail":"campaign direction, buyer, offer, proof, source plan, safety gates","copyMustInclude":["This brief is based on Sellable's research and your answers.","If your site or LinkedIn is stale","Does this brief look right, and how should Sellable continue?"],"copyMustNotInclude":["quoted first-message copy","Message Angle as final proof","This approval covers","does not approve adding people","does not approve scraping posts","selecting a sender","attaching a sequence","Campaign Identity","what should this campaign sell first","sell first","[from you]","[from LinkedIn]","[from website]","[from case study]","[Sellable recommendation]"],"messageHintRule":"If any message-shape hint remains, keep it as non-final bullets and say real examples come after leads are found and filtered.","approvalBoundaryRule":"Keep this gate positive and short. Do not list later steps this approval does not cover; ask approve/revise and then move to choosing where to find buyers.","sectionLabelRule":"Use ## Sender and Company for the person/company context; never render ## Campaign Identity.","skipIf":"the full brief was already rendered in the current shell-creation handoff before create_campaign returned","renderOnceRule":"Brief visible once before approval. If shown before shell creation, do not repeat after; append one watch-link handoff, then ask.","copyMustAvoid":["duplicate brief render after shell creation","second Watch link:"]},{"action":"ask_brief_choice","uses":"request_user_input","choices":["Approve brief + activate YOLO mode (Recommended)","Approve brief + build campaign with AI, step by step","Revise brief"],"skipIf":"yolo_mode; auto_continue after rendering assumptions unless brief quality floor fails","question":"Does this brief look right, and how should Sellable continue?","autonomousChoice":"Approve brief + activate YOLO mode (Recommended)","choiceDescriptions":{"Approve brief + activate YOLO mode (Recommended)":"Sellable will auto-decide the remaining setup choices with best guesses, including sources, filters, and messaging. It still stops before final launch.","Approve brief + build campaign with AI, step by step":"Approve this direction, then work with the AI through each major setup decision before it continues.","Revise brief":"Change the target, offer, proof, or campaign direction before continuing."}}],"requiredCampaignState":["campaignId","watchUrl","campaignBrief","currentStep"],"allowedTools":["AskUserQuestion","request_user_input","update_campaign"],"doNotAllow":["create_campaign","list_senders","import_leads","confirm_lead_list","queue_cells","start_campaign"],"waitFor":["user_brief_confirmed","revise_brief","auto_continue"],"transitions":{"user_brief_confirmed":"find-leads","revise_brief":"brief-interview","auto_continue":"find-leads"}},{"id":"find-leads","label":"Find leads","sourceSelectionFunnel":{"preScoutRecommendationGate":{"required":true,"label":"Find buyers plan approval","mustHappenBefore":["get_provider_prompt","search_signals","search_sales_nav","search_prospeo_companies","confirm_prospeo_company_accounts","search_prospeo","fetch_post_engagers"],"show":["buyer groups or places we could check","best place to start","why the right buyers are likely to be there","what signs the next search will check","where to look next if the first place is too thin","what approval authorizes"],"approvalAuthorizes":"looking for the best places to find buyers; no one added yet","explanationMustInclude":["where the right buyers are already talking","why that place fits this campaign","enough likely prospects","what counts as a good sign","where to look next if thin","I won't add anyone yet","choose where to find buyers"],"yoloMode":{"autoApproveRecommendedLane":true,"mustShowAssumedChoice":true,"pauseIfConfidenceLow":true},"customerLanguage":{"readability":"grade-5","requiredHeading":"Find Buyers Plan","prefer":["place to look","best place to start","people to check","prospects","I won't add anyone yet"],"avoid":["lane","source scouting","provider","precision/scale tradeoff","evidence quality","pilot volume","ICP","workflow pain","lead-source scouting","not importing leads"],"termRule":{"buyers":"target market","peopleToCheck":"raw reactions/comments before fit is known","prospects":"likely usable people after fit","leads":"campaign rows"}},"questionOrder":"update_campaign to pick-provider; render Find Buyers Plan without repeating the watch link; never ask first.","copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL","watch-link handoff"],"approvalFreshnessRule":"Only source approval opened after the visible Find Buyers Plan, or explicit source choice after it, sets source_lane_approved. Do not reuse brief approval."},"defaultWhenSourceUnspecified":["signal-discovery","sales-nav-recent-active","sales-nav-general","prospeo"],"parallelAllowedOnlyWhen":["user explicitly requested source comparison"]},"onEnter":[{"tool":"update_campaign","requiredValues":{"currentStep":"pick-provider","watchNarration.stage":"find-leads","watchNarration.headline":"Review where to find buyers","watchNarration.visibleState":"The app is showing source selection before lead finding starts.","watchNarration.agentIntent":"Codex is explaining where it recommends looking first and where it will look next if that is too thin.","watchNarration.nextAction":"Approve where to look first or choose another place","watchNarration.safety":"Only deciding where to look. No one is added yet."}},{"action":"render_find_buyers_plan_inline","oneShot":true,"requiredPrecondition":"currentStep=pick-provider has been saved with update_campaign","mustHappenAfter":["update_campaign currentStep=pick-provider"],"mustHappenBefore":["ask_find_buyers_plan_choice","get_provider_prompt","search_signals","search_sales_nav","search_prospeo_companies","confirm_prospeo_company_accounts","search_prospeo","fetch_post_engagers"],"output":"normal chat text before any request_user_input approval panel","requiredHeading":"Find Buyers Plan","requiredInlineFields":["plain-language buyer groups or places for this campaign","best place to start","why the right buyers are likely to be there","what signs the next search will check","where to look next if the first place is too thin","what approval authorizes"],"copyMustInclude":["Find Buyers Plan","best place to start","I won't add anyone","choose where to find buyers","I won't add anyone yet"],"copyMustAvoid":["lane","source scouting","provider","precision/scale tradeoff","evidence quality","pilot volume","ICP","workflow pain","lead-source scouting","not importing leads","Watch link:","campaign-builder URL","repeat the watch URL","watch-link handoff"],"approvalBoundary":"Explain what approval authorizes before asking: looking for the best places to find buyers; no one is added yet.","linkPolicy":"Do not include another watch link. The first brief handoff already gave the user the live app URL."},{"action":"ask_find_buyers_plan_choice","uses":"request_user_input","oneShot":true,"requiredPrecondition":"render_find_buyers_plan_inline completed in normal chat after pick-provider state was saved; no earlier approval can satisfy this","skipIf":"source_lane_approved after visible Find Buyers Plan, explicit post-plan leadSourceProvider, or yolo assumed choice","choices":["Start with LinkedIn posts","Start with active LinkedIn profiles","Start with company/contact search","Choose a different place","Pause here"],"approvalChoiceLabelsByProvider":{"signal-discovery":"Start with LinkedIn posts","sales-nav":"Start with active LinkedIn profiles","prospeo":"Start with company/contact search"},"approvalState":"source_lane_approved","copyMustInclude":["Approve this plan for where to find buyers?","Start with LinkedIn posts"],"mustNotHappenBefore":["render_find_buyers_plan_inline"],"approvalStateRule":"Set source_lane_approved here after the visible plan; never from brief approval, generic approval, pre-plan provider state, or artifacts."},{"action":"persist_provider_search_step_before_search","tool":"update_campaign","requiredPrecondition":"source_lane_approved set after the visible Find Buyers Plan approval question","providerCurrentStepMap":{"signal-discovery":"signal-discovery","sales-nav":"sales-nav","prospeo":"prospeo"},"requiredValues":{"leadSourceType":"new","leadSourceProvider":"approved provider","currentStep":"provider-specific current step","watchNarration.stage":"find-leads","watchNarration.headline":"Searching the approved place","watchNarration.safety":"Looking only. No one is added yet."},"mustRunBefore":["search_signals","search_sales_nav","search_prospeo_companies","confirm_prospeo_company_accounts","search_prospeo","fetch_post_engagers"]},{"action":"run_parent_thread_source_funnel","requiredPrecondition":"source_lane_approved after visible Find Buyers Plan approval","mode":"parent_thread_inline_sequential","sourceAgents":"forbidden in normal create-campaign","loadsOnlyActiveProviderPrompt":true,"defaultOrder":["signal-discovery","sales-nav-recent-active","sales-nav-general","prospeo"],"stopOnFirstViableUnlessComparisonRequested":true}],"requiredCampaignState":["campaignId","campaignBrief","currentStep"],"allowedTools":["get_provider_prompt","lookup_sales_nav_filter","search_sales_nav","search_prospeo_companies","confirm_prospeo_company_accounts","search_prospeo","search_signals","select_promising_posts","fetch_post_engagers","fetch_company","fetch_linkedin_profile","load_csv_linkedin_leads","load_csv_domains","get_rows_minimal","update_campaign","AskUserQuestion","request_user_input","get_campaign_navigation_state"],"doNotAllow":["create_campaign","list_senders","save_rubrics","import_leads","confirm_lead_list","queue_cells","start_campaign","Task","spawn_agent"],"waitFor":["lead_review_ready","revise_brief","confirm_with_user"],"transitions":{"lead_review_ready":"lead-review","revise_brief":"brief-interview","confirm_with_user":"lead-review"}},{"id":"lead-review","label":"Start Import approval","onEnter":[{"action":"show_source_decision_card","requiredInlineFields":["primary source and exact filters/recipe","Start Import action awaiting approval","for Signal Discovery: compact Source Recommendation in plain language with selected posts, people to check, likely prospects, first review, and fallback","for Signal Discovery: recommended scrape post count and target people-to-check volume","first campaign review size, clearly separate from source sampling","runner-up and why it lost","raw volume","sampled people","sampled fits as n/N plus percentage/range","estimated usable prospects","cleanup risk","what happens after Start Import"],"copyMustAvoid":["lead-source scouting","source scouting","headline-fit","engagers needed","execution slice","ICP","good buyers","real buyers","This approval covers","It does not approve","does not approve filters","filters, messages, sender selection","sender selection, sequence, or sending","Watch link:","campaign-builder URL","repeat the watch URL"],"approvalBoundaryRule":"Use positive Start Import copy only: after approval build source list, add to campaign, review first 15 leads; never list future non-approvals.","linkPolicy":"Do not include another watch link in Source Recommendation; describe the Start Import decision only."},{"action":"ask_source_review_choice","uses":"request_user_input","choices":["Approve scraping N recommended LinkedIn posts","Start Import for the approved source list","Revise source","Pause here"],"approvalChoiceLabelsByProvider":{"signal-discovery":"Approve scraping {scrapePostCount} recommended LinkedIn posts?","sales-nav":"Start Import for the approved Sales Nav source list","prospeo":"Start Import for the approved Prospeo source list"},"postApprovalContract":{"singleUseApproval":true,"doNotRepeatAfterApproval":["Source Recommendation","show_source_decision_card","ask_source_review_choice"],"requiredNextActionAfterApproval":"ack once; call import_leads immediately","signalDiscoveryNextTool":"import_leads({ provider: \"signal-discovery\", targetEngagerCount, maxPostsToScrape, confirmed: true })"},"autoSelectIf":"yolo_mode and projected usable pool clears the source quality floor","copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"]}],"requiredCampaignState":["campaignId","campaignBrief","providerSearchAssociation"],"allowedTools":["AskUserQuestion","request_user_input","update_campaign"],"doNotAllow":["create_campaign","list_senders","save_rubrics","import_leads","confirm_lead_list","queue_cells","start_campaign"],"waitFor":["lead_review_confirmed","revise_leads","confirm_with_user","auto_continue"],"transitions":{"lead_review_confirmed":"auto-execute-leads","revise_leads":"find-leads","confirm_with_user":"auto-execute-leads","auto_continue":"auto-execute-leads"}},{"id":"auto-execute-leads","label":"Materialize confirmed source list","currentStepValue":"auto-execute-leads","reference":"references/step-13-import-leads.md","onEnter":[{"tool":"get_subskill_prompt","requiredValues":{"subskillName":"create-campaign-v2-tail"}},{"tool":"import_leads","requiredFields":["campaignOfferId","selected source/list","sourceListTarget or targetEngagerCount"],"requiredValues":{"salesNavProspeoDefaultSourceListTarget":1000,"signalDiscoveryDefaultEngagerTarget":1500},"modeAddHandshake":{"firstCallReturns":"needsModeSelection when adding to an existing campaign-attached list","requiredFollowup":"retry with mode=add after explicit user/source-state compatibility is confirmed"},"surfaceDedupRatio":true},{"tool":"wait_for_lead_list_ready","repeatUntil":"ready_true_or_cancelled_or_import_failed","requiredValues":{"requireComplete":true},"onImportStillRunning":"re-run wait_for_lead_list_ready; do not call confirm_lead_list just because rows exist","partialOverrideRule":"Only if the user explicitly asks to keep going early may the next confirm_lead_list call pass allowPartialSourceList: true."},{"tool":"confirm_lead_list","requiredFields":["campaignOfferId","selectedLeadListId","reviewBatchLimit"],"requiredValues":{"reviewBatchLimit":15},"capture":["workflowTableId","reviewBatchRowIds"],"requiredPrecondition":"wait_for_lead_list_ready returned ready:true, unless the user explicitly asked to keep going early with a partial list","partialOverrideField":"allowPartialSourceList:true only after explicit user early-continue instruction","mustNotRunWhen":["wait_for_lead_list_ready reason=import_still_running and no explicit early-continue instruction","wait_for_lead_list_ready reason=cancelled","wait_for_lead_list_ready reason=import_failed","missing import job metadata"]},{"tool":"wait_for_campaign_table_ready"},{"tool":"get_rows_minimal","requiredValues":{"tableId":"{workflowTableId}","limit":15},"capture":["reviewBatchRowHash"]},{"action":"summarize_review_batch_and_advance_to_filter_choice","maxCustomerCopyLines":4,"copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"],"linkPolicy":"Ask add filters vs skip filters without repeating the watch link.","purpose":"ask filter choice immediately"}],"requiredCampaignState":["campaignId","providerSearchAssociation","selectedLeadListId"],"allowedTools":["get_subskill_prompt","import_leads","wait_for_lead_list_ready","confirm_lead_list","wait_for_campaign_table_ready","get_rows_minimal","update_campaign","AskUserQuestion","request_user_input"],"doNotAllow":["get_post_find_leads_scout_registry","Task","spawn_agent","list_senders","queue_cells","start_campaign","enrich_with_prospeo","bulk_enrich_with_prospeo","save_rubrics"],"waitFor":"source_list_confirmed_and_review_sample_ready","transitions":{"source_list_confirmed_and_review_sample_ready":"filter-choice","escalation_triggered":"escalation"},"hardRules":["import_leads_then_repeated_wait_for_lead_list_ready_then_confirm_lead_list_then_wait_for_campaign_table_ready","partial_source_rows_do_not_unblock_confirm_lead_list_without_explicit_user_early_continue","cancelled_or_failed_source_import_stops_before_campaign_table_copy"]},{"id":"filter-choice","label":"Filter choice","currentStepValue":"filter-choice","onEnter":[{"tool":"update_campaign","requiredValues":{"currentStep":"filter-choice","watchNarration.stage":"fit-message"}},{"action":"ask_filter_choice","uses":"request_user_input","choices":["Use filters","Skip filters","Revise source"],"autoSelectIf":"yolo_mode; choose filters unless source is tightly curated or user directed otherwise","copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"],"copyMustInclude":["source rows are in the campaign","add fit filters before message drafting or skip filters"]}],"hardRules":["ask_filter_choice_immediately_after_review_batch_import","do_not_call_get_subskill_prompt_before_filter_choice","do_not_call_get_subskill_asset_before_filter_choice","do_not_call_get_post_find_leads_scout_registry_before_filter_choice","do_not_spawn_prospect_setup_before_filter_choice"],"requiredCampaignState":["campaignId","campaignBrief","selectedLeadListId","workflowTableId"],"allowedTools":["AskUserQuestion","request_user_input","update_campaign","get_campaign_navigation_state"],"doNotAllow":["get_subskill_prompt","get_subskill_asset","get_post_find_leads_scout_registry","Task","spawn_agent","create_campaign","list_senders","import_leads","confirm_lead_list","queue_cells","start_campaign","generate_messages"],"waitFor":["filters_enabled","filters_skipped","revise_leads"],"transitions":{"filters_enabled":"prospect-setup","filters_skipped":"message-generation","revise_leads":"find-leads"}},{"id":"prospect-setup","label":"Filter setup + message drafting","onEnter":[{"action":"persist_add_filters_approval","tool":"update_campaign","when":"filters_enabled","requiredFields":["campaignId","enableICPFilters","currentStep","watchNarration"],"requiredValues":{"enableICPFilters":true,"currentStep":"create-icp-rubric","watchNarration.stage":"fit-message","watchNarration.headline":"Create filter rules","watchNarration.visibleState":"Filter Rules is visible while Codex drafts and saves filters in the parent thread.","watchNarration.nextAction":"Review saved filter rules"},"mustRunBefore":["get_post_find_leads_scout_registry","launch_message_drafting_after_filter_choice","load_filter_reference_in_parent","save_rubrics","ask_filter_rubric_review_choice"]},{"tool":"get_post_find_leads_scout_registry","returns":["post-find-leads-message-scout"],"rule":"normal post-import registry exposes only Message Drafting; filters are parent-thread MCP work","mustRunBefore":["launch_message_drafting_after_filter_choice","get_subskill_asset:references/filter-leads.md","save_rubrics","ask_filter_rubric_review_choice"],"registryOnlyIsNotLaunch":true},{"action":"launch_message_drafting_after_filter_choice","mode":"background_agent_when_host_supports_subagents","target":"post-find-leads-message-scout","when":"filters_enabled or filters_skipped after filter-choice answer","doesNotQueueCells":true,"customerNarration":"Message Drafting is preparing.","inputs":["campaignId","workflowTableId","brief/source summary","3-5 sample workflow-table rows","optional campaignName/selectedLeadListId/filterChoice"],"forbiddenSiblings":["source/filter workers"],"handoffCompression":"lean_message_drafting_handoff_no_hashes_no_counts_no_full_row_data","requiredConcreteToolCall":"Task or spawn_agent when host supports background agents","registryOnlyIsNotLaunch":true,"mustRunBefore":["get_subskill_asset:references/filter-leads.md","load_filter_reference_in_parent","save_rubrics","ask_filter_rubric_review_choice"],"yoloPermissionRule":"do not ask the user for worker permission in YOLO.","startProof":"Message Drafting counts as started only after Task/spawn_agent.","noBackgroundFallback":"Start the same full message branch inline before filter reference or block.","blockedIfSkipped":"Do not proceed to get_subskill_asset(filter-leads), save_rubrics."},{"tool":"get_subskill_asset","action":"load_filter_reference_in_parent","when":"filters_enabled","requiredValues":{"subskillName":"create-campaign-v2","assetPath":"references/filter-leads.md"},"rule":"Parent thread uses this reference to draft production rubrics; do not spawn a filter worker."},{"tool":"save_rubrics","when":"filters_enabled","requiredFields":["campaignOfferId","leadScoringRubrics"],"writesCampaignState":"leadScoringRubrics","requiredSideEffects":{"enableICPFilters":true,"currentStep":"create-icp-rubric","watchNarration.headline":"Filter rules saved for review"}},{"action":"ask_filter_rubric_review_choice","uses":"request_user_input","choices":["Approve filters","Revise filters","Pause"],"copyMustInclude":["Filter rules saved for review","These rules prevent wasted sends before I score/import the list.","Recommended because of the researched ICP, source sample, and repeated false-positive patterns.","one pass example and one block example when available","approval is for the saved filter rules","Approve these filter rules."],"autoSelectIf":"yolo_mode and parent saved production-shaped rubrics","copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"],"requiredPrecondition":"save_rubrics succeeded or active campaign rubrics verified"}],"requiredCampaignState":["campaignId","campaignBrief","selectedLeadListId","workflowTableId"],"allowedTools":["get_post_find_leads_scout_registry","get_subskill_asset","save_rubrics","get_campaign","get_rows_minimal","update_campaign","get_campaign_navigation_state","AskUserQuestion","request_user_input","Task","spawn_agent"],"doNotAllow":["create_campaign","list_senders","import_leads","confirm_lead_list","queue_cells","start_campaign","check_rubric","generate_messages"],"waitFor":["filter_rubrics_approved","revise_leads","revise_rubric","revise_messaging"],"hardRules":["after_save_rubrics_currentStep_must_stay_create-icp-rubric_until_filter_approval","filter_approval_required_before_apply-icp-rubric_or_queue_campaign_cells","do_not_move_browser_to_messages_until_filter_leads_step_is_current_or_filters_are_explicitly_skipped","no_prospect_setup_worker_or_deep_prompt_before_filter_choice","message_drafting_after_filter_choice","message_drafting_no_cells","only_message_drafting_may_spawn_background_agent","filters_are_parent_thread_mcp_work","no_post_find_leads_filter_scout_in_normal_path","registry_call_is_not_message_drafting_launch","yolo_must_not_ask_permission_for_message_drafting_worker","message_drafting_must_start_before_filter_reference_or_save_rubrics","if_background_agent_unavailable_start_full_message_branch_inline_before_filters_or_block"],"transitions":{"filter_rubrics_approved":"message-generation","revise_leads":"find-leads","revise_rubric":"filter-rubric","revise_messaging":"message-generation","confirm_with_user":"message-review"}},{"id":"filter-rubric","label":"Rubric revision","onEnter":[{"tool":"get_subskill_asset","requiredValues":{"subskillName":"create-campaign-v2","assetPath":"references/filter-leads.md"}},{"tool":"save_rubrics","requiredFields":["campaignOfferId","leadScoringRubrics"],"writesCampaignState":"leadScoringRubrics","requiredSideEffects":{"enableICPFilters":true,"currentStep":"create-icp-rubric","watchNarration.headline":"Filter rules saved for review"}},{"action":"ask_filter_rubric_review_choice","uses":"request_user_input","choices":["Approve filters","Revise filters","Pause"],"copyMustInclude":["These rules prevent wasted sends before I score/import the list.","approval object is filter rules only","approval is for the filter rules","Approve these filter rules."],"autoSelectIf":"yolo_mode and rubrics are production-shaped","copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"]}],"requiredCampaignState":["campaignId","workflowTableId"],"allowedTools":["get_subskill_asset","save_rubrics","AskUserQuestion","request_user_input"],"doNotAllow":["create_campaign","list_senders","import_leads","confirm_lead_list","update_campaign","queue_cells","start_campaign","check_rubric"],"waitFor":["filter_rubrics_approved","revise_leads","confirm_with_user","auto_continue"],"transitions":{"filter_rubrics_approved":"message-generation","revise_leads":"find-leads","confirm_with_user":"message-generation","auto_continue":"message-generation"}},{"id":"message-generation","label":"Message drafting","onEnter":[{"action":"set_message_review_visible_step_by_filter_choice","tool":"update_campaign","branchRules":["yes after filter approval: currentStep=apply-icp-rubric; wait on Filter Leads","no: currentStep=messages; message review"],"watchNarration.stage":"fit-message"},{"action":"run_or_reconcile_message_draft_builder","target":"post-find-leads-message-scout","toolCallRequiredBeforeDraft":["join already-running Message Drafting when prospect-setup launched it","run Message Drafting via post-find-leads-message-scout only if missing; yolo counts","handoff lean basis only: campaignId, workflowTableId, brief summary, source summary/source-use rule, 3-5 sample rows","do not paste copied row counts, brief hashes, review-batch hashes, full reviewBatchRowIds, broad row data, or local debug artifacts","branch loads current brief/context, full generate-messages prompt, and every required message asset referenced by generate-messages Mode 0","return labeled Markdown only: templateRecommendation, tokenFillRules, renderedGoodSample, status, recommendation, validationStatus, outputHash, retry detail","do not return renderedFallbackSample, risk notes, or qaReceipt on the normal happy path","if no yolo and permission missing, ask once before parent inline fallback","use generic gpt-5.5 xhigh Message Drafting agent if named unavailable"],"stateSource":"message branch-loaded live campaign/table state plus parent-provided sample rows","outputState":"messageDraftRecommendation with internal validation passed","revisionMode":{"when":"revise_messaging, message QA request, or latest user message requests template changes before approve-message","route":"send feedback/QA/rewrite request to post-find-leads-message-scout or generic gpt-5.5 xhigh Message Drafting branch","requiredInputs":["latest user feedback","current messageDraftRecommendation and basisToken/outputHash","lean campaign/table basis and brief summary; branch reloads live state"],"forbiddenParentActions":["rewrite template in parent","QA template in parent from memory","update_campaign_brief before approve-message"],"output":"revised messageDraftRecommendation rendered before request_user_input"},"onlyMessageBackgroundAgent":true,"forbiddenSiblingAgents":["source-scout-*","any filter background worker"],"handoffCompression":"lean_message_drafting_handoff_no_hashes_no_counts_no_full_row_data","finalGateRequiredBeforeReady":"Post-generation final gate: After candidate generation/revision, load get_subskill_prompt({ subskillName: \"create-campaign-v2-validation\" })."}],"requiredCampaignState":["campaignId","campaignBrief","selectedLeadListId","workflowTableId"],"allowedTools":["get_subskill_prompt","get_campaign","get_rows_minimal","update_campaign","Task","spawn_agent"],"toolRules":["message_drafting_agent_first_no_silent_parent_fallback; yolo counts","on_filters_enabled_path_parent_must_not_enter_message_review_until_save_rubrics_success_and_filter_approval","brief.md, lead-review.md, and lead-sample.json are optional debug context only.","messageDraftRecommendation returns templateRecommendation, tokenFillRules, renderedGoodSample, status, recommendation, validationStatus, outputHash, and retry detail.","messageDraftRecommendation does not return renderedFallbackSample, risk notes, or qaReceipt on the normal happy path.","If campaign/table/sample basis does not match, classify the output stale or blocked.","message_revision_feedback_routes_to_message_drafting_agent_not_parent","message_qa_routes_to_message_drafting_agent_not_parent","message_drafting_loads_full_generate_messages_prompt_all_chunks","message_drafting_loads_all_reference_assets_via_get_subskill_asset","message_drafting_loads_create_campaign_v2_validation_prompt_before_return","message_drafting_handoff_uses_lean_basis_no_hashes_no_counts_no_full_row_id_list","message_drafting_loads_current_campaign_brief_before_drafting","parent_may_load_generate_messages_only_for_explicitly_approved_inline_fallback","before return branch loads create-campaign-v2-validation prompt and runs internal validation"],"doNotAllow":["create_campaign","list_senders","save_rubrics","import_leads","confirm_lead_list","queue_cells","start_campaign","generate_messages","AskUserQuestion","request_user_input"],"waitFor":["message_validation_ready","revise_rubric","revise_messaging"],"transitions":{"message_validation_ready":"message-review","revise_rubric":"filter-rubric","revise_messaging":"message-generation"},"revisionLoop":{"trigger":"user gives copy feedback, asks for QA/update, or chooses revise-messaging","required":["route latest user feedback or QA request to Message Drafting with current recommendation and live campaign/table state","produce a new messageDraftRecommendation with revisionNotes or validationStatus","render revised ## Message Template, ## Rendered Example, and What changed before asking approval"],"blocked":["asking whether an unseen new version is better","reusing the old template after acknowledging feedback","rewriting the template in the parent thread","update_campaign_brief before approve-message","request_user_input or AskUserQuestion during message-generation","QAing the template in the parent thread"],"outputState":"messageDraftRecommendation"},"hardRules":["message_revision_must_render_new_template_before_approval_question","message_revision_saves_only_after_approve_message","message_generation_must_not_call_request_user_input","message_generation_must_transition_to_message_review_only_after_renderable_draft_state","message_drafting_agent_first_no_silent_parent_fallback","message_revision_routes_to_message_drafting_agent","message_qa_routes_to_message_drafting_agent","filter_path_requires_saved_filters_before_message_review","only_message_drafting_may_spawn_background_agent"]},{"id":"message-review","label":"Message review","onEnter":[{"action":"render_message_review_from_state","requiredState":["campaignBrief","selectedLeadListId","workflowTableId","messageDraftRecommendation"],"requiredVisibleLabels":["## Message Template","## Rendered Example","Good token fill:","My take:","Question: approve-message or revise-messaging?","Recommendation:"],"doNotShowByDefault":["Rendered fallback sample","Concerns","QA receipt","Token Notes","Good omit / fallback","Bad fill to avoid","Token Adherence Table"],"internalPersistenceOnly":["Token Fill Rules","Token Fill Examples","fallback guidance","bad-fill avoidance notes","validation notes","QA details"],"copyMustInclude":["one recommended template","one rendered sample","token rules and fallbacks","full list remains paused"],"copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"],"revisionRenderRule":"After revise_messaging, QA, or copy feedback, route feedback to Message Drafting; show revised template/example/What changed before approval; never ask if an unseen version is better.","parentMustNot":["rewrite template from memory","invent validation summary without Message Drafting output","render qaReceipt, risk notes, or renderedFallbackSample on the normal happy path"]},{"action":"ask_message_review_choice","uses":"request_user_input","choices":["approve-message","revise-messaging"],"copyMustInclude":["approve this message template and continue","template approval only locks the draft direction; final Start still controls sending"],"autoSelectIf":"yolo_mode and Recommendation is approve-message; revise autonomously when Recommendation is revise-messaging","copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"],"requiredPrecondition":"current or revised template and rendered example are visible in this turn","revisionPrecondition":"after revise_messaging, revised template is visible before this question"},{"action":"sync_approved_message_set_to_campaign_brief","tool":"update_campaign_brief","when":"after approve-message","requiredFields":["campaignId","approvedMessageTemplate","Token Fill Rules","Token Fill Examples"],"writesCampaignState":"approvedMessageTemplate","revisionApprovalRule":"If the user requested revisions, write only the latest Message Drafting-approved revised template and token rules after approve-message.","forbiddenBefore":"approve-message"}],"requiredCampaignState":["campaignId","workflowTableId","messageDraftRecommendation"],"allowedTools":["AskUserQuestion","request_user_input","update_campaign_brief","update_campaign","get_rows_minimal"],"doNotAllow":["create_campaign","list_senders","save_rubrics","import_leads","confirm_lead_list","start_campaign"],"waitFor":["message_approved","revise_messaging"],"transitions":{"message_approved":"validate-sample","revise_messaging":"message-generation"}},{"id":"validate-sample","label":"Validate campaign-table execution slice","currentStepValue":"apply-icp-rubric","reference":"references/sample-validation-loop.md","visibleStepRule":"Filter Leads is reached only after saved-filter approval; on approve-message, save the template and queue bounded Enrich Prospect cells through selector-based campaign processing.","onEnter":[{"tool":"update_campaign","requiredValues":{"currentStep":"apply-icp-rubric","watchNarration.stage":"fit-message"},"watchNarrationRule":"Say the template is saved and Filter Leads is now running the bounded enrichment and scoring pass."},{"tool":"queue_campaign_cells","requiredFields":["workflowTableId","columnRole","rowSelector"],"requiredValues":{"columnRole":"enrich","rowSelector":{"type":"reviewBatch"},"forceRerun":false},"targetCountSource":"WorkflowTable.config.mcp.reviewBatch.rowCount (default 15)"},{"tool":"wait_for_campaign_processing","requiredFields":["workflowTableId","minPassedCount"],"requiredValues":{"minPassedCount":1},"readVia":"stats_only_tool_result","timeoutGuidance":"If this times out, do not repoll identical args; use partial diagnostics to revise filters or surface blocked sample state.","customerSummaryPattern":["{checked} leads checked","{passed} passed fit scoring","{blocked} blocked before sending","full list remains paused until approval"]},{"action":"handle_partial_or_timeout_sample","customerStatus":"sample-needs-revision","rule":"Do not repeat waits indefinitely; surface partial status and route to revision.","copyMustInclude":"completed, passed, pending, blocked before sending, and full list remains paused"}],"requiredCampaignState":["campaignId","workflowTableId","approvedMessageTemplate","filterRubricsApproved when enableICPFilters=true"],"allowedTools":["get_subskill_asset","queue_campaign_cells","select_campaign_cells","get_campaign_table_schema","wait_for_campaign_processing","update_campaign","AskUserQuestion","request_user_input"],"doNotAllow":["import_leads","list_senders","start_campaign","enrich_with_prospeo","bulk_enrich_with_prospeo","check_rubric"],"hardRules":["campaign_processing_waits_are_stats_only_by_default","queue_review_batch_by_selector_never_fetch_rows_for_cell_ids","timeout_never_repeats_without_customer_handoff","timeout_or_underfloor_sample_never_advances_to_settings"],"waitFor":["sample_validated","sample_revision_required"],"transitions":{"sample_validated":"auto-execute-messaging","sample_revision_required":"lead-review","revise_leads":"find-leads","revise_rubric":"filter-rubric","escalation_triggered":"escalation"}},{"id":"auto-execute-messaging","label":"Generate initial campaign-row messages","currentStepValue":"auto-execute-messaging","references":["references/parallel-critique-protocol.md","references/thomas-variant-selection.md","references/sellable-cleanup-rules.md","references/step-15-re-cascade.md"],"onEnter":[{"tool":"queue_campaign_cells","batchSize":100,"when":"any passing row has pending, empty, or stale Generate Message cell","requiredFields":["workflowTableId","columnRole","rowSelector"],"requiredValues":{"columnRole":"generateMessage","rowSelector":{"type":"needsGeneratedMessage"},"forceRerun":false},"cellSource":"selector-engine needsGeneratedMessage rows using approved campaign brief template"},{"tool":"wait_for_campaign_processing","purpose":"wait_for_first_current_revision_generated_message","requiredValues":{"minGeneratedMessages":1,"templateRevision":"current"},"readVia":"stats_only_tool_result","invariant":"A generated message implies the pass gate; stale or wrong-revision messages are excluded from readiness."},{"action":"observe_generate_message_results","reference":"references/step-15-re-cascade.md"},{"action":"enforce_token_contract","modeFromConfig":"messaging.tokenContract"},{"action":"optional_critique_pass","enabledFromConfig":"messaging.critique.enabled","protocol":"references/parallel-critique-protocol.md","sampleSizeFromConfig":"messaging.critique.sampleSize","budgetUsdCapFromConfig":"messaging.critique.budgetUsdCap","perCriticTimeoutFromConfig":"messaging.critique.perCriticTimeoutSeconds","totalTimeoutFromConfig":"messaging.critique.totalTimeoutSeconds","criticsFromConfig":"messaging.critique.critics","enforceFinalizerPassFromConfig":"messaging.critique.synthesis.enforceFinalizerPass","rejectOnFakeProofFromConfig":"messaging.critique.rejectOnFakeProof","rejectOnUnsupportedTokenFromConfig":"messaging.critique.rejectOnUnsupportedToken","onBudgetTrip":"halt_critique_continue_plain_tail","onPerCriticTimeout":"drop_critic_proceed_with_remaining","onTotalTimeout":"persist_plain_message_for_row","onTokenContractViolation":"persist_plain_message_for_row","onFakeProof":"persist_plain_message_for_row"},{"action":"optional_opus_subset","enabledFromConfig":"messaging.critique.opus.enabled","selection":"references/thomas-variant-selection.md","maxMessagesPerPassFromConfig":"messaging.critique.opus.maxMessagesPerPass","budgetUsdCapFromConfig":"messaging.critique.opus.budgetUsdCap","onOpusBudgetTrip":"halt_opus_continue_non_opus","onOpusTokenContractViolation":"fallback_to_non_opus_rewrite"},{"tool":"update_campaign","requiredValues":{"currentStep":"auto-execute-messaging","watchNarration.stage":"review-ready"},"watchNarrationRule":"Say the first passing generated message is ready in Messages; next is review before Settings."},{"action":"ask_generated_message_review_choice","uses":"request_user_input","choices":["Approve reviewed draft rows and continue to Settings","Revise filters","Revise message template","Pause here"],"copyMustInclude":["approve the reviewed draft rows and continue to Settings","tokens resolved","company/person match","proof claim","prospect angle","language/tone when known","obvious bad fits","Would I take this call?","weak personalization can burn the sender's reputation","full list remains paused","this approves reviewed draft rows for setup; final Start still controls sending"],"autoSelectIf":"yolo_mode and the first passing generated message clears the quality floor","copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"]}],"allowedTools":["get_subskill_asset","get_campaign_table_schema","select_campaign_cells","queue_campaign_cells","wait_for_campaign_processing","revise_message_template_and_rerun","update_campaign","AskUserQuestion","request_user_input"],"doNotAllow":["import_leads","list_senders","start_campaign","generate_messages"],"hardRules":["generated_message_count_excludes_stale_or_wrong_revision_messages","message_template_revision_uses_brief_update_then_generate_message_rerun","do_not_overwrite_generated_message_cells_directly","critique_failure_never_escalates","critique_sample_size_bounded_by_config","first_passing_generated_message_unblocks_review","critics_fixed_at_targeting_copy_voice","synthesis_enforces_message_token_contract","opus_reserved_for_highest_value_subset","proposed_token_never_persisted_in_rewrite"],"waitFor":["generated_messages_approved","sample_revision_required"],"transitions":{"generated_messages_approved":"awaiting-user-greenlight","revise_filters":"filter-rubric","revise_messaging":"message-generation","escalation_triggered":"escalation"}},{"id":"awaiting-user-greenlight","label":"Settings, sender, sequence, and greenlight","currentStepValue":"awaiting-user-greenlight","reference":"references/final-handoff-contract.md","onEnter":[{"tool":"get_campaign"},{"tool":"list_senders","purpose":"surface available connected senders only at Settings"},{"tool":"update_campaign","requiredValues":{"currentStep":"settings","watchNarration.stage":"review-ready"},"watchNarrationRule":"Say message review is complete, app is on Settings, lead research/filtering happened outside LinkedIn, and nothing sends until Start."},{"action":"surface_sender_and_slack_handoff","requiredVisibleContent":["connect or select a LinkedIn sender","Lead research and filtering already happened outside your LinkedIn account.","This account is only used for approved sending after final launch.","Nothing sends until the final Start step.","Slack reply review","recommended sequence","final launch confirmation is still ahead"],"copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"]},{"action":"ask_sender_selection","uses":"request_user_input","singleChoice":true,"choices":["Use this connected sender","Connect a different sender in Settings","Pause here"],"autoSelectIf":"yolo_mode and exactly one safe connected sender is available","copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"]},{"action":"attach_selected_sender","tool":"update_campaign","when":"user selected an available connected sender","requiredValues":{"senderIds":["{selectedSenderId}"],"currentStep":"sequence","watchNarration.stage":"review-ready"},"watchNarrationRule":"Say the sender was attached and the app is moving to Sequence review; sequence remains editable and nothing sends until Start."},{"tool":"attach_recommended_sequence","when":"after senderIds are attached","requiredValues":{"campaignId":"{campaignId}","currentStep":"send","watchNarration.stage":"review-ready"},"watchNarrationRule":"The sequence tool owns this visible beat: say the recommended follow-up sequence is attached, the app is on final launch review, and it is still not sending until Start."},{"action":"ask_final_launch_greenlight","uses":"request_user_input","singleChoice":true,"choices":["Start campaign","Review campaign first","Pause here"],"copyMustInclude":["quality confidence means sample messages and prospect angle were checked","launch confidence means sender, sequence, and Start are ready","approved messages begin sending according to the sequence","replies and meetings follow connected settings","you can monitor and pause","no hidden extra approval disappears after clicking Start"],"onUserStart":"claude-greenlight","neverAutoSelectInYolo":true,"copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"]}],"allowedTools":["get_campaign","get_campaign_navigation_state","list_senders","update_campaign","attach_recommended_sequence","AskUserQuestion","request_user_input"],"doNotAllow":["start_campaign","import_leads"],"autoStart":false,"watchRequired":true,"waitFor":["sender_connection_required","sender_attached","sequence_attached","ready_to_launch","user_greenlight","ui_start_detected"],"transitions":{"sender_connection_required":"awaiting-user-greenlight","sender_attached":"awaiting-user-greenlight","sequence_attached":"awaiting-user-greenlight","ready_to_launch":"awaiting-user-greenlight","user_greenlight":"claude-greenlight","ui_start_detected":"running"}},{"id":"claude-greenlight","label":"Explicit launch","reference":"references/final-handoff-contract.md","onEnter":[{"tool":"get_campaign"},{"action":"verify_sequence_and_current_step_before_start","requiredState":["workflowTableId","senderIds","sequenceTemplate","at least one approved generated message","currentStep in awaiting-user-greenlight|claude-greenlight|send"]},{"tool":"start_campaign","requiredFields":["campaignId"],"persistsCurrentStep":"running","watchNarrationRule":"After start_campaign succeeds, the running state must say the final greenlight was accepted, the campaign is now live/running, and the user can watch progress from the campaign."}],"allowedTools":["get_campaign","attach_recommended_sequence","start_campaign","AskUserQuestion","request_user_input"],"watchRequired":true,"waitFor":"campaign_started","transitions":{"campaign_started":"running"}},{"id":"running","label":"Campaign is live","currentStepValue":"running","onEnter":[{"action":"surface_campaign_live_confirmation_without_watch_link","copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"]}],"allowedTools":["get_campaign","AskUserQuestion","request_user_input"],"doNotAllow":["start_campaign"],"terminal":true},{"id":"escalation","label":"Escalation","reference":"references/escalation-ladder.md","allowedTools":["AskUserQuestion","request_user_input"],"doNotAllow":["start_campaign","import_leads"],"transitions":{"revise_brief":"brief-interview","revise_leads":"find-leads","revise_rubric":"filter-rubric","revise_messaging":"message-generation","abort":"abort"}},{"id":"abort","label":"Abort","allowedTools":["AskUserQuestion","request_user_input"],"terminal":true}]}
|
|
@@ -148,7 +148,7 @@ summary.
|
|
|
148
148
|
Do not add bracketed inline source tags to individual claims.
|
|
149
149
|
Include this correction affordance before
|
|
150
150
|
approval: "If your site or LinkedIn is stale, tell me what to update before
|
|
151
|
-
I build the lead list." Ask "
|
|
151
|
+
I build the lead list." Ask "Does this brief look right, and how should Sellable continue?"
|
|
152
152
|
Keep this approval positive and short; do not add a "this approval does not
|
|
153
153
|
approve..." sentence or a list of later gated steps.
|
|
154
154
|
2. **lead sample** — first 5-10 rows from `lead-sample.json` rendered as
|
|
@@ -9,7 +9,7 @@ visibility: internal
|
|
|
9
9
|
<role>
|
|
10
10
|
You are the Sellable LinkedIn post writer for a specific user.
|
|
11
11
|
|
|
12
|
-
Your job is not to invent a "viral" post from thin air. Your job is to preserve the user's raw idea, load their real voice/proof/stories, research what hooks
|
|
12
|
+
Your job is not to invent a "viral" post from thin air. Your job is to preserve the user's raw idea, load their real voice/proof/stories, research what hooks have been working over the past few months, and save a validated draft that sounds like them.
|
|
13
13
|
|
|
14
14
|
Hard fail patterns:
|
|
15
15
|
|
|
@@ -50,6 +50,7 @@ Before drafting, load all required assets with `mcp__sellable__get_subskill_asse
|
|
|
50
50
|
1. `subskillName: "create-post", assetPath: "references/post-file-contract.md"`
|
|
51
51
|
2. `subskillName: "create-post", assetPath: "references/hook-research-playbook.md"`
|
|
52
52
|
3. `subskillName: "create-post", assetPath: "references/post-validation.md"`
|
|
53
|
+
4. `subskillName: "create-post", assetPath: "references/gold-standard-post-pack.md"`
|
|
53
54
|
|
|
54
55
|
If any required asset is missing, unreadable, truncated without continuation, or internally inconsistent, return:
|
|
55
56
|
|
|
@@ -100,7 +101,16 @@ Load user memory before hook generation or drafting:
|
|
|
100
101
|
- `core/context-modes.md`
|
|
101
102
|
- `core/decision-rules.md`
|
|
102
103
|
- `core/references/**`
|
|
103
|
-
3.
|
|
104
|
+
3. Use `memory.postWritingRules` returned by `mcp__sellable__get_engage_memory` when present. If the host permits direct file reads and the memory response does not include it, read post-specific writing rules from `~/.sellable/configs/writing/posts.md` when available.
|
|
105
|
+
4. Read the gold-standard post pack from `core/references/linkedin-posts/INDEX.md` and its approved copied/distilled examples when present. `get_engage_memory` may return this through the composed `core/references/**` indexes; if the host permits direct file reads, load the files directly when needed.
|
|
106
|
+
|
|
107
|
+
`writing/posts.md` is a post-writing config file, not a draft library. Treat its structure this way:
|
|
108
|
+
|
|
109
|
+
- Top scratch or `## Draft Rule Candidates` sections are candidate taste notes only.
|
|
110
|
+
- The bottom `## Canonical Compiled Rules` section is the active post-writing config.
|
|
111
|
+
- `## Canonical Compiled Rules` overrides conflicting draft notes.
|
|
112
|
+
- Use draft candidates only when they do not conflict with canonical rules or when the user explicitly asks to test one.
|
|
113
|
+
- Do not save post bodies, hook research, or published records into `writing/posts.md`.
|
|
104
114
|
|
|
105
115
|
New users may have blank or missing core files until they run `$sellable:interview`. If story, proof, identity, or company memory is missing, ask for the missing material or ask the user to run `$sellable:interview`. Never fabricate what should have come from `story-bank.md`, `proof-ledger.md`, or `answer-bank.md`.
|
|
106
116
|
|
|
@@ -138,6 +148,31 @@ Use when the user says "run the pipeline on this idea" or asks for an immediate
|
|
|
138
148
|
Normal ad hoc mode still creates an ID'd idea artifact first with `mcp__sellable__capture_post_idea`, preserving the raw input first. Then run the full pipeline immediately.
|
|
139
149
|
|
|
140
150
|
If the user explicitly says not to save anything, label the output `unsaved_preview`, do not call it draft-ready, and do not mark it as validated for publishing.
|
|
151
|
+
|
|
152
|
+
## Gold Standard Pack Mode
|
|
153
|
+
|
|
154
|
+
Use when the user asks to import their best posts, research best posts in the space, build a gold-standard pack, or add inspiration examples.
|
|
155
|
+
|
|
156
|
+
Load `references/gold-standard-post-pack.md` and follow it exactly.
|
|
157
|
+
|
|
158
|
+
Personal best import:
|
|
159
|
+
|
|
160
|
+
1. Ask for the user's LinkedIn profile URL or handle if missing.
|
|
161
|
+
2. Call `mcp__sellable__fetch_linkedin_posts`.
|
|
162
|
+
3. Rank candidate posts by engagement quality, full-text availability, voice usefulness, and topic fit.
|
|
163
|
+
4. Split each candidate into hook, content/body mechanism, rhythm, sentence structure, proof/story use, voice fit, risks, and allowed use.
|
|
164
|
+
5. Show a numbered list and ask which posts to add.
|
|
165
|
+
6. Add only the user-approved selections to `~/.sellable/configs/core/references/linkedin-posts/**`.
|
|
166
|
+
|
|
167
|
+
Space benchmark research:
|
|
168
|
+
|
|
169
|
+
1. Use `mcp__sellable__search_engagement_posts` and the hook research playbook.
|
|
170
|
+
2. Shortlist high-performing posts from the user's space using the weighted signal rules.
|
|
171
|
+
3. Penalize lead magnets, giveaways, engagement bait, celebrity-only reach, and poor voice fit.
|
|
172
|
+
4. Show the user the candidates and ask: "These look good. Should we use any as gold standards?"
|
|
173
|
+
5. Add only the user-approved selections.
|
|
174
|
+
|
|
175
|
+
The approved pack is capped at 20 gold standards. If adding new approved examples would exceed 20, ask which existing item to replace or skip the overflow. Candidate lists can be longer, but approved saved standards cannot exceed 20.
|
|
141
176
|
</modes>
|
|
142
177
|
|
|
143
178
|
<pipeline>
|
|
@@ -145,8 +180,9 @@ If the user explicitly says not to save anything, label the output `unsaved_prev
|
|
|
145
180
|
|
|
146
181
|
1. Load all required assets.
|
|
147
182
|
2. Load memory and post writing rules.
|
|
148
|
-
3.
|
|
149
|
-
4.
|
|
183
|
+
3. Load the gold-standard post pack if present.
|
|
184
|
+
4. Verify auth/workspace if hook research will use Sellable search.
|
|
185
|
+
5. Capture or load the source idea.
|
|
150
186
|
|
|
151
187
|
If local idea capture succeeds but auth/workspace is missing, keep the idea and return a typed blocked state for draft readiness:
|
|
152
188
|
|
|
@@ -163,20 +199,27 @@ Use `references/hook-research-playbook.md`.
|
|
|
163
199
|
Default flow:
|
|
164
200
|
|
|
165
201
|
1. Convert the idea into 3-8 search keywords.
|
|
166
|
-
2. Call `mcp__sellable__search_engagement_posts
|
|
167
|
-
3. Shortlist high-engagement posts by topic fit and
|
|
202
|
+
2. Call `mcp__sellable__search_engagement_posts` with an explicit multi-month window. Default to `maxAgeDays: 120`, tightening to 30-60 days only when the topic is trend-sensitive.
|
|
203
|
+
3. Shortlist high-engagement posts by topic fit, hook strength, creator repeat evidence, and weighted engagement quality.
|
|
168
204
|
4. Because search results may only include previews, call `mcp__sellable__fetch_linkedin_posts` for shortlisted authors/profile URLs and match recent posts by URL/activity ID when full text is needed.
|
|
169
205
|
5. If full text cannot be matched, record `full_text_unavailable` and use only the preview. Do not invent missing body details.
|
|
170
|
-
6.
|
|
171
|
-
7.
|
|
206
|
+
6. Weigh shares/reposts above comments, comments above reactions, and reactions as weak reach unless paired with stronger signals. If shares/reposts are unavailable, record `repost_data_unavailable`.
|
|
207
|
+
7. Penalize lead-magnet or giveaway mechanics unless the user explicitly asks for a lead magnet post.
|
|
208
|
+
8. For story posts, extract the story mechanism that made the post work, not just the first line.
|
|
209
|
+
9. Extract hook structures, not wording.
|
|
210
|
+
10. Save the research with `mcp__sellable__save_hook_research`.
|
|
172
211
|
|
|
173
212
|
Record provenance:
|
|
174
213
|
|
|
175
214
|
- keywords
|
|
176
215
|
- filters
|
|
216
|
+
- search window
|
|
177
217
|
- source post URLs
|
|
178
218
|
- authors/profile URLs
|
|
179
|
-
- engagement totals
|
|
219
|
+
- engagement totals and available likes/comments/shares breakdown
|
|
220
|
+
- creator repeat evidence
|
|
221
|
+
- lead-magnet or engagement-bait penalties
|
|
222
|
+
- story mechanism when relevant
|
|
180
223
|
- full-text match status
|
|
181
224
|
- selected hook patterns
|
|
182
225
|
- why each pattern fits the user's idea and voice
|
|
@@ -189,13 +232,29 @@ Each hook must include:
|
|
|
189
232
|
|
|
190
233
|
- source hook pattern
|
|
191
234
|
- why it fits this idea
|
|
192
|
-
-
|
|
235
|
+
- `charCount`
|
|
236
|
+
- `firstLineChars`
|
|
237
|
+
- `firstTwoLinesChars`
|
|
238
|
+
- `mobilePreviewFit`
|
|
239
|
+
- `desktopPreviewFit`
|
|
240
|
+
- `lineCountEstimate`
|
|
241
|
+
- `truncationRisk`
|
|
242
|
+
- `rewriteIfTruncated`
|
|
193
243
|
- proof/story dependency
|
|
194
244
|
- AI-tell risk
|
|
195
245
|
- score
|
|
196
246
|
|
|
197
247
|
Do not copy source wording. Copy only the structure.
|
|
198
248
|
|
|
249
|
+
Use these LinkedIn preview budgets as conservative scoring constraints. LinkedIn rendering varies by device, font, and line break, so these are planning budgets, not exact guarantees:
|
|
250
|
+
|
|
251
|
+
- mobile first line: 35-45 chars
|
|
252
|
+
- mobile first two lines: 80-100 chars
|
|
253
|
+
- desktop first line: 70-90 chars
|
|
254
|
+
- desktop first two lines: 140-180 chars
|
|
255
|
+
|
|
256
|
+
If a hook's point depends on text after the likely preview, rewrite it before selecting it.
|
|
257
|
+
|
|
199
258
|
## Step 3: Draft
|
|
200
259
|
|
|
201
260
|
Draft from:
|
|
@@ -206,6 +265,7 @@ Draft from:
|
|
|
206
265
|
- user's core memory
|
|
207
266
|
- story/proof files
|
|
208
267
|
- post writing rules
|
|
268
|
+
- 1-3 relevant approved gold standards when available
|
|
209
269
|
|
|
210
270
|
If a claim cannot be traced to the raw idea, core memory, or user answer in the current session, remove it or ask.
|
|
211
271
|
|
|
@@ -221,9 +281,12 @@ Every saved draft needs a validation receipt with:
|
|
|
221
281
|
- selected hook and why
|
|
222
282
|
- proof claims used and source
|
|
223
283
|
- story/proof files consulted
|
|
284
|
+
- gold standards consulted
|
|
285
|
+
- LinkedIn preview audit
|
|
224
286
|
- simplifier/concrete-language audit findings
|
|
225
287
|
- voice audit findings
|
|
226
288
|
- anti-AI audit findings
|
|
289
|
+
- outbound AI-tell audit findings
|
|
227
290
|
- finalizer changes
|
|
228
291
|
- blocked/retry-needed reasons, if any
|
|
229
292
|
|
|
@@ -278,6 +341,7 @@ validation_summary:
|
|
|
278
341
|
proof: pass | needs_user_input | blocked
|
|
279
342
|
voice: pass | needs_revision
|
|
280
343
|
anti_ai: pass | needs_revision
|
|
344
|
+
linkedin_preview: pass | needs_revision
|
|
281
345
|
concrete_language: pass | needs_revision
|
|
282
346
|
next_step: <what the user should do next>
|
|
283
347
|
```
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
# Gold Standard Post Pack
|
|
2
|
+
|
|
3
|
+
The gold-standard pack is user-owned memory with an MCP-owned schema.
|
|
4
|
+
|
|
5
|
+
MCP owns:
|
|
6
|
+
|
|
7
|
+
- this schema
|
|
8
|
+
- selection workflow
|
|
9
|
+
- scoring rubric
|
|
10
|
+
- decomposition rules
|
|
11
|
+
- validation rules
|
|
12
|
+
|
|
13
|
+
The user's computer owns:
|
|
14
|
+
|
|
15
|
+
- personal best posts
|
|
16
|
+
- approved inspiration posts from the space
|
|
17
|
+
- copied or distilled examples
|
|
18
|
+
- approval notes
|
|
19
|
+
- allowed-use limits
|
|
20
|
+
|
|
21
|
+
## Storage
|
|
22
|
+
|
|
23
|
+
Approved gold standards live under:
|
|
24
|
+
|
|
25
|
+
```text
|
|
26
|
+
~/.sellable/configs/core/references/linkedin-posts/
|
|
27
|
+
INDEX.md
|
|
28
|
+
gold-standard-<slug>.md
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
The pack is capped at 20 approved gold standards. Candidate examples do not
|
|
32
|
+
count toward the cap until the user approves them.
|
|
33
|
+
|
|
34
|
+
Do not store gold standards under `~/.sellable/content/linkedin/**`. Content
|
|
35
|
+
folders are for ideas, hook research, drafts, published records, and future
|
|
36
|
+
comment history. Gold standards are durable writing memory.
|
|
37
|
+
|
|
38
|
+
## Import Modes
|
|
39
|
+
|
|
40
|
+
### Personal Best Import
|
|
41
|
+
|
|
42
|
+
Use when the user asks to import their best posts, calibrate on their old posts,
|
|
43
|
+
or build the pack from their LinkedIn profile.
|
|
44
|
+
|
|
45
|
+
1. Ask for the user's LinkedIn profile URL or handle if it is not already known.
|
|
46
|
+
2. Call `mcp__sellable__fetch_linkedin_posts`.
|
|
47
|
+
3. Rank candidate personal posts by engagement quality, topic fit, full-text
|
|
48
|
+
availability, voice usefulness, and whether the user says the post felt like
|
|
49
|
+
them.
|
|
50
|
+
4. Present a numbered candidate list.
|
|
51
|
+
5. Ask which posts to add, skip, or mark as anti-examples.
|
|
52
|
+
6. Add only the user-approved selections.
|
|
53
|
+
|
|
54
|
+
### Space Benchmark Research
|
|
55
|
+
|
|
56
|
+
Use when the user asks to research what is working in the space or add outside
|
|
57
|
+
gold standards.
|
|
58
|
+
|
|
59
|
+
1. Derive search terms from the user's topic, ICP, current idea, and proven
|
|
60
|
+
search memory.
|
|
61
|
+
2. Use the hook research playbook and `mcp__sellable__search_engagement_posts`.
|
|
62
|
+
3. Prefer recent multi-month winners, repeated creator success, strong
|
|
63
|
+
share/repost signals, and full-text availability.
|
|
64
|
+
4. Penalize lead magnets, giveaway mechanics, engagement bait, celebrity-only
|
|
65
|
+
reach, and examples that cannot be adapted to the user's voice.
|
|
66
|
+
5. Present candidates with a clear "should we use these as gold standards?"
|
|
67
|
+
approval gate.
|
|
68
|
+
6. Add only the user-approved selections.
|
|
69
|
+
|
|
70
|
+
## Candidate Presentation
|
|
71
|
+
|
|
72
|
+
Show compact candidate cards. Include:
|
|
73
|
+
|
|
74
|
+
- candidate number
|
|
75
|
+
- type: `personal_best`, `space_benchmark`, or `anti_example`
|
|
76
|
+
- author
|
|
77
|
+
- source URL
|
|
78
|
+
- engagement breakdown when available
|
|
79
|
+
- why it might belong in the pack
|
|
80
|
+
- hook mechanism
|
|
81
|
+
- content/body mechanism
|
|
82
|
+
- rhythm notes
|
|
83
|
+
- sentence-structure notes
|
|
84
|
+
- proof/story use
|
|
85
|
+
- voice fit
|
|
86
|
+
- risks
|
|
87
|
+
- allowed use recommendation
|
|
88
|
+
|
|
89
|
+
Approval prompt:
|
|
90
|
+
|
|
91
|
+
```text
|
|
92
|
+
Gold-standard pack: <current_count>/20 approved.
|
|
93
|
+
|
|
94
|
+
These look like candidates worth saving. Which should I add?
|
|
95
|
+
Reply with numbers to add, "skip all", or "replace <existing> with <new>" if the pack is full.
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Do not write candidates to the approved pack before the user chooses.
|
|
99
|
+
|
|
100
|
+
## Approved Index Schema
|
|
101
|
+
|
|
102
|
+
`INDEX.md` should use a table with these fields:
|
|
103
|
+
|
|
104
|
+
- `Status` - `approved` or `anti-example`
|
|
105
|
+
- `Type` - `personal_best` or `space_benchmark`
|
|
106
|
+
- `Title`
|
|
107
|
+
- `Source URL`
|
|
108
|
+
- `Copied Path`
|
|
109
|
+
- `Added`
|
|
110
|
+
- `Score`
|
|
111
|
+
- `Tags`
|
|
112
|
+
- `Primary Use`
|
|
113
|
+
- `Allowed Use`
|
|
114
|
+
- `Hook Pattern`
|
|
115
|
+
- `Body Pattern`
|
|
116
|
+
- `Rhythm Notes`
|
|
117
|
+
- `Sentence Notes`
|
|
118
|
+
- `Risks`
|
|
119
|
+
- `Key`
|
|
120
|
+
|
|
121
|
+
Use stable keys from normalized source URL/title plus an optional content hash.
|
|
122
|
+
If the same source appears again, update the existing row instead of adding a
|
|
123
|
+
duplicate.
|
|
124
|
+
|
|
125
|
+
## Approved Example File Schema
|
|
126
|
+
|
|
127
|
+
Each copied or distilled `gold-standard-<slug>.md` should include:
|
|
128
|
+
|
|
129
|
+
```text
|
|
130
|
+
# <Title>
|
|
131
|
+
|
|
132
|
+
Source URL:
|
|
133
|
+
Type:
|
|
134
|
+
Status:
|
|
135
|
+
Added:
|
|
136
|
+
Allowed use:
|
|
137
|
+
Tags:
|
|
138
|
+
|
|
139
|
+
## Original Or Distilled Text
|
|
140
|
+
|
|
141
|
+
<Full text only when safe and approved. Otherwise store a short distilled summary.>
|
|
142
|
+
|
|
143
|
+
## Hook Breakdown
|
|
144
|
+
|
|
145
|
+
- hook text:
|
|
146
|
+
- char count:
|
|
147
|
+
- first-line chars:
|
|
148
|
+
- first-two-line chars:
|
|
149
|
+
- mobile preview fit:
|
|
150
|
+
- desktop preview fit:
|
|
151
|
+
- hook mechanism:
|
|
152
|
+
- internal question:
|
|
153
|
+
- emotional trigger:
|
|
154
|
+
|
|
155
|
+
## Content / Body Breakdown
|
|
156
|
+
|
|
157
|
+
- thesis:
|
|
158
|
+
- body mechanism:
|
|
159
|
+
- story mechanism:
|
|
160
|
+
- proof mechanism:
|
|
161
|
+
- argument moves:
|
|
162
|
+
- close:
|
|
163
|
+
- what made it work:
|
|
164
|
+
|
|
165
|
+
## Rhythm
|
|
166
|
+
|
|
167
|
+
- line breaks:
|
|
168
|
+
- paragraph length:
|
|
169
|
+
- sentence length:
|
|
170
|
+
- pacing:
|
|
171
|
+
- punctuation:
|
|
172
|
+
|
|
173
|
+
## Sentence Structure
|
|
174
|
+
|
|
175
|
+
- sentence starts:
|
|
176
|
+
- fragments:
|
|
177
|
+
- contrast moves:
|
|
178
|
+
- specificity moves:
|
|
179
|
+
- repeated syntax:
|
|
180
|
+
|
|
181
|
+
## Usage Notes
|
|
182
|
+
|
|
183
|
+
- imitate:
|
|
184
|
+
- do not copy:
|
|
185
|
+
- voice fit:
|
|
186
|
+
- risks:
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
## Selection Rules For Drafting
|
|
190
|
+
|
|
191
|
+
Before drafting a post, use at most 1-3 relevant gold standards. Prefer:
|
|
192
|
+
|
|
193
|
+
- the user's own approved posts over outside benchmarks
|
|
194
|
+
- examples with matching topic, mechanism, or story shape
|
|
195
|
+
- examples whose allowed use includes `imitate structure`
|
|
196
|
+
- examples with clear hook/body/rhythm/sentence decomposition
|
|
197
|
+
|
|
198
|
+
Never copy outside wording. For personal posts, reuse wording only when the user
|
|
199
|
+
explicitly asks to reuse their own line. Otherwise, use the pack for structure,
|
|
200
|
+
taste, and judgment.
|
|
201
|
+
|
|
202
|
+
If the pack is empty, continue with core memory and hook research. Do not block
|
|
203
|
+
normal drafting just because no gold standards exist.
|
|
@@ -15,11 +15,51 @@ Derive 3-8 keyword searches from:
|
|
|
15
15
|
|
|
16
16
|
Use `mcp__sellable__search_engagement_posts` with practical constraints:
|
|
17
17
|
|
|
18
|
-
-
|
|
18
|
+
- explicit `maxAgeDays: 120` by default so research covers the past 30-120 days, not only this week's posts
|
|
19
|
+
- tighten to 30-60 days for trend-sensitive topics; widen only when the topic has low volume
|
|
19
20
|
- high enough engagement to matter
|
|
20
21
|
- narrow enough to match the idea
|
|
22
|
+
- enough result depth to see whether a creator has repeated winners, not just one viral outlier
|
|
21
23
|
|
|
22
|
-
Record every keyword, filter, and result count.
|
|
24
|
+
Record every keyword, filter, search window, page, and result count.
|
|
25
|
+
|
|
26
|
+
## Weighted Signals
|
|
27
|
+
|
|
28
|
+
Rank source posts with a weighted signal view. Do not sort by total engagement alone.
|
|
29
|
+
|
|
30
|
+
Use this scoring shape:
|
|
31
|
+
|
|
32
|
+
```text
|
|
33
|
+
hook_score =
|
|
34
|
+
topic_fit
|
|
35
|
+
+ hook_clarity
|
|
36
|
+
+ creator_repeat_success
|
|
37
|
+
+ repost_share_strength
|
|
38
|
+
+ comment_quality
|
|
39
|
+
- lead_magnet_penalty
|
|
40
|
+
- engagement_bait_penalty
|
|
41
|
+
- off_voice_penalty
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Guidance:
|
|
45
|
+
|
|
46
|
+
- Shares/reposts are the strongest signal because a reader spends their own feed reputation.
|
|
47
|
+
- Comments are stronger than reactions only when they are substantive; giveaway comments are weak.
|
|
48
|
+
- Reactions are weak reach unless paired with shares/reposts or strong comments.
|
|
49
|
+
- If the tool returns `engagement.shares`, treat it as the repost/share proxy.
|
|
50
|
+
- If share or repost data is unavailable, record `repost_data_unavailable` instead of inventing it.
|
|
51
|
+
- Prefer creators with repeated high-performing posts in the same lane over one-off viral posts.
|
|
52
|
+
|
|
53
|
+
Penalize lead-magnet and engagement-bait mechanics unless the user explicitly asks for that style:
|
|
54
|
+
|
|
55
|
+
- `comment "template"` / `comment "guide"` / `comment "playbook"`
|
|
56
|
+
- "DM me"
|
|
57
|
+
- "I'll send it"
|
|
58
|
+
- "free template"
|
|
59
|
+
- "free prompt"
|
|
60
|
+
- "repost for access"
|
|
61
|
+
- "like and comment"
|
|
62
|
+
- broad giveaway framing that makes engagement inflate without proving the hook/body worked
|
|
23
63
|
|
|
24
64
|
## Full Text Reality
|
|
25
65
|
|
|
@@ -42,15 +82,33 @@ For each shortlisted source post, record:
|
|
|
42
82
|
|
|
43
83
|
- URL
|
|
44
84
|
- author
|
|
45
|
-
- engagement totals
|
|
85
|
+
- engagement totals and available likes/comments/shares breakdown
|
|
86
|
+
- creator repeat evidence
|
|
46
87
|
- visible hook text or preview
|
|
47
88
|
- line count and mobile preview fit
|
|
48
89
|
- hook mechanism
|
|
90
|
+
- story mechanism when the post is a story
|
|
49
91
|
- internal question created
|
|
50
92
|
- emotional trigger
|
|
51
93
|
- proof/story dependency
|
|
94
|
+
- lead magnet or engagement bait penalty
|
|
95
|
+
- weighted signal notes
|
|
52
96
|
- replicability score
|
|
53
97
|
|
|
98
|
+
## Story Mechanism
|
|
99
|
+
|
|
100
|
+
For story posts, do not reduce the source to "strong first line" or a generic lesson. Extract the mechanism that made the story travel:
|
|
101
|
+
|
|
102
|
+
- concrete first-person moment
|
|
103
|
+
- visible tension or tradeoff
|
|
104
|
+
- mistake, regret, reversal, or confession
|
|
105
|
+
- before/after shift
|
|
106
|
+
- founder/operator decision point
|
|
107
|
+
- uncomfortable but specific truth
|
|
108
|
+
- proof embedded in the scene rather than pasted on later
|
|
109
|
+
|
|
110
|
+
If the source story only works because of celebrity, outrage, giveaway mechanics, or an unavailable body section, mark it as low-replicability or `full_text_unavailable`.
|
|
111
|
+
|
|
54
112
|
## Blocked States
|
|
55
113
|
|
|
56
114
|
Local idea capture can still succeed when research fails.
|
|
@@ -12,9 +12,12 @@ Every saved draft needs a validation receipt. A draft without this receipt is no
|
|
|
12
12
|
- `proofClaimsUsed`
|
|
13
13
|
- `proofClaimSources`
|
|
14
14
|
- `storyFilesConsulted`
|
|
15
|
+
- `goldStandardsConsulted`
|
|
15
16
|
- `voiceRulesApplied`
|
|
17
|
+
- `linkedinPreviewAudit`
|
|
16
18
|
- `simplifierConcreteLanguageAudit`
|
|
17
19
|
- `antiAiAudit`
|
|
20
|
+
- `outboundAiTellAudit`
|
|
18
21
|
- `finalizerChanges`
|
|
19
22
|
- `blockedReasons`
|
|
20
23
|
- `retryNeededReasons`
|
|
@@ -29,6 +32,8 @@ Each candidate should include:
|
|
|
29
32
|
- hook text
|
|
30
33
|
- source pattern
|
|
31
34
|
- score
|
|
35
|
+
- char count and first-line / first-two-line preview measurements
|
|
36
|
+
- mobile and desktop preview fit
|
|
32
37
|
- proof/story dependency
|
|
33
38
|
- AI-tell risk
|
|
34
39
|
- why it should win or lose
|
|
@@ -43,6 +48,29 @@ After the first draft:
|
|
|
43
48
|
4. replace generic language with concrete words only when supported
|
|
44
49
|
5. preserve the user's actual story and point
|
|
45
50
|
6. remove AI tells
|
|
51
|
+
7. re-check LinkedIn preview fit after edits
|
|
52
|
+
|
|
53
|
+
## LinkedIn Preview Audit
|
|
54
|
+
|
|
55
|
+
Audit the selected hook and top candidates against conservative LinkedIn preview budgets:
|
|
56
|
+
|
|
57
|
+
- mobile first line: 35-45 chars
|
|
58
|
+
- mobile first two lines: 80-100 chars
|
|
59
|
+
- desktop first line: 70-90 chars
|
|
60
|
+
- desktop first two lines: 140-180 chars
|
|
61
|
+
|
|
62
|
+
Record:
|
|
63
|
+
|
|
64
|
+
- `charCount`
|
|
65
|
+
- `firstLineChars`
|
|
66
|
+
- `firstTwoLinesChars`
|
|
67
|
+
- `mobilePreviewFit`
|
|
68
|
+
- `desktopPreviewFit`
|
|
69
|
+
- `lineCountEstimate`
|
|
70
|
+
- `truncationRisk`
|
|
71
|
+
- `rewriteIfTruncated`
|
|
72
|
+
|
|
73
|
+
LinkedIn rendering varies by device and line break, so this is a planning audit. If the hook only works after likely truncation, rewrite it.
|
|
46
74
|
|
|
47
75
|
## Simplifier / Concrete-Language Audit
|
|
48
76
|
|
|
@@ -59,7 +87,25 @@ Do not make language concrete by inventing new facts.
|
|
|
59
87
|
|
|
60
88
|
## Anti-AI Audit
|
|
61
89
|
|
|
62
|
-
|
|
90
|
+
Start with the Outbound Base AI-Tell Layer from the message pipeline, adapted for public posts. This is a starter smell detector, not permission to run outbound workflows or force outbound copy style.
|
|
91
|
+
|
|
92
|
+
Reject or rewrite outbound-derived tells when they appear in a post:
|
|
93
|
+
|
|
94
|
+
- body-level self-introductions that add no story context
|
|
95
|
+
- source-citation phrasing such as "your bio says", "your profile shows", or "saw on LinkedIn"
|
|
96
|
+
- explicit date or duration precision that feels scraped
|
|
97
|
+
- block quotes of another person's phrasing longer than 4 words
|
|
98
|
+
- parenthetical stack or feature dumps
|
|
99
|
+
- over-precise numerics without a traceable source
|
|
100
|
+
- hardcoded signoffs
|
|
101
|
+
- vague proof brags such as "trusted by leaders" without named or sourced proof
|
|
102
|
+
- formal CTA phrasing
|
|
103
|
+
- reusable openers that could fit any topic
|
|
104
|
+
- em dashes in the draft body unless the user's post rules explicitly allow them
|
|
105
|
+
- resume-recap or scraped-personalization bridges
|
|
106
|
+
- flattened "worth sending" bridges
|
|
107
|
+
|
|
108
|
+
Also reject or rewrite post-native AI tells:
|
|
63
109
|
|
|
64
110
|
- "game-changing"
|
|
65
111
|
- "leverage"
|
|
@@ -67,9 +113,13 @@ Reject or rewrite:
|
|
|
67
113
|
- "this is key"
|
|
68
114
|
- "let that sink in"
|
|
69
115
|
- "read that again"
|
|
116
|
+
- "here's the thing"
|
|
117
|
+
- "unpopular opinion" without a specific argument
|
|
70
118
|
- generic lessons without a story
|
|
71
119
|
- tidy three-part frameworks that were not in the source material
|
|
72
120
|
- fabricated numbers or examples
|
|
121
|
+
- contrarian bait without proof
|
|
122
|
+
- lead-magnet bait when the user did not ask for a lead magnet
|
|
73
123
|
|
|
74
124
|
## Proof And Voice Gates
|
|
75
125
|
|
|
@@ -83,6 +133,10 @@ Every claim must trace to at least one of:
|
|
|
83
133
|
- `core/answer-bank.md`
|
|
84
134
|
- approved reference material
|
|
85
135
|
|
|
136
|
+
Gold standards can guide structure, rhythm, and judgment. They cannot create new
|
|
137
|
+
claims. If an outside gold standard is used, verify the draft copied no outside
|
|
138
|
+
wording unless the user explicitly approved quotation.
|
|
139
|
+
|
|
86
140
|
If the necessary proof or story is missing, ask the user or return blocked/retry-needed.
|
|
87
141
|
|
|
88
142
|
## Ready Status
|
|
@@ -66,3 +66,30 @@ Personal LinkedIn posts and inspiration examples deserve especially clear
|
|
|
66
66
|
notes: explain whether the lesson is voice, hook shape, proof handling,
|
|
67
67
|
argument structure, audience fit, or a pattern to avoid. Do not mix admiration
|
|
68
68
|
with permission to copy; many inspiration examples should be structural only.
|
|
69
|
+
|
|
70
|
+
## LinkedIn Post Gold Standards
|
|
71
|
+
|
|
72
|
+
The approved LinkedIn post gold-standard pack lives in
|
|
73
|
+
`~/.sellable/configs/core/references/linkedin-posts/` and is capped at 20
|
|
74
|
+
approved examples. User-owned examples can be personal best posts or
|
|
75
|
+
user-approved space benchmarks researched from high-performing posts in the
|
|
76
|
+
space.
|
|
77
|
+
|
|
78
|
+
Do not add researched outside posts directly to the approved pack. First show a
|
|
79
|
+
candidate list and ask which examples the user wants to add, skip, mark as
|
|
80
|
+
anti-examples, or use to replace an existing item when the pack is full.
|
|
81
|
+
|
|
82
|
+
Each approved post reference should split the lesson into:
|
|
83
|
+
|
|
84
|
+
- hook mechanism
|
|
85
|
+
- content/body mechanism
|
|
86
|
+
- rhythm and pacing
|
|
87
|
+
- sentence structure
|
|
88
|
+
- proof/story use
|
|
89
|
+
- voice fit
|
|
90
|
+
- risks and allowed use
|
|
91
|
+
|
|
92
|
+
Outside examples are structure/taste references only unless the user explicitly
|
|
93
|
+
approves quotation. Personal posts can be stronger voice references, but still
|
|
94
|
+
record whether to quote, imitate structure, summarize, keep private, or treat as
|
|
95
|
+
an anti-example.
|