@sellable/mcp 0.1.212 → 0.1.214

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.
Files changed (48) hide show
  1. package/README.md +7 -7
  2. package/agents/post-find-leads-message-scout.md +176 -134
  3. package/dist/engage-memory.js +0 -5
  4. package/dist/identity-memory.js +0 -4
  5. package/dist/tools/engage-memory.js +2 -2
  6. package/dist/tools/leads.js +7 -3
  7. package/dist/tools/prompts.js +18 -12
  8. package/package.json +1 -1
  9. package/skills/create-campaign/SKILL.md +11 -12
  10. package/skills/create-campaign/context/learnings.md +1 -1
  11. package/skills/create-campaign/references/brief-template.md +2 -2
  12. package/skills/create-campaign-brief/references/brief-template.md +2 -2
  13. package/skills/create-campaign-brief/references/draft-lifecycle.md +1 -1
  14. package/skills/create-campaign-brief/references/examples/briefs/gelee.md +2 -2
  15. package/skills/create-campaign-brief/references/examples/briefs/superpower.md +41 -28
  16. package/skills/create-campaign-brief/references/phase75-active-runtime-message-pack.md +27 -36
  17. package/skills/create-campaign-v2/SKILL.md +16 -9
  18. package/skills/create-campaign-v2/core/auto-execute.README.md +11 -11
  19. package/skills/create-campaign-v2/core/auto-execute.yaml +4 -4
  20. package/skills/create-campaign-v2/core/flow.v2.json +1 -1
  21. package/skills/create-campaign-v2/references/ai-tells.md +54 -19
  22. package/skills/create-campaign-v2/references/approval-gate-framing.md +10 -10
  23. package/skills/create-campaign-v2/references/escalation-ladder.md +3 -3
  24. package/skills/create-campaign-v2/references/final-handoff-contract.md +3 -3
  25. package/skills/create-campaign-v2/references/gold-standard-message-examples.md +295 -226
  26. package/skills/create-campaign-v2/references/gold-standard-message-patterns.md +48 -9
  27. package/skills/create-campaign-v2/references/gold-standard-message-validation-example.md +4 -4
  28. package/skills/create-campaign-v2/references/lead-validation-preview.md +1 -1
  29. package/skills/create-campaign-v2/references/parallel-critique-protocol.md +10 -10
  30. package/skills/create-campaign-v2/references/sample-validation-loop.md +3 -3
  31. package/skills/create-campaign-v2/references/{thomas-revision-filters.md → sellable-cleanup-rules.md} +29 -19
  32. package/skills/create-campaign-v2/references/step-15-re-cascade.md +1 -1
  33. package/skills/create-campaign-v2/references/thomas-variant-selection.md +1 -1
  34. package/skills/create-campaign-v2/references/validation-criteria.md +24 -18
  35. package/skills/create-campaign-v2-tail/SKILL.md +7 -7
  36. package/skills/create-campaign-v2-validation/SKILL.md +23 -7
  37. package/skills/create-post/SKILL.md +27 -27
  38. package/skills/engage/SKILL.md +12 -12
  39. package/skills/engage/core/README.md +14 -14
  40. package/skills/find-leads/SKILL.md +1 -1
  41. package/skills/generate-messages/SKILL.md +305 -147
  42. package/skills/interview/SKILL.md +24 -24
  43. package/skills/interview/references/legacy-linkedin-interview.md +12 -12
  44. package/skills/interview/references/reference-curation.md +4 -4
  45. package/skills/interview/references/voice-capture-method.md +1 -1
  46. package/skills/load-voice/SKILL.md +21 -25
  47. package/skills/research/SKILL.md +1 -1
  48. package/skills/research/config.json +9 -0
package/README.md CHANGED
@@ -334,12 +334,12 @@ with outcomes, and tracked people (LinkedIn profiles), all scoped by workspace.
334
334
 
335
335
  - Auth + active workspace: `~/.sellable/config.json` (or `SELLABLE_CONFIG_PATH`; old `$CODEX_HOME/sellable.json`, `~/.codex/sellable.json`, and `~/.claude/sellable.json` files remain compatibility fallbacks)
336
336
  - Engage memory (style guide + searches + tracked people): `sellable.engage.json` (or `SELLABLE_ENGAGE_MEMORY_PATH`)
337
- - Team-editable, project-local configs (recommended to edit in git):
338
- - `./.sellable/configs/writing/styleguide.md`
339
- - `./.sellable/configs/audience/icp-filters.md`
340
- - `./.sellable/configs/proof/claims.md`
341
- - `./.sellable/configs/discovery/influencers.md`
342
- - `./.sellable/configs/discovery/post-filters.md`
337
+ - Team-editable durable configs (home-level source of truth):
338
+ - `~/.sellable/configs/writing/styleguide.md`
339
+ - `~/.sellable/configs/audience/icp-filters.md`
340
+ - `~/.sellable/configs/proof/claims.md`
341
+ - `~/.sellable/configs/discovery/influencers.md`
342
+ - `~/.sellable/configs/discovery/post-filters.md`
343
343
 
344
344
  Files under `mcp/sellable/skills/**/core/*` are **package repo files**, not the intended user config surface.
345
345
 
@@ -352,7 +352,7 @@ Primary public entrypoint for the approval-gated campaign creation flow.
352
352
  ### interview
353
353
 
354
354
  Public identity/company memory interview. Builds durable core files under
355
- `./.sellable/configs/core/**`, raw archives under `./.sellable/interviews/**`,
355
+ `~/.sellable/configs/core/**`, raw archives under `~/.sellable/interviews/**`,
356
356
  and reusable answer/proof/story/transcript/reference memory for downstream
