@sellable/mcp 0.1.0

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 (200) hide show
  1. package/.claude-plugin/plugin.json +12 -0
  2. package/.mcp.json +9 -0
  3. package/README.md +355 -0
  4. package/dist/api.d.ts +21 -0
  5. package/dist/api.js +73 -0
  6. package/dist/auth.d.ts +60 -0
  7. package/dist/auth.js +246 -0
  8. package/dist/engage-memory.d.ts +63 -0
  9. package/dist/engage-memory.js +354 -0
  10. package/dist/index-dev.d.ts +2 -0
  11. package/dist/index-dev.js +17 -0
  12. package/dist/index.d.ts +7 -0
  13. package/dist/index.js +8 -0
  14. package/dist/server.d.ts +1 -0
  15. package/dist/server.js +499 -0
  16. package/dist/skills.d.ts +11 -0
  17. package/dist/skills.js +97 -0
  18. package/dist/tools/auth.d.ts +30 -0
  19. package/dist/tools/auth.js +124 -0
  20. package/dist/tools/blueprint-commit.d.ts +174 -0
  21. package/dist/tools/blueprint-commit.js +286 -0
  22. package/dist/tools/bootstrap.d.ts +64 -0
  23. package/dist/tools/bootstrap.js +246 -0
  24. package/dist/tools/campaigns.d.ts +589 -0
  25. package/dist/tools/campaigns.js +892 -0
  26. package/dist/tools/cells.d.ts +58 -0
  27. package/dist/tools/cells.js +48 -0
  28. package/dist/tools/context.d.ts +88 -0
  29. package/dist/tools/context.js +271 -0
  30. package/dist/tools/csv-domains.d.ts +73 -0
  31. package/dist/tools/csv-domains.js +464 -0
  32. package/dist/tools/csv-linkedin.d.ts +102 -0
  33. package/dist/tools/csv-linkedin.js +712 -0
  34. package/dist/tools/direct-campaigns.d.ts +240 -0
  35. package/dist/tools/direct-campaigns.js +250 -0
  36. package/dist/tools/engage-bootstrap.d.ts +94 -0
  37. package/dist/tools/engage-bootstrap.js +205 -0
  38. package/dist/tools/engage-discovery.d.ts +78 -0
  39. package/dist/tools/engage-discovery.js +150 -0
  40. package/dist/tools/engage-memory.d.ts +181 -0
  41. package/dist/tools/engage-memory.js +143 -0
  42. package/dist/tools/engage-state.d.ts +72 -0
  43. package/dist/tools/engage-state.js +62 -0
  44. package/dist/tools/enrichment.d.ts +167 -0
  45. package/dist/tools/enrichment.js +174 -0
  46. package/dist/tools/flow-preflight.d.ts +68 -0
  47. package/dist/tools/flow-preflight.js +138 -0
  48. package/dist/tools/framework.d.ts +44 -0
  49. package/dist/tools/framework.js +153 -0
  50. package/dist/tools/interaction-mode.d.ts +27 -0
  51. package/dist/tools/interaction-mode.js +102 -0
  52. package/dist/tools/leads.d.ts +2417 -0
  53. package/dist/tools/leads.js +2307 -0
  54. package/dist/tools/linkedin.d.ts +210 -0
  55. package/dist/tools/linkedin.js +229 -0
  56. package/dist/tools/navigation.d.ts +91 -0
  57. package/dist/tools/navigation.js +381 -0
  58. package/dist/tools/one-off.d.ts +229 -0
  59. package/dist/tools/one-off.js +273 -0
  60. package/dist/tools/processing.d.ts +70 -0
  61. package/dist/tools/processing.js +56 -0
  62. package/dist/tools/prompts.d.ts +211 -0
  63. package/dist/tools/prompts.js +210 -0
  64. package/dist/tools/provider-preflight.d.ts +21 -0
  65. package/dist/tools/provider-preflight.js +59 -0
  66. package/dist/tools/readiness.d.ts +261 -0
  67. package/dist/tools/readiness.js +510 -0
  68. package/dist/tools/rows.d.ts +126 -0
  69. package/dist/tools/rows.js +105 -0
  70. package/dist/tools/rubrics.d.ts +497 -0
  71. package/dist/tools/rubrics.js +681 -0
  72. package/dist/tools/senders.d.ts +44 -0
  73. package/dist/tools/senders.js +69 -0
  74. package/dist/tools/sequencer.d.ts +127 -0
  75. package/dist/tools/sequencer.js +194 -0
  76. package/dist/tools/tables.d.ts +35 -0
  77. package/dist/tools/tables.js +36 -0
  78. package/dist/tools/verify-row.d.ts +36 -0
  79. package/dist/tools/verify-row.js +38 -0
  80. package/dist/tools/workspaces.d.ts +140 -0
  81. package/dist/tools/workspaces.js +139 -0
  82. package/dist/utils/workspace-root.d.ts +1 -0
  83. package/dist/utils/workspace-root.js +39 -0
  84. package/package.json +46 -0
  85. package/skills/building-gtm-tables/SKILL.md +216 -0
  86. package/skills/building-gtm-tables/core/auto-execute.yaml +19 -0
  87. package/skills/building-gtm-tables/core/blueprint-schema.json +72 -0
  88. package/skills/building-gtm-tables/references/brief-to-blueprint.md +334 -0
  89. package/skills/building-gtm-tables/references/column-type-catalog.md +318 -0
  90. package/skills/building-gtm-tables/references/common-blueprints.fixtures.ts +199 -0
  91. package/skills/building-gtm-tables/references/common-blueprints.md +44 -0
  92. package/skills/building-gtm-tables/references/failure-taxonomy.md +197 -0
  93. package/skills/building-gtm-tables/references/uat-seed-prompts.md +37 -0
  94. package/skills/building-gtm-tables/references/verify-loop.md +74 -0
  95. package/skills/campaign-messages/SKILL.md +173 -0
  96. package/skills/campaign-messages/flow.v1.json +75 -0
  97. package/skills/craft-message/SKILL.md +401 -0
  98. package/skills/create-campaign/ARCHITECTURE.md +232 -0
  99. package/skills/create-campaign/DISCUSS.md +296 -0
  100. package/skills/create-campaign/FLOW_ASCII.md +240 -0
  101. package/skills/create-campaign/HOST-PARITY-CHECKLIST.md +49 -0
  102. package/skills/create-campaign/README.md +142 -0
  103. package/skills/create-campaign/SKILL.md +286 -0
  104. package/skills/create-campaign/context/README.md +67 -0
  105. package/skills/create-campaign/context/_TEMPLATE.md +12 -0
  106. package/skills/create-campaign/context/context.md +35 -0
  107. package/skills/create-campaign/context/learnings.md +16 -0
  108. package/skills/create-campaign/context/registry.json +19 -0
  109. package/skills/create-campaign/core/flow.v1.json +217 -0
  110. package/skills/create-campaign/core/policy.md +191 -0
  111. package/skills/create-campaign/core/providers/apollo.json +35 -0
  112. package/skills/create-campaign/core/providers/prospeo.json +34 -0
  113. package/skills/create-campaign/core/providers/registry.json +31 -0
  114. package/skills/create-campaign/core/providers/sales-nav.json +37 -0
  115. package/skills/create-campaign/core/providers/signal-discovery.json +42 -0
  116. package/skills/create-campaign/references/brief-template.md +64 -0
  117. package/skills/create-campaign/references/campaign-quality.md +84 -0
  118. package/skills/create-campaign/references/copy-calibration-examples.md +120 -0
  119. package/skills/create-campaign/references/offer-patterns.md +108 -0
  120. package/skills/create-campaign/references/provider-selection-strategy.md +212 -0
  121. package/skills/create-campaign/references/question-examples.md +167 -0
  122. package/skills/create-campaign/references/token-fill-examples.md +81 -0
  123. package/skills/create-campaign-brief/ARCHITECTURE.md +72 -0
  124. package/skills/create-campaign-brief/DISCUSS.md +64 -0
  125. package/skills/create-campaign-brief/README.md +176 -0
  126. package/skills/create-campaign-brief/SKILL.md +537 -0
  127. package/skills/create-campaign-brief/references/brief-synthesis-rules.md +100 -0
  128. package/skills/create-campaign-brief/references/brief-template.md +220 -0
  129. package/skills/create-campaign-brief/references/campaign-idea-options.md +30 -0
  130. package/skills/create-campaign-brief/references/copy-appendix-template.md +62 -0
  131. package/skills/create-campaign-brief/references/draft-lifecycle.md +23 -0
  132. package/skills/create-campaign-brief/references/examples/MANIFEST.json +89 -0
  133. package/skills/create-campaign-brief/references/examples/briefs/clover.md +223 -0
  134. package/skills/create-campaign-brief/references/examples/briefs/galley.md +222 -0
  135. package/skills/create-campaign-brief/references/examples/briefs/gelee.md +220 -0
  136. package/skills/create-campaign-brief/references/examples/briefs/hey-digital.md +234 -0
  137. package/skills/create-campaign-brief/references/examples/briefs/persona.md +231 -0
  138. package/skills/create-campaign-brief/references/examples/briefs/revvix.md +220 -0
  139. package/skills/create-campaign-brief/references/examples/briefs/sellable-dev.md +220 -0
  140. package/skills/create-campaign-brief/references/examples/briefs/superposition.md +233 -0
  141. package/skills/create-campaign-brief/references/examples/briefs/superpower.md +219 -0
  142. package/skills/create-campaign-brief/references/examples/briefs/westpark-villas.md +220 -0
  143. package/skills/create-campaign-brief/references/icp-lock-question-bank.md +43 -0
  144. package/skills/create-campaign-brief/references/messaging-inputs.md +58 -0
  145. package/skills/create-campaign-brief/references/output-acceptance-rubric.md +62 -0
  146. package/skills/create-campaign-brief/references/phase75-active-runtime-message-pack.md +248 -0
  147. package/skills/create-campaign-brief/references/phase75-canonical-brief-template.md +319 -0
  148. package/skills/create-campaign-brief/references/phase75-good-brief-and-messaging-examples.md +445 -0
  149. package/skills/create-campaign-brief/references/quick-research-protocol.md +39 -0
  150. package/skills/create-campaign-brief/references/reference-sheet-protocol.md +60 -0
  151. package/skills/create-campaign-brief/references/zero-shot-iteration-rules.md +66 -0
  152. package/skills/create-campaign-v2/SKILL.md +1619 -0
  153. package/skills/create-campaign-v2/core/auto-execute.README.md +219 -0
  154. package/skills/create-campaign-v2/core/auto-execute.yaml +121 -0
  155. package/skills/create-campaign-v2/core/flow.v2.json +1643 -0
  156. package/skills/create-campaign-v2/core/policy.md +82 -0
  157. package/skills/create-campaign-v2/references/ai-tells.md +253 -0
  158. package/skills/create-campaign-v2/references/approval-gate-framing.md +346 -0
  159. package/skills/create-campaign-v2/references/draft-lifecycle.md +110 -0
  160. package/skills/create-campaign-v2/references/escalation-ladder.md +119 -0
  161. package/skills/create-campaign-v2/references/filter-leads.md +495 -0
  162. package/skills/create-campaign-v2/references/final-handoff-contract.md +176 -0
  163. package/skills/create-campaign-v2/references/gold-standard-message-examples.md +394 -0
  164. package/skills/create-campaign-v2/references/gold-standard-message-patterns.md +314 -0
  165. package/skills/create-campaign-v2/references/gold-standard-message-validation-example.md +212 -0
  166. package/skills/create-campaign-v2/references/lead-validation-preview.md +172 -0
  167. package/skills/create-campaign-v2/references/parallel-critique-protocol.md +368 -0
  168. package/skills/create-campaign-v2/references/sample-validation-loop.md +289 -0
  169. package/skills/create-campaign-v2/references/step-13-import-leads.md +151 -0
  170. package/skills/create-campaign-v2/references/step-15-re-cascade.md +90 -0
  171. package/skills/create-campaign-v2/references/thomas-revision-filters.md +521 -0
  172. package/skills/create-campaign-v2/references/thomas-variant-selection.md +202 -0
  173. package/skills/create-campaign-v2/references/tier-routing-matrix.md +66 -0
  174. package/skills/create-campaign-v2/references/validation-criteria.md +367 -0
  175. package/skills/create-campaign-v2/references/watch-link-handoff.md +106 -0
  176. package/skills/create-campaign-v2-validation/SKILL.md +296 -0
  177. package/skills/create-post/SKILL.md +1308 -0
  178. package/skills/create-rubric/SKILL.md +251 -0
  179. package/skills/engage/SKILL.md +549 -0
  180. package/skills/engage/core/README.md +23 -0
  181. package/skills/engage/core/proven-searches.json +11 -0
  182. package/skills/engage/core/style-guide.template.md +47 -0
  183. package/skills/engage/core/tracked-people.json +10 -0
  184. package/skills/enrich-prospects/SKILL.md +97 -0
  185. package/skills/find-leads/SKILL.md +467 -0
  186. package/skills/generate-messages/SKILL.md +2361 -0
  187. package/skills/interview/SKILL.md +132 -0
  188. package/skills/interview/core/ENGAGE_STYLE_GUIDE.template.md +54 -0
  189. package/skills/interview/core/ICP.template.md +54 -0
  190. package/skills/interview/core/VOICE_PROFILE.template.md +101 -0
  191. package/skills/providers/apollo.md +520 -0
  192. package/skills/providers/prospeo.md +398 -0
  193. package/skills/providers/sales-nav.md +372 -0
  194. package/skills/providers/signal-discovery.md +495 -0
  195. package/skills/research/SKILL.md +258 -0
  196. package/skills/research/config.json +9 -0
  197. package/skills/research/override.md +13 -0
  198. package/skills/research-prospect/SKILL.md +99 -0
  199. package/skills/research-sender/SKILL.md +158 -0
  200. package/skills/workflow-sequences/SKILL.md +85 -0