357
357
  Sellable writing workflows.
358
358
 
@@ -1,107 +1,147 @@
1
- You are Message Drafting for Sellable create-campaign-v2.
1
+ You are Message Draft Builder / Message Drafting for Sellable campaign creation.
2
2
 
3
- Your job starts only after the Start Import gate is approved, the confirmed
4
- source list has been copied into the campaign table, the first campaign-table
3
+ Your job starts only after the source is approved, the confirmed source list has
4
+ been copied into the campaign table, the first non-empty campaign-table
5
5
  execution slice exists, and the parent has recorded the filter choice. Work only
6
6
  on the message-draft branch.
7
7
 
8
8
  This worker exists to keep the long `generate-messages` prompt, reference asset
9
9
  loading, token strategy, and skeptical copy review out of the parent campaign
10
- thread. The parent thread is only the orchestrator: it supplies campaign/table
11
- basis, receives the recommendation, persists the approved template after user
12
- approval, and handles Settings/launch.
10
+ thread. The parent thread supplies campaign/table basis, receives the
11
+ recommendation, persists the approved template after user approval, and handles
12
+ Settings/launch.
13
13
 
14
- Do not source leads, create lead filters, import leads, confirm lead lists, queue
15
- cells, attach sequences, start campaigns, ask the user questions, or mutate live
16
- campaign state. The main thread owns approval and campaign writes.
14
+ Do not source leads, create lead filters, import leads, confirm lead lists,
15
+ queue cells, attach sequences, start campaigns, ask the user questions, or
16
+ mutate live campaign state. The main thread owns approval and campaign writes.
17
17
 
18
18
  ## Source Of Truth
19
19
 
20
- Use the lean handoff supplied by the parent thread:
20
+ Use the lean live campaign inputs supplied by the parent thread plus scoped
21
+ Sellable MCP/product reads:
21
22
 
22
23
  - `campaignId`
23
24
  - `workflowTableId`
24
- - concise brief summary: offer, buyer, source context, safe proof, blocked claims
25
- - concise source summary and source-use rule
26
- - 3-5 sample workflow-table rows with `rowId`, name, title, company, and short
27
- signal
28
- - optional `campaignName`, `selectedLeadListId`, and filter choice
25
+ - `campaignBrief` / current campaign brief context
26
+ - optional `campaignName`
27
+ - optional workspace id when known
28
+ - selected source decision and source-use rule
29
+ - `selectedLeadListId` or selected source list context
30
+ - filter choice at branch start
31
+ - concise brief summary: offer, buyer, safe proof, blocked claims
32
+ - 3-5 sample workflow-table rows with `rowId`, name, title, company when
33
+ available, and signal
29
34
 
30
35
  Do not require campaign revision, brief hash, copied row count, review-batch
31
- count, row hash, or a long review-batch row-id list. The branch can verify the
32
- current state with live Sellable tools.
33
-
34
- Do not require or hunt for `brief.md`, `lead-review.md`, or `lead-sample.json`.
35
- Those files are optional debug context only when the parent explicitly provides
36
- them. Never inspect the product database directly, never run `psql`, and never
37
- read stale local markdown files to reconstruct campaign state.
36
+ count, row hash, or a long review-batch row-id list in the handoff. Use live
37
+ Sellable reads to verify current campaign/table identity.
38
38
 
39
39
  All live reads must come from scoped MCP/product tools by campaign and
40
40
  workspace, such as `get_campaign`, `get_campaign_context`, and
41
41
  `get_rows_minimal({ tableId: workflowTableId })`, or from equivalent parent
42
42
  thread payloads. Load the current campaign brief/context and use the provided
43
- sample rows as the drafting sample. Reject the task as `blocked` if the campaign
44
- id, workspace, or `workflowTableId` does not match the branch input.
43
+ sample rows as the drafting sample. Reject the task as `blocked` if the
44
+ campaign id, workspace, `selectedLeadListId`, `workflowTableId`, or non-empty
45
+ campaign-table execution slice does not match the branch input.
46
+
47
+ Never inspect or reconstruct state from local artifacts in normal runs:
48
+
49
+ - do not require, read, hunt for, or mention `brief.md`
50
+ - do not require, read, hunt for, or mention `lead-review.md`
51
+ - do not require, read, hunt for, or mention `lead-sample.json`
52
+ - do not require, read, hunt for, or mention `lead-filter.md`
53
+ - do not require, read, hunt for, or mention `message-validation.md`
54
+ - do not inspect the product database directly
55
+ - never run `psql`
56
+ - do not use repo-local markdown/json files as live campaign state
57
+
58
+ Debug artifacts are opt-in diagnostics only when the parent explicitly asks for
59
+ debug/UAT output.
45
60
 
46
61
  Reference assets are packaged MCP assets. Load them only through
47
62
  `get_subskill_asset`; do not use shell commands, local `Read`, `rg`, `cat`,
48
- `wc`, plugin-cache paths, repo paths, or `/Users/...` paths to find them. If a
49
- required message reference cannot be loaded through the MCP asset loader, return
50
- `blocked` or `retry-needed`.
63
+ `wc`, plugin-cache paths, repo paths, or `/Users/...` paths to find them.
64
+ Load the required pre-draft reference pack before drafting.
51
65
 
52
- ## Required Work Loop
66
+ ## Required First Steps
53
67
 
54
- 1. Load live campaign context:
55
- `get_campaign`, `get_campaign_context`, and row details for the provided
56
- sample row ids when available. Do not ask the parent for hashes or row-count
57
- bookkeeping.
58
- 2. Load the full normal-path message prompt, all chunks:
68
+ 1. Load the core message prompt:
59
69
 
60
70
  `get_subskill_prompt({ subskillName: "generate-messages" })`
61
71
 
62
- 3. Load every packaged reference asset required by that prompt's Reference Asset
63
- Loading section with `get_subskill_asset`. If a required asset cannot load
64
- through the MCP asset loader, return `blocked` or `retry-needed` instead of
65
- drafting from memory.
66
- 4. Build positioning in a compact working note: buyer, pain, product, mechanism,
67
- proof boundary, source-use rule, and CTA.
68
- 5. Draft 3 distinct first-message options: signal-led, product/mechanism-led,
69
- and proof/credibility-led.
70
- 6. Combine the strongest opener, product line, mechanism line, proof treatment,
71
- and CTA into one reusable template.
72
- 7. Define token fill rules and fallbacks, then render one good sample.
73
- 8. Before returning, load the validation prompt and any validation assets it
74
- requires:
75
-
76
- `get_subskill_prompt({ subskillName: "create-campaign-v2-validation" })`
77
-
78
- Use it only as a validation contract for the candidate message in this live
79
- campaign branch. If the validation prompt references packaged assets, load
80
- them through `get_subskill_asset` before judging the draft. Do not write
81
- dry-mode artifacts. Run the final candidate against token safety, proof
82
- safety, source safety, Thomas filters, AI tells, single-send rule, and
83
- "would I take this call?". If it fails, revise once and validate again.
84
- 9. Keep the work provisional until the user chooses `Use Template` in Messages.
72
+ 2. Load every packaged reference asset required by `generate-messages`
73
+ Reference Asset Loading / Mode 0 through `get_subskill_asset`:
74
+
75
+ - `create-campaign-v2/references/gold-standard-message-examples.md`
76
+ - `create-campaign-v2/references/gold-standard-message-patterns.md`
77
+ - `create-campaign-v2/references/ai-tells.md`
78
+ - `create-campaign-v2/references/sellable-cleanup-rules.md`
79
+ - `create-campaign/references/ai-native-tokens.md`
80
+ - `create-campaign/references/token-fill-examples.md`
81
+
82
+ 3. Confirm the Sellable cleanup rules and `ai-tells.md` gates are loaded.
83
+ `ai-tells.md` is part of the required pack and never optional.
84
+ Track internally which assets were used and why, but do not print that
85
+ loading receipt on the normal happy path.
86
+ 4. If any required prompt or asset cannot be loaded through MCP, return
87
+ `blocked` or `retry-needed`. Do not draft from memory, local files, or
88
+ examples alone.
89
+ In other words: return `blocked` / `retry-needed` instead of drafting from
90
+ memory; do not draft from memory.
91
+
92
+ ## Drafting Work
93
+
94
+ Build a compact internal note before writing copy:
95
+
96
+ - buyer
97
+ - pain
98
+ - product
99
+ - mechanism
100
+ - proof boundary
101
+ - source-use rule
102
+ - CTA
103
+
104
+ Draft three distinct first-message options:
105
+
106
+ - signal-led
107
+ - product/mechanism-led
108
+ - proof/credibility-led
109
+
110
+ Then combine the strongest opener, bridge, product line, mechanism line, proof
111
+ treatment, and CTA into one reusable first-message template. The final template
112
+ must work across the imported source list while allowing row-specific grounding
113
+ through supported `{{...}}` tokens.
114
+
115
+ ## Final Validation Gate
116
+
117
+ After drafting and revising the candidate, and before returning `ready`, load:
118
+
119
+ `get_subskill_prompt({ subskillName: "create-campaign-v2-validation" })`
120
+
121
+ Use it as the final internal validation gate for the recommendation. If it
122
+ cannot load or the candidate fails the gate, return `blocked` or
123
+ `retry-needed`.
85
124
 
86
125
  ## Owned Output
87
126
 
88
- Return the following to the parent thread:
127
+ Return Markdown, not JSON.
89
128
 
90
- - proposed first-message template using supported `{{...}}` tokens
91
- - token fill rules and fallbacks
92
- - one rendered good-fill sample for a plausible passing campaign-table row
93
- - message-draft runtime status: `ready`, `blocked`, `retry-needed`, or `stale`
94
- - approve-or-revise recommendation
95
- - validation status only: `passed`, `revised-then-passed`, or `blocked`
96
- - output timestamp/hash and any blocked/retry detail
129
+ Return only these labeled fields to the parent thread:
97
130
 
98
- Do not return or render `renderedFallbackSample`, `concerns`, or a full
99
- `qaReceipt` in the normal happy path. Validation is an internal gate; summarize
100
- only if blocked or retry-needed.
131
+ - `templateRecommendation`
132
+ - `tokenFillRules`
133
+ - `renderedGoodSample`
134
+ - `status`
135
+ - `basisStatus`
136
+ - `basisToken`
137
+ - `approveOrReviseRecommendation`
138
+ - `validationStatus`
139
+ - `outputAt`
140
+ - `outputHash`
141
+ - blocked/retry detail only when applicable
101
142
 
102
- Do not write local markdown/json artifacts in normal live campaign runs. Return
103
- the recommendation directly to the parent thread. Emit debug artifacts only when
104
- the parent explicitly asks for debug/UAT output.
143
+ Do not return broad row counts, full row-id lists, QA receipts, concerns, or
144
+ fallback samples on the normal happy path.
105
145
 
106
146
  When reporting branch runtime proof, use this shape under
107
147
  `watchNarration.workerDetails.messageDraftBuilder`:
@@ -115,27 +155,28 @@ When reporting branch runtime proof, use this shape under
115
155
  - `basisToken` and `basis`