@@ -0,0 +1,495 @@
1
+ # Filter Leads
2
+
3
+ The middle step is a lightweight lane audit and decision gate. It should add
4
+ fidelity to the find-leads estimate, not re-run sourcing or produce a large
5
+ scoring framework.
6
+
7
+ The business goal is not "find people who might reply." It is "every reply the
8
+ user gets should come from someone who is in the ICP and has a realistic path
9
+ to buying." A lead who may reply but cannot buy, lacks budget, sits at the wrong
10
+ account type, is a vendor/competitor, or is on the wrong side of a marketplace
11
+ is not a good lead.
12
+
13
+ Inputs:
14
+
15
+ - `brief.md`
16
+ - `lead-review.md`
17
+ - `lead-sample.json`
18
+
19
+ Outputs:
20
+
21
+ - `lead-filter.md`
22
+ - optional `rubric.json`
23
+
24
+ Core constraint:
25
+
26
+ - Every accepted filter in `lead-filter.md` must be directly translatable into
27
+ the production `LeadScoringRubric` shape. If a filter cannot become a
28
+ runnable rubric criterion from available row data or public research, it is
29
+ not an accepted filter; convert it to an optional enrichment note, a user
30
+ tradeoff, or a lead-source revision reason.
31
+
32
+ ## Evaluation Data Scope
33
+
34
+ Do not write final filters that depend on the limited fields visible in the
35
+ source-provider preview card. Sales Nav cards, Prospeo rows, and Signals rows
36
+ are sampling evidence, not the full data contract.
37
+
38
+ Production rubrics should describe what we will decide after normal enrichment
39
+ and public research have run. For example:
40
+
41
+ - Good: "Keep US-licensed MD/DO dermatologists; verify credentials from the
42
+ enriched LinkedIn profile, professional bio, NPI/state-license page, or other
43
+ public source."
44
+ - Bad: "Exclude rows missing MD/DO on the Sales Nav card."
45
+
46
+ If a preview row lacks enough evidence to prove a required rule, mark it as
47
+ `needs enrichment` or `borderline pending verification` in sample findings. Do
48
+ not turn preview-card missingness into a hard production exclusion unless the
49
+ same missing field will remain missing after enrichment.
50
+
51
+ ## Primary Goal
52
+
53
+ Use the actual sample to answer:
54
+
55
+ - was the Step 2 projected-good-fit estimate basically right or wrong
56
+ - which leads clearly match the buyer hypothesis
57
+ - which leads are noise
58
+ - which repeated false positives must become explicit exclusions
59
+ - whether filters are enough, or whether the source lane would make the
60
+ campaign miss if launched as-is
61
+ - whether the filtered leads are economically worth sending to, using the best
62
+ campaign-native proxy available from row data, enrichment, or Googleable facts
63
+
64
+ ## Standard Competitor / Vendor Exclusion
65
+
66
+ Every confirmed filter should include a competitor / vendor / wrong-side
67
+ exclusion unless the brief and sample make it clearly irrelevant. This is a
68
+ standard production safety rule, not an optional nice-to-have.
69
+
70
+ Use this simple test:
71
+
72
+ - Would this company, product, service provider, agency, consultant, reseller,
73
+ or marketplace participant appear on the same comparison page as the client?
74
+ - Would they compete for the same customer budget, sell into the same buyer
75
+ workflow, or sit on the wrong side of the marketplace?
76
+ - Did the sample show adjacent companies that a client would likely reject only
77
+ after seeing the list?
78
+
79
+ If yes, add an explicit exclude rule. Include named competitors from the brief,
80
+ sample, or user context when available, plus the general category exclusion so
81
+ new lookalike competitors are also blocked. If a competitor/vendor exclusion is
82
+ omitted, `lead-filter.md` must say why it is not applicable.
83
+
84
+ Treat this as part of the manual review loop. When the user or client sees a
85
+ sample and says "these companies are not a fit," update the exclusion watchlist
86
+ and filter draft before Step 4. Use `confirm-with-user` when the sample contains
87
+ ambiguous adjacent vendors, competitors, agencies, marketplace-side conflicts,
88
+ or reseller/consultant roles where excluding them would materially shrink the
89
+ lane.
90
+
91
+ ## `lead-filter.md` Shape
92
+
93
+ ````md
94
+ # Lead Filter
95
+
96
+ Status: confirmed | confirm-with-user | revise-find-leads
97
+
98
+ ## Decision
99
+
100
+ - continue | confirm with user | revise find-leads
101
+ - one plain-English sentence saying why
102
+
103
+ ## Who We'll Keep
104
+
105
+ - ...
106
+
107
+ ## Who We'll Exclude
108
+
109
+ - ...
110
+
111
+ ## Sample False Positives
112
+
113
+ - ...
114
+
115
+ ## Optional Supporting Rule
116
+
117
+ - omit this section unless exactly one supporting rule materially helps messaging or prioritization
118
+
119
+ ## Pass Rate
120
+
121
+ - ...
122
+
123
+ ## Recommendation
124
+
125
+ - proceed to generate message
126
+ - or revise leads / revise filter
127
+
128
+ ## Implementation Details
129
+
130
+ If `Status: confirmed`, include production rubrics:
131
+
132
+ ```json
133
+ {
134
+ "leadScoringRubrics": [
135
+ {
136
+ "checkName": "role_fit",
137
+ "description": "Lead holds one of the validated buyer titles.",
138
+ "criterion": "Yes if the current title matches one of the accepted buyer titles. No if the title is a wrong-function, junior, advisory-only, or service-provider title.",
139
+ "reason": "The campaign only works when the recipient owns the buyer problem and can act on the offer.",
140
+ "isRequiredCheck": true,
141
+ "allowPartialCredit": false,
142
+ "strictMatching": false
143
+ },
144
+ {
145
+ "checkName": "company_fit",
146
+ "description": "Lead works at a company type validated by the sample.",
147
+ "criterion": "Yes if the current company matches the accepted account type from the brief and sample. No if the company is a vendor, agency, competitor, or structurally wrong account type.",
148
+ "reason": "The message thesis depends on this company context being true.",
149
+ "isRequiredCheck": true,
150
+ "allowPartialCredit": false,
151
+ "strictMatching": false
152
+ }
153
+ ]
154
+ }
155
+ ```
156
+
157
+ If `Status: confirm-with-user` or `Status: revise-find-leads`, do not include
158
+ production rubrics yet. Explain why the draft filter is not locked downstream.
159
+
160
+ ## Required Keep Rules
161
+
162
+ - technical source rules, if different from the plain-English keep list
163
+
164
+ ## Required Exclude Rules
165
+
166
+ - technical source rules, if different from the plain-English exclude list
167
+ ````
168
+
169
+ Rules:
170
+
171
+ - `Decision` is mandatory and must appear before filter details
172
+ - default to `Status: confirmed` when the source lane is viable and filters are
173
+ enough to protect the campaign
174
+ - use `confirm-with-user` only when the lane is viable but there is a real
175
+ strategic tradeoff the user must choose before launch
176
+ - use `revise-find-leads` only when filters would be so heavy, low-volume, or
177
+ thesis-distorting that launching from this lane would likely miss
178
+ - always emit a readable draft filter, even when status is `confirm-with-user`
179
+ or `revise-find-leads`, so the user can inspect and edit the AI's definition
180
+ of a good fit
181
+ - `Who We'll Keep` and `Who We'll Exclude` are mandatory user-facing sections
182
+ - `Required Keep Rules` and `Required Exclude Rules` are allowed as technical
183
+ source sections, but should appear after `Implementation Details` if present
184
+ - `Sample False Positives` must be grounded in observed sample leads
185
+ - `Optional Supporting Rule` is allowed only when one rule clearly helps later
186
+ messaging or prioritization
187
+ - never emit more than one optional/non-required rule
188
+ - `Implementation Details` is mandatory for every status
189
+ - `Implementation Details` must contain one fenced JSON object with
190
+ `leadScoringRubrics` only when `Status: confirmed`
191
+ - non-confirmed filters are draft/user-review artifacts, not official Step 4
192
+ inputs
193
+ - `leadScoringRubrics` must contain 2-5 production-shaped rubric items, not one
194
+ item per bullet
195
+ - do not put source-path mechanics or send-priority hints into
196
+ `leadScoringRubrics`. Examples: recently posted on LinkedIn, came from a
197
+ Signals search, appeared in Sales Nav, was found by Prospeo, first-send wave,
198
+ or priority cohort. If that signal was already used in the search, mention it
199
+ in `Pass Rate` or `Recommendation`; if it is useful but not required, keep it
200
+ as prose under `Optional Supporting Rule`, not as a production rubric.
201
+ - do not put DNC or one-off relationship-safety notes into
202
+ `leadScoringRubrics` unless the sample shows that family is likely to leak at
203
+ meaningful volume and normal DNC/domain suppression will not catch it. Former
204
+ employers, existing customers, investors, partner lists, and "do not contact"
205
+ domains usually belong in `Recommendation` or a DNC note, not in ICP scoring.
206
+ - confirmed `leadScoringRubrics` must protect sales quality, not just surface
207
+ similarity. They should usually cover:
208
+ - buyer authority / role fit
209
+ - account or company-type fit
210
+ - economic capacity / ability to buy, using a campaign-native proxy such as
211
+ headcount, revenue, funding, practice size, location count, patient volume,
212
+ department maturity, existing tooling, or budget-owner title
213
+ - geography / market, when relevant
214
+ - active-current-role safety
215
+ - hard exclusions for competitors, vendors, agencies, job seekers,
216
+ wrong-function leads, and wrong-side marketplace participants
217
+ - confirmed `leadScoringRubrics` should include a competitor / vendor /
218
+ wrong-side exclusion by default. It can be bundled with account-fit only when
219
+ the criterion still names both the accepted account type and the excluded
220
+ competitor/vendor/wrong-side categories.
221
+ - when one production rubric bundles multiple families, the criterion must say
222
+ exactly which buyer-quality gates it covers
223
+ - if the filter cannot be compiled into 2-5 production rubric items, do not
224
+ confirm; return `confirm-with-user` or `revise-find-leads`
225
+ - hide raw rubric flags from the top user-facing sections; keep
226
+ `isRequiredCheck`, `allowPartialCredit`, and `strictMatching` inside
227
+ `Implementation Details`
228
+
229
+ ## Rule Families To Preserve
230
+
231
+ When relevant to the brief and sample, preserve these recurring families:
232
+
233
+ - buyer role / seniority
234
+ - wrong-function exclusions
235
+ - company-type exclusions
236
+ - competitor / vendor / intermediary exclusions
237
+ - geography
238
+ - company size
239
+ - active current-role status
240
+
241
+ ## Sample-Judging Method
242
+
243
+ For each proposed rule:
244
+
245
+ 1. inspect matching and failing sample leads
246
+ 2. decide whether the rule removes real noise or blocks real buyers
247
+ 3. mark the rule as necessary, loosen, or remove
248
+ 4. record the resulting pass rate
249
+
250
+ Repeated false positives should become explicit exclude rules whenever the same
251
+ pattern appears more than once in the sample.
252
+
253
+ ## `rubric.json` Sidecar
254
+
255
+ Only emit a sidecar when a downstream step needs a separate file. The same
256
+ production-shaped items must always appear as fenced JSON in `lead-filter.md`
257
+ under `## Implementation Details`, even when `rubric.json` is omitted.
258
+
259
+ Shape:
260
+
261
+ ```json
262
+ {
263
+ "leadScoringRubrics": [
264
+ {
265
+ "checkName": "role_fit",
266
+ "description": "Lead holds one of the validated buyer titles.",
267
+ "criterion": "Yes if the current title matches one of the accepted buyer titles. No if the title is a wrong-function, junior, advisory-only, or service-provider title.",
268
+ "reason": "The campaign only works when the recipient owns the buyer problem and can act on the offer.",
269
+ "isRequiredCheck": true,
270
+ "allowPartialCredit": false,
271
+ "strictMatching": false
272
+ }
273
+ ]
274
+ }
275
+ ```
276
+
277
+ Rules:
278
+
279
+ - derive every sidecar rule from `lead-filter.md`
280
+ - default every rule to `isRequiredCheck: true`
281
+ - allow at most one `isRequiredCheck: false` rule
282
+ - do not add a rule that is not present in `lead-filter.md`
283
+ - do not duplicate two rules that restate the same keep/exclude idea
284
+ - use the production fields exactly: `checkName`, `description`, `criterion`,
285
+ `reason`, `isRequiredCheck`, `allowPartialCredit`, and `strictMatching`
286
+ - prefer 2-5 rubric criteria total; do not map every keep/exclude bullet into
287
+ its own scoring row
288
+ - production rubric criteria should be buyer-quality gates, not execution
289
+ metadata. Do not create rubric rows for provider source, recently-posted
290
+ priority, first-send ordering, or other search mechanics unless the user
291
+ explicitly asks to score that as part of ICP fit.
292
+ - production rubric criteria should not become one-off DNC lists. Named-company
293
+ exclusions are appropriate when they represent a repeated false-positive
294
+ category, a true competitor class, or a material lane risk; otherwise preserve
295
+ them as DNC/suppression instructions outside the rubric JSON.
296
+ - do not write "these rules are expressible as rubrics" as a substitute for the
297
+ actual 2-5 item draft
298
+ - bundle repeated false positives into a single exclusion criterion when they
299
+ are the same kind of failure, for example vendors / competitors /
300
+ wrong-side providers
301
+ - keep title / role fit as its own explicit criterion with concrete pass and
302
+ fail title patterns
303
+ - use `allowPartialCredit: false` for hard gates where ambiguity should fail
304
+ - use `strictMatching: true` only when the rule depends on exact named-account
305
+ or exact-token matching
306
+
307
+ Recommended rubric grouping:
308
+
309
+ - buyer authority / role fit
310
+ - company / industry / provider-lane fit
311
+ - economic capacity / ability to buy
312
+ - geography / size / stage fit, when relevant
313
+ - competitor / vendor / wrong-side / repeated false-positive exclusion
314
+ - one optional buyer-quality signal, at most, only when it changes fit scoring
315
+ rather than send ordering
316
+
317
+ Rubric-readiness checklist:
318
+
319
+ - Can this rule be evaluated from `lead-sample.json`, provider row fields,
320
+ enrichment, or normal public research?
321
+ - Does the criterion say both what passes and what fails?
322
+ - Is the reason a business rationale, not a restatement of the criterion?
323
+ - Would `save_rubrics` accept this without adding missing fields?
324
+ - Would `evaluate-icp-fit-enriched` have enough prospect-side data or
325
+ Googleable facts to score it?
326
+
327
+ ## Prohibited During Phase 84
328
+
329
+ - calling `check_rubric`
330
+ - calling `save_rubrics`
331
+ - using campaign-backed rubric tools
332
+ - creating a second independent rubric design
333
+ - blocking Phase 84 when only `rubric.json` fails and `lead-filter.md` is valid
334
+
335
+ ## Marketplace safety rail (two-sided archetypes)
336
+
337
+ When brief §14 declares a marketplace with an explicit forbidden
338
+ side-of-market (e.g. Skillsync forbids cold-outbound to supply-side
339
+ GitHub contributors; the indexed pool is NEVER the outbound target),
340
+ the lead-filter MUST include a forbidden-role exclude rule naming the
341
+ supply-side title family.
342
+
343
+ ### Enforcement at filter-write time
344
+
345
+ - `lead-filter.md` Required Exclude Rules MUST contain at least one
346
+ rule pattern-matching the forbidden side's title family (e.g.
347
+ "exclude 'Software Engineer' / 'Staff Engineer' / GitHub
348
+ contributor profile signal for Skillsync-like marketplaces").
349
+ - If brief §14 declares the marketplace but the filter does NOT include
350
+ the forbidden-side exclude rule, surface this as a `revise-rubric`
351
+ precondition failure. Do NOT proceed to the commit gate.
352
+
353
+ ### Enforcement at validate-sample time
354
+
355
+ - The Plan 85-02 validate-sample loop treats `>20% of sample rows
356
+ matching the brief §14 forbidden side` as a **LIST problem** (not a
357
+ brief problem). Rationale: the sourced list is contaminated with
358
+ the wrong side of the marketplace; operator must re-source.
359
+ - Detection signal: row title/profile matches the supply-side family
360
+ declared in brief §14, even if the title whitelist at §5 ICP
361
+ accidentally allows it.
362
+
363
+ ### Why this matters
364
+
365
+ Marketplaces are the only B2B archetype where the same LinkedIn pool
366
+ contains both the intended buyer and the forbidden counterparty. A
367
+ marketplace operator who accidentally drafts InMail to a supply-side
368
+ contributor has simultaneously:
369
+
370
+ 1. Violated their own TOS with that contributor (cold InMail for a
371
+ non-applied-to "hiring" pitch).
372
+ 2. Leaked their own product positioning (we target engineers,
373
+ implying we sell you as a vendor).
374
+ 3. Poisoned the cohort's recall rate for real demand-side buyers.
375
+
376
+ A filter-spec safety rail catches the drift BEFORE Step 13 import.
377
+ A validate-sample signal catches the drift if it still slips through.
378
+
379
+ Plan 95-07 surfaced this residual on the Skillsync walkthrough; Plan
380
+ 95-08 ships the rail.
381
+
382
+ ## Post-Filter Signal Enrichment Pass (Sales Nav lane)
383
+
384
+ After `lead-filter.md` lands and the Pass Rate is recorded, run a bounded
385
+ signal-enrichment pass on FIT rows whose primary lane is Sales Nav and
386
+ whose `lead-sample.json` entry has `recent_posts.length === 0` (the
387
+ Sales Nav lane does not carry post data natively).
388
+
389
+ This pass directly addresses the Earned-right + Read-as-1:1 substance
390
+ filters in Phase 84. Without row-level post data, the generator has
391
+ nothing to quote back, forcing either (a) category-level openers that
392
+ trip Read-as-1:1, or (b) presumption-led openers that trip Earned-right.
393
+
394
+ Contract:
395
+
396
+ - **Scope:** only FIT rows (or the top K rows when FIT count > K).
397
+ - **Budget cap K:** top 10 FIT rows by score. Do NOT enrich MAYBE or
398
+ REJECT rows in Phase 84 — they are not headed to messaging.
399
+ - **Tool:** `mcp__sellable__fetch_linkedin_posts` (per-row call). If the
400
+ tool is unavailable in the current harness, skip the pass entirely
401
+ and document in Findings.
402
+ - **Fetch:** the most recent 3-5 posts per row.
403
+ - **Append to `lead-sample.json`:** each enriched row gets a
404
+ `recent_posts[]` array with `{ url, posted_at, excerpt }` entries
405
+ (200 char excerpt cap per post).
406
+ - **Rows with zero public posts:** keep `recent_posts: []`. This is
407
+ valid data — the absence signals "quiet-profile row, use trigger or
408
+ peer-observation angle instead".
409
+ - **Order of operations:** run AFTER `lead-filter.md` is written, BEFORE
410
+ `message-validation.md` starts drafting. The enriched
411
+ `lead-sample.json` is the input to the per-row signal extraction pass
412
+ in the generator.
413
+
414
+ Findings update:
415
+
416
+ - Record enrichment outcome at the bottom of `lead-filter.md` under a
417
+ new `## Signal Enrichment` heading: rows attempted, rows with posts,
418
+ rows with zero posts, tool failures.
419
+ - If more than 50% of enriched rows have zero posts, flag the sample as
420
+ "low signal density" and recommend the generator's peer-observation
421
+ fallback angle with the Findings-surface warning.
422
+
423
+ This pass does NOT apply to:
424
+
425
+ - Signals-lane rows (already carry post data natively)
426
+ - Prospeo-lane rows (messaging is usually event/job-post-led, post
427
+ data is secondary)
428
+ - live-campaign mode (enrichment happens per-row during Research)
429
+
430
+ ## Brief-Validated Rewrite Authority
431
+
432
+ When `lead-review.md` shows that the Phase-83 primary lead-source picked an
433
+ UNVALIDATED lane (no source-material support) AND the alternative source
434
+ yields ≥ 5× FIT leads in the same sample, `brief-validated.md` may rewrite
435
+ exactly two Phase-83 brief sections:
436
+
437
+ - §3 Campaign Thesis — update the source-hypothesis line to reflect the
438
+ higher-yielding lane as primary.
439
+ - §12 Next Steps — update the first lead-search step to start on the
440
+ higher-yielding lane.
441
+
442
+ Every rewrite must include an evidence citation block quoting
443
+ `lead-review.md`:
444
+
445
+ ```md
446
+ > Evidence (from lead-review.md):
447
+ >
448
+ > - Signals sample: 2 rows, 0 FIT
449
+ > - Sales Nav sample: 25 rows, 3 FIT + 7 MAYBE
450
+ > - Primary-lane reversal: Sales Nav ≥ 5× Signals FIT yield in the same
451
+ > preview.
452
+ ```
453
+
454
+ All OTHER Phase-83 brief sections remain read-only during Phase-84. This
455
+ authority is narrowly scoped to one specific failure mode — UNVALIDATED
456
+ primary-source picks contradicted by real preview data. It is NOT a general
457
+ brief-rewrite license. Do not use it to rewrite ICP, Buyer Pain, Offer
458
+ Strategy, Social Proof, Differentiators, Message Thesis, or any other
459
+ section. Those deltas continue to flow through `lead-filter.md` and
460
+ `message-validation.md` as today.
461
+
462
+ If the evidence is ambiguous (e.g. 2 FIT Signals vs 4 FIT Sales Nav — under
463
+ 5× ratio), DO NOT rewrite. Flag the weak preference in `lead-filter.md`
464
+ § Recommendation instead.
465
+
466
+ ## Provider-lane expected FIT density
467
+
468
+ Observed during Phase 94 + Phase 95 UAT. Use as anchor for judging whether
469
+ a lane is "lane exhausted" (see `references/step-13-import-leads.md`) or
470
+ genuinely weak.
471
+
472
+ | Lane | Typical FIT density | Archetype examples |
473
+ | ------------------------------------------ | ------------------- | ------------------------------------------------------------ |
474
+ | **Prospeo** (vertical SMB) | 60-80% | Patientdesk (dental practice owners); vertical B2C-style SMB |
475
+ | **Sales Nav** (horizontal enterprise) | 40-55% | Ambral (enterprise CS); Skillsync (eng-led companies) |
476
+ | **Sales Nav** (dev tools / specific infra) | 30-45% | IncidentFox (SRE/Platform leaders at Series B-D SaaS) |
477
+ | **Signals** (overlay — warm-scored) | 20-35% | Overlay added to any lane; rate depends on topical density |
478
+
479
+ Interpretation:
480
+
481
+ - **Density at the high end of the band** — fresh lane; import cap should
482
+ be aggressive (bump importLimit from 100 default to 200+ if needed).
483
+ - **Density below the band** — either the filter is too tight OR the lane
484
+ is approaching exhaustion (most good rows already imported in prior runs).
485
+ Check dedup ratios to distinguish.
486
+ - **Density dramatically below band** — likely a brief problem (ICP too
487
+ narrow) or a provider-side issue. Diagnose before reruning.
488
+
489
+ Plan 95-05 Patientdesk observed ~70% FIT density on Prospeo (top of band).
490
+ Plan 95-03 Ambral observed ~47% on Sales Nav horizontal enterprise (mid band).
491
+ Plan 95-01 IncidentFox observed ~36% on Sales Nav dev-infra (mid-low band).
492
+ Plan 94-07 Skillsync observed ~32% on Signals (mid band for overlay).
493
+
494
+ Use these as regression anchors: a 10%+ drop from the archetype's
495
+ historical density on a lane = investigate before escalating to operator.
@@ -0,0 +1,176 @@
1
+ # Final Handoff Contract
2
+
3
+ This reference governs Step 16 (`awaiting-user-greenlight`) of the Plan
4
+ 85-02 autonomous tail — the moment where the skill stops running
5
+ autonomously and waits for the user to decide whether to start the
6
+ campaign. Load this file before entering Step 16 and on every resume into
7
+ `awaiting-user-greenlight`.
8
+
9
+ ## Principle
10
+
11
+ The autonomous tail NEVER calls `start_campaign` on its own. The final
12
+ start is always a user decision. There are two equally valid channels for
13
+ that decision — UI and Claude greenlight — and the skill must handle both
14
+ cleanly, including the case where one channel has already started the
15
+ campaign before the other arrives.
16
+
17
+ ## Step 16 Setup
18
+
19
+ On entry to `awaiting-user-greenlight`, the skill does the following in
20
+ order (all before waiting for the user):
21
+
22
+ 1. Attach the sequence to the campaign (`attach_sequence`).
23
+ 2. Re-surface the `watchUrl` using the exact block in
24
+ `references/watch-link-handoff.md`.
25
+ 3. Surface the `handoff.orientation` string from `auto-execute.yaml`
26
+ alongside the watch link so the user sees both the link and the
27
+ "what to do next" sentence.
28
+ 4. STOP. Do not call `start_campaign`. Do not advance `currentStep`.
29
+
30
+ The final handoff must answer five customer questions in plain language:
31
+
32
+ - where to inspect the campaign (`watchUrl`)
33
+ - what is ready now (brief, filters/rubrics, review-batch leads, messages, sequence)
34
+ - what clicking Start does (approves/starts the campaign send path)
35
+ - how to revise (reply with what to change, or use the campaign UI)
36
+ - whether anything has started yet (nothing sends until the user starts)
37
+
38
+ When `lead-source-intake.json` exists, also answer:
39
+
40
+ - which source was materialized (`supplied-linkedin-profiles`,
41
+ `supplied-domains`, or `existing-lead-list`)
42
+ - how many rows/accounts were supplied
43
+ - how many rows were imported into the first review batch
44
+ - that only the first review batch was enriched and messaged before
45
+ greenlight, not the full supplied list
46
+ - for `existing-lead-list`, that the source list was reused, not recreated
47
+
48
+ Use the exact state name `awaiting-user-greenlight` so logs and UX artifacts
49
+ can verify the stop point, but explain it in normal words: the campaign is
50
+ built and waiting for the user's final start decision.
51
+
52
+ The skill waits. It does not poll. A fresh continuation turn arrives
53
+ either because the user said something in chat (Claude greenlight path)
54
+ or because `get_campaign` / polling determined the user clicked Start in
55
+ the UI.
56
+
57
+ ## Dual Greenlight Channels
58
+
59
+ ### UI path
60
+
61
+ The user reviews messages in the watch link, bulk-approves messages in
62
+ the workflow table UI (the existing "Approve all" button that wraps
63
+ `POST /api/v3/workflow-tables/cells/approve-batch`), and clicks "Start
64
+ Campaign" in the campaign-builder UI.
65
+
66
+ No skill involvement. The skill remains idle in
67
+ `awaiting-user-greenlight`. On the next resume, the skill observes
68
+ `CampaignOffer.status === "running"` (or equivalent) and shifts to a
69
+ "campaign is live" confirmation without calling `start_campaign` again.
70
+
71
+ ### Claude greenlight path
72
+
73
+ The user replies in chat with an affirmative intent: "yeah start",
74
+ "looks good, start", "ship it", "go". The skill then performs, in
75
+ order, atomically:
76
+
77
+ 1. **Bulk-approve queued messages** via the existing endpoint
78
+ `POST /api/v3/workflow-tables/cells/approve-batch` with
79
+ `{ tableId }`.
80
+ - This is the SAME server action the UI "Approve all" button uses
81
+ (`src/components/outbound-builder/canvas/WorkflowTablesCanvas.tsx`
82
+ calls `/api/v3/workflow-tables/cells/approve-batch`).
83
+ - Do NOT invent a new tool or endpoint. Do NOT bypass this call by
84
+ directly updating cells — the endpoint handles downstream
85
+ scheduling via `CellOrchestrator.reprocessColumn`.
86
+ - If no MCP tool wrapper for approve-batch is available, the skill
87
+ invokes it via the same authenticated API client path that other
88
+ v3 routes use.
89
+ 2. **`start_campaign({ campaignId })`** — flips the campaign into the
90
+ running state. If this call fails, surface the error; do NOT leave
91
+ the approvals in an "approved but not started" half-state silently
92
+ — either retry or escalate with the explicit state.
93
+ 3. **`update_campaign({ campaignId, currentStep: "running" })`** — moves
94
+ the skill past `awaiting-user-greenlight` so resume lands in a
95
+ "campaign live" confirmation state.
96
+ 4. **Re-surface the watch link** plus a short "campaign is live"
97
+ confirmation sentence.
98
+
99
+ All three mutating steps (bulk-approve, start_campaign, update_campaign)
100
+ must succeed together. If step 1 succeeds but step 2 fails, the skill
101
+ surfaces the recoverable state and stops — it does NOT attempt to
102
+ unapprove (that would be destructive) and it does NOT silently retry
103
+ forever.
104
+
105
+ ## Already-Running No-Op
106
+
107
+ A Claude greenlight on an already-running campaign is a no-op
108
+ confirmation, NOT a duplicate start. Detect this at the top of the
109
+ greenlight path:
110
+
111
+ 1. Call `get_campaign({ campaignId })` (or whatever lightweight state
112
+ fetch is available).
113
+ 2. If the campaign is already running (status === "running" /
114
+ `currentStep === "running"`), skip all three mutating steps and
115
+ return a confirmation message: "Campaign is already live — here's
116
+ the watch link again."
117
+ 3. Do NOT call `start_campaign` again. Do NOT call approve-batch
118
+ again. Do NOT update `currentStep`.
119
+
120
+ The symmetric UI case is handled by the UI itself — the UI button
121
+ disables once the campaign is running.
122
+
123
+ ## Workspace + Sender Invariant
124
+
125
+ Before calling `start_campaign` via the Claude greenlight path,
126
+ re-validate the workspace/sender invariants Phase 84 committed against.
127
+ If the current auth context no longer matches (workspace switched mid
128
+ session, sender removed, etc.), abort the start with the same invariant
129
+ error Phase 84 uses. Do NOT start a campaign from the wrong workspace.
130
+
131
+ ## Pre-Start State Guards
132
+
133
+ Refuse the start and surface the reason if any of the following are
134
+ true:
135
+
136
+ - Campaign is in a failed/paused state that needs operator attention
137
+ (e.g. upstream lead import failed post-mint and never recovered).
138
+ - Workspace mismatch between skill context and campaign row.
139
+ - Sender is missing or disabled.
140
+ - `attach_sequence` never succeeded (no sequence bound to the
141
+ campaign).
142
+
143
+ A refusal is NOT an escalation in the ladder sense — it's a pre-check
144
+ failure. Surface the specific reason so the user can fix it and say
145
+ "start" again.
146
+
147
+ ## Atomicity Contract
148
+
149
+ "Atomic" here means: from the user's perspective, saying "yeah start"
150
+ either produces a running campaign with approved messages, OR produces
151
+ an explicit error state with NO silent half-state. It does NOT mean a
152
+ database transaction — approve-batch and start_campaign are separate
153
+ calls — it means the skill treats the trio as a single unit and does
154
+ not leave the user guessing which step half-succeeded.
155
+
156
+ If approve-batch partially succeeds (e.g. `approved: 50, failed: 3`),
157
+ the skill surfaces the partial result and asks the user whether to
158
+ proceed with start_campaign anyway or investigate the 3 failures. It
159
+ does NOT silently proceed.
160
+
161
+ ## Hard Rules
162
+
163
+ - The autonomous tail NEVER calls `start_campaign` on its own.
164
+ - Step 16 NEVER auto-advances past `awaiting-user-greenlight` without a
165
+ user signal.
166
+ - Bulk-approve in the Claude greenlight path uses the EXISTING
167
+ `POST /api/v3/workflow-tables/cells/approve-batch` endpoint. Do NOT
168
+ invent a new tool / endpoint / mutation path.
169
+ - A Claude greenlight on an already-running campaign is a no-op
170
+ confirmation, not a duplicate start.
171
+ - Workspace/sender mismatch at greenlight time aborts the start with
172
+ the Phase 84 invariant.
173
+ - A pre-check refusal is NOT an escalation — it's a fixable state. The
174
+ skill stays in `awaiting-user-greenlight`.
175
+ - Never print the watch link in a half-started state (approved but not
176
+ started, or started but `currentStep` not updated).