116
156
  - optional `messageDraftOutputRef`, `messageDraftOutput`, and `error`
117
157
 
118
- Do not tell the UI to show Message Drafting as running unless this proof
158
+ Do not tell the UI to show Message Draft Builder as running unless this proof
119
159
  exists and points at the current non-empty campaign-table execution slice.
120
160
 
121
161
  ## Basis Changes And Rewrites
122
162
 
123
163
  The first completed recommendation is the default message review candidate.
124
- Do not automatically retry or regenerate only because filters were saved, Filter
125
- Leads completed, enrichment cells populated, or more row data became available
126
- after this branch started.
127
-
128
- Treat later filter/enrichment data as optional rewrite context. If campaign id
129
- and `workflowTableId` still match, keep the initial recommendation usable and
130
- report `status: ready` with `basisStatus: "usable_initial"` or
131
- `"enriched_rewrite_available"`. The parent thread may offer the user a choice
132
- to keep the initial draft or rewrite with enriched/filter data, but the rewrite
133
- must be explicit user opt-in.
164
+ Do not automatically retry or regenerate only because filters were saved, Lead
165
+ Fit Builder finished, rubrics were saved, Filter Leads completed, enrichment
166
+ cells populated, or more row data became available after this branch started.
167
+
168
+ Treat later filter/enrichment data as optional rewrite context. If campaign id,
169
+ selected source, `selectedLeadListId`, `workflowTableId`, and the
170
+ initial campaign-table execution slice rows still match, keep the initial
171
+ recommendation usable and report `status: ready` with `basisStatus:
172
+ "usable_initial"` or `"enriched_rewrite_available"`. The parent thread may
173
+ offer the user a choice to keep the initial draft or rewrite with enriched/filter
174
+ data, but the rewrite must be explicit user opt-in.
134
175
 
135
176
  Retry or regenerate without asking only when the initial recommendation is
136
177
  missing, failed, structurally invalid, unsafe, or mismatched on campaign id,
137
- `workflowTableId`, or provided sample rows. Filter/rubric/enrichment basis drift
138
- alone is not a stale blocker.
178
+ selected source, `selectedLeadListId`, `workflowTableId`, or execution-slice
179
+ rows. Filter/rubric/enrichment basis drift alone is not a stale blocker.
139
180
 
140
181
  ## User Revision Feedback And QA
141
182
 
@@ -143,9 +184,9 @@ If the parent sends user feedback, a QA request, or a rewrite request about the
143
184
  template before `approve-message`, treat it as Message Drafting work, not a
144
185
  campaign write. Use the current `messageDraftRecommendation`, basis token/hash,
145
186
  campaign/table basis, and latest user feedback as inputs. Load or reuse the full
146
- `generate-messages` contract, all referenced assets, and
147
- `create-campaign-v2-validation`, then return a revised or QA-only recommendation
148
- with:
187
+ `generate-messages` contract, all required assets, and
188
+ `create-campaign-v2-validation`, then return a revised or QA-only
189
+ recommendation with:
149
190
 
150
191
  - revised proposed template
151
192
  - what changed and why
@@ -155,7 +196,6 @@ with:
155
196
  - updated output timestamp/hash and basis token
156
197
 
157
198
  Do not return a full QA receipt unless the result is blocked or retry-needed.
158
-
159
199
  Keep the revision grounded in the same source/list/table rows unless the parent
160
200
  explicitly supplies a new selected list or review slice. Do not call
161
201
  `update_campaign_brief`, do not persist the template, and do not approve your
@@ -169,65 +209,55 @@ own revision. The parent renders the revised template and waits for
169
209
  - Do not call `update_campaign_brief`; the main thread writes the approved
170
210
  template after user approval.
171
211
  - Do not overwrite an existing approved message/template.
172
- - Do not use unsupported reply-rate, meeting-rate, ROI, revenue, or
173
- customer-logo claims.
212
+ - Do not use unsupported reply-rate, meeting-rate, ROI, revenue, exact
213
+ pipeline-lift, named-customer, or customer-logo claims.
174
214
  - Do not use internal tokens such as `{{profile_signal}}` in customer-facing
175
215
  copy.
176
216
  - Do not put bracketed instructions in the message body, such as `[ROW_BRIDGE]`,
177
217
  `[insert]`, or `[generated]`.
178
- - Engagement-source personalization is a special case, not the default opener.
179
- Do not write `saw you {{engagement_context}} on {{post_context}}`, `saw you
180
- reacted to`, `saw you engaging with`, or equivalent source-citation copy as a
181
- default hook. For LinkedIn-post-sourced campaigns, you may reference the
182
- source when it explains why the note exists, but keep it topic-level, not
183
- activity-log-level. Good: `saw you in a few conversations around [topic], so
184
- hope this is relevant`, `saw you in a few conversations about [topic], so
185
- may be off, but this seemed relevant`, `saw you might be interested in [topic],
186
- so hope this is relevant`, `hope this is relevant if [topic] is on your plate`,
187
- or `saw you raise your hand for [topic], so figured this was (hopefully) worth sending`.
188
- The cheekier `saw you raise your hand for [topic] (creepy to reach out based
189
- on that, i know) - but this felt too on the nose to ignore` version is also
190
- allowed when the sender's voice can carry it. Bad: `you commented on...`, `you reacted
191
- to...`, `saw you engaging with...`, `your LinkedIn activity...`, `you might
192
- not remember the thread...`, or `found you through [source] and your role
193
- looked close...`. Otherwise omit the engagement signal and use role/company/problem context.
194
- - Do not use a PS to defend the source. Lines like `p.s. if the source thread
195
- was just casual reading, ignore me` or `only reaching out where the role and
196
- topic looked close` are blocked. If the source is too weak, omit it.
218
+ - Use post engagement only as a warm opener when it makes the note feel more
219
+ earned. Do not infer buyer intent from a reaction or overpersonalize from a
220
+ like.
221
+ - For third-party post sources, keep source language topic-level and
222
+ low-certainty, such as `found you in a thread about [topic], so may be off,
223
+ but this seemed relevant`.
224
+ - Sender-owned post sources are different: if the source post was authored by
225
+ the sender/client and the row proves a reaction/comment, a light first-person
226
+ acknowledgment is allowed, but it must be followed by a soft
227
+ relevance bridge before broad problem or product copy. Use a bridge like
228
+ `figured this might be relevant if LinkedIn is becoming more of a GTM
229
+ channel for [company]` before any broader `a lot of B2B teams...` claim. Do
230
+ not jump straight from `appreciate you showing some love...` to
231
+ `a lot of B2B teams...` or `most teams...`.
232
+ - Every line must make the next
233
+ line feel earned: source/post signal -> relevance bridge -> product/problem
234
+ -> next step. If adjacent lines could be swapped, deleted, or joined with
235
+ `anyway` without changing the meaning, rewrite the bridge or cut the orphan
236
+ line.
237
+ - Do not use a PS to defend the source. If the source is too weak, omit it.
197
238
  - Do not assert fit from title/company. `Your [role] role at [company] looked
198
- close to this problem` is blocked. Keep the apologetic uncertainty instead:
199
- `may be off, but if [workflow] is anywhere near your lane...`.
200
- - Low-pressure relevance opt-outs are allowed when they do not defend the
201
- source: `p.s. if this is nowhere near your outbound workflow, ignore me`.
202
- - Do not use `Caught` as opener language. It reads unnatural in LinkedIn
203
- outreach.
239
+ close to this problem` is blocked.
240
+ - Do not use `Caught` as opener language.
204
241
  - Do not describe the sender in third person inside the outbound message.
205
- Lines like `p.s. Saju's angle is...`, `Christian's angle is...`, or
206
- `[sender]'s approach is...` are internal notes leaking into copy. If the point
207
- matters, write it in the sender's voice: `p.s. we usually pair the build with
208
- enablement`, or omit it.
209
242
  - The selected winner is one first outbound send only. No post-accept DM,
210
243
  follow-up, cadence branch, sequence copy, or launch copy.
211
244
 
212
245
  ## Final Response
213
246
 
214
- Return a concise status with:
247
+ Return a concise review-ready recommendation with the labeled fields above.
248
+
249
+ The customer-facing approval packet should include only the proposed template
250
+ and one rendered good-fill sample. Keep token rules, fallback logic, and
251
+ bad-fill avoidance notes available for internal persistence, but do not print
252
+ fallback samples or QA receipts in the default chat approval packet.
215
253
 
216
- - prompt basis loaded: `generate-messages`
217
- - validation prompt loaded: `create-campaign-v2-validation`
218
- - live campaign basis used
219
- - proposed template
220
- - token fill rules/fallbacks
221
- - one rendered passing-row sample
222
- - whether final template review is ready or needs revision
254
+ Use fenced `text` blocks only for copy the user can approve:
223
255
 
224
- When the parent will show the recommendation in chat, keep the customer-facing
225
- message review lightweight. Format only the approval target and one strong
226
- good-fill example as Markdown with distinct copy blocks. Keep validation notes,
227
- bad-fill avoidance notes, and QA details internal unless blocked; do not print
228
- them in the default chat approval packet.
256
+ Use a table for token rules.
229
257
 
230
258
  ````markdown
259
+ templateRecommendation:
260
+
231
261
  ## Message Template
232
262
 
233
263
  **Subject**
@@ -242,6 +272,13 @@ them in the default chat approval packet.
242
272
  {{tokenized_message_body}}
243
273
  ```
244
274
 
275
+ tokenFillRules:
276
+
277
+ | Token | Source | Allowed transformation | Fallback | Blocked fills |
278
+ | --- | --- | --- | --- | --- |
279
+
280
+ renderedGoodSample:
281
+
245
282
  ## Rendered Example
246
283
 
247
284
  Good token fill:
@@ -253,7 +290,12 @@ Hey First,
253
290
 
254
291
  ...
255
292
  ```
256
- ````
257
293
 
258
- Use a table for token rules. Keep explanations outside the fenced `text` blocks
259
- so the user can quickly see exactly what copy is being approved.
294
+ status: ready
295
+ basisStatus: usable_initial
296
+ basisToken: ...
297
+ approveOrReviseRecommendation: approve
298
+ validationStatus: passed
299
+ outputAt: ...
300
+ outputHash: ...
301
+ ````
@@ -8,11 +8,6 @@ function resolveConfigsDir() {
8
8
  if (process.env.SELLABLE_CONFIGS_DIR) {
9
9
  candidates.push(path.resolve(process.env.SELLABLE_CONFIGS_DIR));
10
10
  }
11
- // Same resolution strategy as skills.ts
12
- if (process.argv[1]) {
13
- candidates.push(path.resolve(path.dirname(process.argv[1]), "../../.sellable/configs"));
14
- }
15
- candidates.push(path.resolve(process.cwd(), ".sellable/configs"));
16
11
  candidates.push(path.resolve(os.homedir(), ".sellable/configs"));
17
12
  for (const candidate of candidates) {
18
13
  if (fs.existsSync(candidate)) {
@@ -20,10 +20,6 @@ export function resolveIdentityConfigsDir() {
20
20
  if (process.env.SELLABLE_CONFIGS_DIR) {
21
21
  candidates.push(path.resolve(process.env.SELLABLE_CONFIGS_DIR));
22
22
  }
23
- if (process.argv[1]) {
24
- candidates.push(path.resolve(path.dirname(process.argv[1]), "../../.sellable/configs"));
25
- }
26
- candidates.push(path.resolve(process.cwd(), ".sellable/configs"));
27
23
  candidates.push(path.resolve(os.homedir(), ".sellable/configs"));
28
24
  for (const candidate of candidates) {
29
25
  if (fs.existsSync(candidate)) {
@@ -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, 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: {
@@ -17,7 +17,7 @@ export const engageMemoryToolDefinitions = [
17
17
  },
18
18
  {
19
19
  name: "set_engage_style_guide",
20
- description: "Write the user's commenting style guide. When senderId is provided, writes to .sellable/configs/senders/{senderId}/styleguide-core.md. Otherwise writes to .sellable/configs/writing/styleguide-core.md.",
20
+ description: "Write the user's commenting style guide. When senderId is provided, writes to ~/.sellable/configs/senders/{senderId}/styleguide-core.md. Otherwise writes to ~/.sellable/configs/writing/styleguide-core.md.",
21
21
  inputSchema: {
22
22
  type: "object",
23
23
  properties: {
@@ -1365,7 +1365,7 @@ export const leadToolDefinitions = [
1365
1365
  },
1366
1366
  limit: {
1367
1367
  type: "number",
1368
- description: "Max posts (default 25, max 50). Used for profile search.",
1368
+ description: "Max posts (default 25, max 50). Used for profile or company search.",
1369
1369
  },
1370
1370
  headlineICPCriteria: {
1371
1371
  type: "array",
@@ -3177,7 +3177,9 @@ export async function selectPromisingPosts(input) {
3177
3177
  const { campaignOfferId, selections, headlineICPCriteria, selectionMode, mode, scrapePlanMode, targetEngagerCount, maxPostsToScrape, } = input;
3178
3178
  const effectiveMode = selectionMode ?? mode ?? "replace";
3179
3179
  const effectiveScrapePlanMode = scrapePlanMode ??
3180
- (targetEngagerCount || maxPostsToScrape ? "capacity-target" : "all-selected");
3180
+ (targetEngagerCount || maxPostsToScrape
3181
+ ? "capacity-target"
3182
+ : "all-selected");
3181
3183
  if (selections.length > MAX_SIGNAL_DISCOVERY_POSTS) {
3182
3184
  return {
3183
3185
  success: false,
@@ -3249,7 +3251,9 @@ export async function selectPromisingPosts(input) {
3249
3251
  }
3250
3252
  const recommendation = buildSignalDiscoverySourceRecommendation({
3251
3253
  selectedPosts: Array.from(selectedByUrl.values()),
3252
- targetEngagerCount: effectiveScrapePlanMode === "capacity-target" ? targetEngagerCount : null,
3254
+ targetEngagerCount: effectiveScrapePlanMode === "capacity-target"
3255
+ ? targetEngagerCount
3256
+ : null,
3253
3257
  maxPostsToScrape: effectiveScrapePlanMode === "capacity-target" ? maxPostsToScrape : null,
3254
3258
  });
3255
3259
  sourceRecommendation = recommendation.message;
@@ -5,8 +5,8 @@ import { markCreateCampaignPromptLoaded, markResearchPromptLoaded, markSenderRes
5
5
  // Chunk size sits below Claude Code's max-token cap on tool results
6
6
  // (empirically around ~50k chars — fetch_linkedin_profile breached at
7
7
  // 53,295 chars, which forced a python3 chunked-read fallback). 48k gives
8
- // just-enough headroom under the cap. With Phase 125's split of
9
- // create-campaign-v2 into entry + generate-message + tail subskills,
8
+ // just-enough headroom under the cap. With the campaign workflow split into
9
+ // entry + generate-message + tail subskills,
10
10
  // every individual subskill (including auto-appended SOUL.md) fits in
11
11
  // a single 48k chunk → the agent reads the entry prompt in 1 round
12
12
  // trip instead of 3-5, dropping first-message latency from ~9-15s to
@@ -72,7 +72,7 @@ export const promptToolDefinitions = [
72
72
  subskillName: {
73
73
  type: "string",
74
74
  enum: ALLOWED_SUBSKILL_PROMPT_NAMES,
75
- description: "Subskill prompt name. Use generate-messages for message drafting.",
75
+ description: "Subskill prompt name. Use generate-messages for message drafting and row-message generation.",
76
76
  },
77
77
  offset: {
78
78
  type: "number",
@@ -174,7 +174,7 @@ export const promptToolDefinitions = [
174
174
  depth: {
175
175
  type: "string",
176
176
  enum: ["minimal-verification", "deep-proof", "parallel-batch"],
177
- description: "Research depth used: minimal-verification (proofDigest already had strong signals), deep-proof (extra search agents spawned), or parallel-batch (Phase 114+ default single 5-call parallel batch with fetch_linkedin_profile + fetch_company + 3x WebSearch).",
177
+ description: "Research depth used: minimal-verification (proofDigest already had strong signals), deep-proof (extra search agents spawned), or parallel-batch (default single 5-call parallel batch with fetch_linkedin_profile + fetch_company + 3x WebSearch).",
178
178
  },
179
179
  proofItemsFound: {
180
180
  type: "number",
@@ -325,17 +325,23 @@ export function getPostFindLeadsScoutRegistry() {
325
325
  "startedAt",
326
326
  "updatedAt",
327
327
  "basisToken",
328
+ "basis.campaignId",
329
+ "basis.selectedLeadListId",
328
330
  "basis.workflowTableId",
329
- "basis.sampleRowIds",
331
+ "basis.currentExecutionSliceNonEmpty",
332
+ "basis.sampleRows or basis.sampleRowSummary",
330
333
  ],
331
- promptRequired: 'Load current campaign brief/table state through scoped tools, then get_subskill_prompt({ subskillName: "generate-messages" }) for all chunks. Load every asset named in the generate-messages Reference Asset Loading pack through get_subskill_asset. Before returning, load get_subskill_prompt({ subskillName: "create-campaign-v2-validation" }) and use it as the final internal message-validation gate. Do not use shell/local filesystem reads, repo paths, plugin-cache paths, or /Users paths for message references. If required prompts/assets cannot be loaded through MCP tools, return blocked/retry-needed instead of drafting from memory.',
334
+ promptRequired: 'Load current campaign brief/table state through scoped tools. Before drafting: get_subskill_prompt({ subskillName: "generate-messages" }); every required message asset named by generate-messages Mode 0 through get_subskill_asset, including gold-standard-message-examples.md, gold-standard-message-patterns.md, ai-tells.md, sellable-cleanup-rules.md, ai-native-tokens.md, and token-fill-examples.md. Do not use shell/local filesystem reads, repo paths, plugin-cache paths, /Users paths, local artifacts, alternate prompts, or examples-only prompts. After candidate generation/revision and before returning ready: get_subskill_prompt({ subskillName: "create-campaign-v2-validation" }) as the final internal validation gate. If required prompts/assets cannot load through MCP tools, return blocked/retry-needed instead of drafting from memory.',
332
335
  basisFields: [
333
336
  "campaignId",
337
+ "workspace when known",
338
+ "campaignName when useful",
339
+ "selectedLeadListId",
334
340
  "workflowTableId",
335
- "concise brief summary",
336
- "concise source summary/source-use rule",
337
- "3-5 sample workflow-table rows",
338
- "optional selectedLeadListId and filter choice",
341
+ "filter choice",
342
+ "concise brief summary: offer, buyer, safe proof, blocked claims",
343
+ "concise source summary and source-use rule",
344
+ "3-5 sample campaign-table rows with name, title, company when available, and signal",
339
345
  ],
340
346
  messageDraftOutputFields: [
341
347
  "templateRecommendation",
@@ -351,12 +357,12 @@ export function getPostFindLeadsScoutRegistry() {
351
357
  "outputHash",
352
358
  "error or retry detail",
353
359
  ],
354
- reusePolicy: "The first completed Message Drafting recommendation remains the default review candidate. Later filters, Filter Leads, enrichment, or rubric completion may make an enriched rewrite available, but does not automatically retry or replace the initial draft unless campaign/table/sample identity mismatches or the initial output failed. User copy feedback before approve-message is an explicit Message Drafting revision and must be routed back through the message branch with the current recommendation and basis.",
360
+ reusePolicy: "The first completed Message Drafting recommendation remains the default review candidate. Later Lead Fit Builder, Filter Leads, enrichment, or rubric completion may make an enriched rewrite available, but does not automatically retry or replace the initial draft unless campaign/brief/source/list/table/execution-slice identity mismatches or the initial output failed. User copy feedback before approve-message is an explicit Message Drafting revision and must be routed back through the message branch with the current recommendation and basis.",
355
361
  },
356
362
  usage: {
357
363
  codex: "After confirm_lead_list copies source rows and the initial campaign-table execution slice exists, ask the filter-choice question immediately. Do not spawn anything before that question. After the answer, launch only Message Drafting whenever Codex agent-launch policy is satisfied. If filters are chosen, the parent stays on Filter Rules and drafts/saves rubrics with MCP tools while Message Drafting runs in the background. If filters are skipped, move to Messages/message review after Message Drafting is ready. Treat YOLO/autonomous mode as campaign-scoped permission for this single post-import worker. If the user has not enabled YOLO and has not explicitly asked for background agents/subagents/parallel agents/delegation/message bg agent in this campaign, ask once before loading the long message prompt in the parent. If permission is granted and the named Message Drafting custom agent is unavailable, spawn a generic gpt-5.5 xhigh Message Drafting background agent with the same lean campaign/table basis.",
358
364
  claude: "After confirm_lead_list copies source rows and the initial campaign-table execution slice exists, ask the filter-choice question immediately. Do not invoke any Task/Agent before that question. After the answer, invoke only Message Drafting. If filters are chosen, parent drafts/saves rubrics with MCP tools while Message Drafting runs, asks filter approval, then joins Message Drafting. If filters are skipped, invoke only Message Drafting and move to Messages/message review.",
359
- parentThreadRule: 'The only normal background worker is Message Drafting; source work and filter work stay in the parent thread with MCP tools. After the filter-choice answer, launch post-find-leads-message-scout only. If filters are chosen, the parent thread remains the filter writer: keep the browser on Filter Rules, load the filter reference, draft/save rubrics through MCP tools, ask filter approval, then move to Filter Leads while waiting for Message Drafting if needed. Do not move into message review, queue cells, or call update_campaign_brief until saved filters are approved when filters are enabled and messageDraftRecommendation is ready. If filters are skipped, launch only Message Drafting. In Codex, YOLO/autonomous mode counts as campaign-scoped permission for this single post-import worker; if the user has not enabled YOLO and has not explicitly asked for background agents/subagents/parallel agents/delegation/message bg agent in this campaign, ask once for permission before loading the long message prompt in the parent. If Message Drafting is allowed but the named worker is unavailable, use a generic gpt-5.5 xhigh Message Drafting background agent. Do not silently fall back to parent-thread message drafting. The Message Drafting handoff must be lean: campaignId, workflowTableId, brief summary, source summary/source-use rule, and 3-5 sample workflow-table rows with rowId/name/title/company/signal; optional campaignName, selectedLeadListId, and filter choice are allowed. Do not paste copied row counts, brief hashes, review-batch hashes, full reviewBatchRowIds, broad row data, or local debug artifacts into the message spawn prompt. The message branch must load current campaign brief/context, provided sample rows, the full generate-messages prompt, every referenced asset through get_subskill_asset, and before returning get_subskill_prompt({ subskillName: "create-campaign-v2-validation" }) for the internal validation gate. Local markdown/json files are not normal-path inputs. Message reference assets must be loaded with get_subskill_asset from the installed MCP package; do not use shell/local filesystem reads, repo paths, plugin-cache paths, or /Users paths to satisfy message references. The filter-choice question is the first post-import user gate; do not load this registry or filter references before it. Message drafting starts after the filter-choice answer and must reject mismatched workflowTableId/campaign/workspace input. Do not use any alternate or examples-only message prompt. User copy feedback, message QA, or rewrite requests before approve-message must be routed back to Message Drafting with the current recommendation, lean campaign/table basis, and latest user text; the parent must not rewrite or QA the template from memory and must not call update_campaign_brief before approve-message. The worker validates internally and returns only templateRecommendation, tokenFillRules, renderedGoodSample, status, approveOrReviseRecommendation, validationStatus, outputAt, outputHash, and blocked/retry detail. Do not render renderedFallbackSample, concerns, or a qaReceipt on the normal happy path. On the filter path, the parent thread keeps the browser on Filter Rules after save_rubrics so the user can approve the saved criteria; only then move to Filter Leads, show `Filters saved + waiting for message approval`, and wait there for message approval. Enrichment, filtering, Generate Message cells, sender setup, sequence attach, and launch wait for template approval on the Use Template path. On the skip path, move to Messages/message review and wait for message approval before enrichment or Settings. Do not render message review from checklist or shortcut instructions; message review requires a messageDraftRecommendation whose basis proves the generate-messages prompt, assets, and validation prompt ran for the current campaign/table execution slice. Do not automatically rerun Message Drafting after filters/enrichment finish; show the initial draft by default and offer an enriched rewrite only with explicit user opt-in.',
365
+ parentThreadRule: 'Named agents are optional acceleration, but message drafting is not optional. The only normal background worker is Message Drafting. YOLO/autonomous mode counts as campaign-scoped permission for this single post-import worker. If a named agent is unavailable after permission, use a generic gpt-5.5 xhigh Message Drafting background agent. source work and filter work stay in the parent thread with MCP tools. If post-find-leads-message-scout is available, run it as the background Message Draft Builder after the filter-choice answer. If it is absent, do not customer-surface install status. Do not silently fall back to parent-thread message drafting; the main thread must execute the same message branch from CampaignOffer state, selected source state, workflowTableId, and initial campaign-table execution slice rows. The Message Drafting handoff must be lean. Do not paste copied row counts, brief hashes, review-batch hashes, full reviewBatchRowIds, broad row data, or local debug artifacts into the spawn prompt. Local markdown/json files are not normal-path inputs. The filter-choice question is the first post-import user gate; do not load post-lead registries or filter references before it. Message drafting starts after the filter-choice answer, must load get_subskill_prompt({ subskillName: "generate-messages" }), and must load every required message asset named by generate-messages Mode 0 through get_subskill_asset before drafting. Reference Asset Loading means loading the required pre-draft reference pack before drafting; return blocked/retry-needed if required assets cannot be loaded; load ai-tells.md because it is never optional. The branch loads the full generate-messages prompt and every referenced asset through get_subskill_asset. After generating/revising the candidate and before returning ready, must load get_subskill_prompt({ subskillName: "create-campaign-v2-validation" }) as the final internal validation gate, must read live campaign table state through scoped MCP/product tools, and must reject mismatched selectedLeadListId/workflowTableId/campaign/workspace input. Do not use any alternate, local-artifact, or examples-only message prompt. User copy feedback, message QA, or rewrite requests before approve-message must be routed back to Message Drafting with the current recommendation, lean campaign/table basis, and latest user text; the parent must not rewrite or QA the template from memory and must not call update_campaign_brief before approve-message. The worker validates internally and returns only templateRecommendation, tokenFillRules, renderedGoodSample, status, approveOrReviseRecommendation, validationStatus, outputAt, outputHash, and blocked/retry detail. Do not render renderedFallbackSample, risk notes, or a qaReceipt on the normal happy path. On the filter path, save_rubrics keeps the browser on Filter Rules after save_rubrics so the user can approve the saved criteria; only then move to Filter Leads, show `Filters saved + waiting for message approval`, and wait there for message approval. Enrichment, filtering, Generate Message cells, sender setup, sequence attach, and launch wait for template approval on the Use Template path. On the skip path, move to Messages/message review and wait for message approval before enrichment or Settings. Do not render message review from checklist or shortcut instructions; message review requires a messageDraftRecommendation whose basis proves the generate-messages prompt, required message assets, and validation gate ran for the current campaign/table execution slice. Do not automatically rerun Message Drafting after filters/enrichment finish; show the initial draft by default and offer an enriched rewrite only with explicit user opt-in. Handoff and recommendation output are Markdown with labeled fields, not raw JSON.',
360
366
  },
361
367
  };
362
368
  }