@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,521 @@
1
+ # Thomas Revision Filters
2
+
3
+ Use these filters before Phase 84 message validation is marked `confirmed`.
4
+
5
+ Read "What Good Looks Like" first. These are the positive targets. Use
6
+ "Automatic Revisions" as the rejection backstop, not the primary lens.
7
+
8
+ ## What Good Looks Like
9
+
10
+ **Voice and reader experience**
11
+
12
+ - reads like a sharp human operator, not a mail-merge template
13
+ - sentences sound typed, not assembled — varied length, plain words, no
14
+ framework seams
15
+ - a busy buyer could skim it in under ten seconds and get the idea
16
+ - the tone matches the Primary Example's reading level (casual-lowercase if
17
+ Superpower, dense-credentialed if Superposition — don't flatten)
18
+
19
+ **Relevance and grounding**
20
+
21
+ - specific to the validated lead and signal; removing the personalization
22
+ would make the message feel clearly off for this buyer
23
+ - every proof claim traces back to something the brief actually supports
24
+ - every token traces back to a declared source field
25
+
26
+ **Motion alignment**
27
+
28
+ - matches the quality bar of one archived motion, not a blend of two
29
+ - opens with the strongest concrete hook the brief supports (event > signal >
30
+ job > workflow observation)
31
+ - proof sits where the motion expects it (body line for event/signal, stacked
32
+ body for job-post, short `PS` for stack-replacement / proof-led fallback)
33
+ - the CTA is useful and proportional to buyer intent — not a naked meeting
34
+ ask, not a fabricated asset
35
+
36
+ **Craft**
37
+
38
+ - one observation or one pain in the opener, not both stacked
39
+ - 3 short body paragraphs max, **one sentence per line** (literal — two sentences on one line is a revision)
40
+ - sentences at or below ~15 words, or split into shorter lines when longer
41
+ - plain English — a 5th-grade reader should follow the whole message without stopping
42
+ - jargon is limited to brief-native product and tool names (e.g. Shopify, HubSpot, Salesforce, ServiceNow) — not glue language, which is any B2B compound noun the buyer wouldn't say naturally in conversation
43
+ - mechanism said once, directly (product-name-first when the product can carry the sentence)
44
+ - if the mechanism touches three workflows, pick the most painful one or break them into short lines
45
+ - PS reads like one human telling another why the sender gets their pain — at most two proof beats, connected into a sentence or two, first names preferred
46
+ - the sample set reads like siblings from one campaign, not three experiments
47
+ - the Finalizer Pass result is better than any single candidate, or one candidate is named the sweep and the winning axis is declared
48
+
49
+ ## Automatic Revisions
50
+
51
+ Revise or reject the sample when any of these happen.
52
+
53
+ **Token and proof safety**
54
+
55
+ - unresolved `{{token}}`
56
+ - unsupported token not documented in the brief
57
+ - invented metric, logo, customer, funding detail, or product claim
58
+ - personalization that cannot be traced to `lead-sample.json`
59
+ - the message has no compelling proof even though the brief supports one
60
+ - stronger safe proof exists in the brief but the draft settles for weaker
61
+ generic credibility
62
+
63
+ **Voice and inbox readability**
64
+
65
+ - generic praise, compliment-sandwich openers, or filler flattery
66
+ - weak observational hooks that could fit almost anyone
67
+ - copy that sounds like a template with the company name swapped in
68
+ - **sentences above ~15 words, or with more than one comma, when they could be split**
69
+ - **two or more sentences on the same line with no blank line between them** — default is one sentence per paragraph, blank line between every sentence in the body
70
+ - **body renders as multi-sentence paragraphs** instead of one-sentence paragraphs separated by blank lines
71
+ - **a single mechanism line that stacks three workflows together** as a comma list (e.g. "order intake, inventory sync, and reorder timing"). Give each workflow its own one-sentence paragraph, or pick the most painful one and drop the others
72
+ - **product clarity failure**: a cold reader cannot state what the product does in one sentence after reading. The mechanism gestures at "that chain", "the stack", or "that work" without a crisp `Product is an X that does Y` anchor sentence and/or without naming specific actions
73
+ - **missing "what the product IS" anchor** — the action breakdown appears without a one-sentence statement of what the product is. The reader needs to know the product category before the actions make sense
74
+ - **actions are implied, not stated** — e.g. "runs that chain as AI agents" when a clearer version would name the specific actions (verb + object, one per line)
75
+ - **category-level opener used when a per-lead signal exists** — if `lead-sample.json` carries any per-lead signal (post, hire, visible tool, topic engagement), the opener must reference it. Category-level openers of shape `"Most [category] teams still do X by hand"` are only acceptable when zero per-lead signal is in the sample. When a category-level opener is used as fallback, Findings must flag it explicitly
76
+ - **mind-reading from engagement signals** — a topic engagement, post, public activity, role, company, or hiring trigger does not prove buyer intent. Reject phrases like `"AI-GTM stack is clearly on your mind"`, `"you're clearly focused on..."`, `"obviously relevant"`, or `"already thinking about..."` unless that exact priority is explicitly present in `lead-sample.json`. Translate to low-certainty buyer context or omit the signal from copy.
77
+ - **source-y signal narration** — reject `"saw you on..."`, `"saw you around..."`, `"saw you engaging with..."`, `"your LinkedIn activity..."`, or any line that makes the recipient feel watched unless the chosen archived motion is intentionally self-aware about the signal. Translate the signal into a natural buyer-context line or omit it.
78
+ - **internal-metric flex** — reject compute time, token/cache details, model names, agent-counts, orchestration internals, or similar process metrics unless the brief proves the buyer cares about that exact detail. `~5 min of compute per message` is not buyer value by itself.
79
+ - **action lines are parallel (same subject, e.g. `It X` / `It Y` / `It Z`) but rendered as one-line paragraphs instead of bullets** — parallel action lines are the bullet case. One-line paragraphs are only for actions that carry a short narrative clause
80
+ - **PS carries a second proof beat that doesn't answer a different objection** — the second beat must open a dimension beat #1 didn't address (e.g. technical reliability when beat #1 was operator empathy; named backing when beat #1 was founder track record). Default PS is ONE beat
81
+ - **subject line uses the same banned glue jargon as the body** — any B2B compound noun the buyer wouldn't say naturally in conversation. Subject follows the same jargon rules as the body
82
+ - **jargon that a 5th-grade reader could not parse** — any B2B compound noun the buyer wouldn't say naturally in conversation. Common examples across motions: "handoff", "rip-and-replace", "pane of glass", "single source of truth", "alert fatigue", "top of funnel", "time to hire". Keep product and tool names that the buyer recognizes; cut the glue language around them
83
+ - em dashes in copy that is otherwise plainspoken
84
+ - repeated metaphor framing makes the sample set feel machine-generated
85
+ - the note reads above a 5th-grade reading level when the motion does not require technical jargon
86
+
87
+ **PS shape and social proof**
88
+
89
+ - **the PS lands as a resume list** (three credential sentences in a row, separated only by periods). Rewrite as a thin narrative of at most two proof beats
90
+ - **proof beats are stacked with no connective tissue** — e.g. "CEO X did A. CTO Y did B. Backer Z." No human writes proof like that in an inbox note
91
+ - **full titles in the PS when the bare role word would read more natural** — "our CEO [did X]" reads more natural than "CEO Alex, 2x founder, [did X]". Drop titles unless the title itself is the proof
92
+ - more than two proof beats in the PS (three or more reads as a resume dump)
93
+ - a proof beat that is not tied to the buyer's situation — proof should explain why this sender gets the buyer's pain, not just list credentials
94
+
95
+ **Motion alignment**
96
+
97
+ - a concrete validated hook exists but the opener falls back to generic pain
98
+ - an abstract noun subject line when a concrete event/signal/asset/offer
99
+ subject was available
100
+ - the draft blends multiple example motions into one overbuilt message
101
+ - a matched archived winner is the same company as the brief, but the draft
102
+ rewrites its casing, spacing, or proof order anyway
103
+ - the three sample messages don't feel like siblings from one campaign
104
+ - a signal-led note had a clean chance to sound like an honest operator but
105
+ got polished into generic sales copy
106
+
107
+ **Fallback (proof-led specialist) drift**
108
+
109
+ - opens with "most teams..." / "scaling X..." without a concrete
110
+ buyer-language workflow anchor
111
+ - opens with synthesized "running X means..." / "being Y means..." framing
112
+ - leans on "at your stage" or "in a setup like yours"
113
+ - mechanism hides behind "we built..." / "we created..." when the product
114
+ name can carry the sentence
115
+ - stack-replacement copy never names the workflow pieces or the breaking
116
+ handoff even though the brief supports them
117
+ - invents a report, brief, teardown, video, or other lead magnet the brief
118
+ never said exists
119
+ - defaults to "can I send it over?" / "send a short overview" when the
120
+ motion is conversational
121
+
122
+ **CTA and structure**
123
+
124
+ - a CTA heavier than the proof and signal support
125
+ - asks for a meeting without offering a useful next step
126
+ - a `PS` appears anywhere other than as the final line
127
+ - a single sample stacks too many ideas (long opener + product explanation +
128
+ proof dump + CTA in one dense block)
129
+
130
+ **Finalizer discipline**
131
+
132
+ - three candidates drafted but no winner declared
133
+ - winner named "hybrid" without naming which candidate each borrowed piece
134
+ came from
135
+ - winner claimed to sweep without declaring the winning axis
136
+ - token fill rules unclear enough that the copy looks guessed
137
+
138
+ ## Substance Filters (block the message until fixed)
139
+
140
+ These filters sit ABOVE the "Automatic Revisions" list — they check whether
141
+ the message has any substance at all, not just whether the lint passes.
142
+ A candidate that fails ANY of these five filters is BLOCKED. The finalizer
143
+ pass CANNOT select a blocked candidate. If all 3 candidates fail, route to
144
+ `revise-message` with the failure reasons enumerated per candidate.
145
+
146
+ The "Automatic Revisions" list catches syntax and voice issues. Substance
147
+ Filters catch the polished-looking spam that passes lint but has no reason
148
+ to exist in this specific recipient's inbox.
149
+
150
+ ### Filter 1 — Earned-right filter
151
+
152
+ **Rule.** At least ONE line in the body MUST cite something specific to
153
+ the recipient that is verifiable in `lead-sample.json` for THAT row: a
154
+ post excerpt, profile-summary phrase, hiring-trigger, recent-funding
155
+ event, mutual connection, or company-event. If the message contains ZERO
156
+ recipient-specific signals, it has no earned right to the inbox.
157
+
158
+ **Example of a violation** (from the Ambral wrongly-approved winner):
159
+
160
+ > "Your mid-market and SMB book is where NDR hides — not enough AMs to
161
+ > cover it." — this opens with a 3-presumption accusation and cites zero
162
+ > row-specific signal.
163
+
164
+ **Route.** REJECT the candidate. Route to `revise-filter` when the signal
165
+ is missing because the lead source itself is thin (e.g., Sales Nav lane
166
+ with no post data). Route to `revise-message` when the signal IS present
167
+ in `lead-sample.json` but the draft ignored it.
168
+
169
+ ### Filter 2 — Presumption filter
170
+
171
+ **Rule.** The opener CANNOT assert facts about the recipient's business
172
+ that are not validated in `lead-sample.json` — book size, NDR location,
173
+ churn rate, staffing level, segment coverage, or any other claim about
174
+ how their team is structured or what their numbers look like. If asserted
175
+ as fact without lead-sample evidence, the message sounds like a cold
176
+ SDR reading from a guess.
177
+
178
+ Signals must also avoid intent certainty. Engagement with a topic proves
179
+ only that the person touched the topic publicly; it does not prove the
180
+ topic is "on their mind," a priority, a budget line, or an active project.
181
+ Use signals as a light reason for sending the note, not as a diagnosis.
182
+ If the natural wording would be `saw you on/around...`, the signal is
183
+ probably better used for targeting than copy.
184
+
185
+ **Example of a violation** (Ambral winner):
186
+
187
+ > "Your mid-market and SMB book is where NDR hides — not enough AMs to
188
+ > cover it." — asserts THREE unvalidated facts (book structure, NDR
189
+ > location, staffing deficit) in one sentence, none traced to the row.
190
+
191
+ **Route.** REJECT. Soften to "Most VPs of CS we talk to see..." pattern
192
+ framing (makes it an industry observation, not an accusation), OR drop
193
+ the assertion entirely and lead with a real signal. Route to
194
+ `revise-message`.
195
+
196
+ ### Filter 3 — Vague-proof filter
197
+
198
+ **Rule.** Phrases like "multi-billion-dollar enterprises", "Fortune 500
199
+ companies", "leading SaaS", "top scale-ups", or "major brands" without a
200
+ NAMED example are vapor proof. Cold buyers read these and think "name
201
+ one." Vapor proof is worse than no proof — it signals the sender has
202
+ nothing real to point to. Either strip to mechanism-only, or name a
203
+ specific customer (with permission, from the brief's safe-claims).
204
+
205
+ **Example of a violation** (Ambral winner):
206
+
207
+ > "We're deploying Ambral Cortex — AI Account Managers — inside
208
+ > multi-billion-dollar enterprises to close exactly that gap." — named
209
+ > customer count: zero.
210
+
211
+ **Route.** REJECT. Two acceptable rewrites:
212
+ (a) strip the proof line and keep only the mechanism sentence ("It
213
+ detects revenue signals across CRM + Slack + Gong + ZoomInfo and drafts
214
+ QBR follow-ups");
215
+ (b) name a specific customer from the brief's safe-claims list.
216
+ Route to `revise-message` (mechanism exists) or `revise-brief` (brief's
217
+ safe-claims are empty or too weak to name any customer).
218
+
219
+ ### Filter 4 — Read-as-1:1 filter
220
+
221
+ **Rule.** Explicit gate — could this exact message body be sent
222
+ UNCHANGED to 50 OTHER recipients in the sample and read plausibly to
223
+ each? If yes, the message is broadcast spam dressed up as a 1:1 note.
224
+ Force at least one row-specific reference (post quote, profile phrase,
225
+ hire trigger, named company event) that makes the message meaningfully
226
+ different for THIS recipient.
227
+
228
+ **Example of a violation** (Ambral winner):
229
+
230
+ > The entire Ambral Variant A body — opener, mechanism, founder-origin,
231
+ > CTA — could be sent unchanged to every one of the 14 FIT rows and 16
232
+ > non-FIT rows in `lead-sample.json`. Nothing in it points at the
233
+ > specific recipient.
234
+
235
+ **Route.** REJECT. This is the spam check. Route to `revise-message`
236
+ when `lead-sample.json` carries row-specific signal the draft ignored;
237
+ route to `revise-filter` when the sample genuinely has no row-level
238
+ signal to pull from (the real fix is upstream — find-leads should
239
+ surface post data / hire triggers / visible tools before messaging).
240
+
241
+ ### Filter 5 — Founder-origin coherence filter
242
+
243
+ **Rule.** If a founder-origin line is included, the IMMEDIATELY-FOLLOWING
244
+ sentence MUST explicitly explain how that experience created the product
245
+ mechanism. The bridge CANNOT be corporate abstraction ("intimacy at
246
+ scale", "operator empathy", "insight at velocity") — it must be a
247
+ CONCRETE mechanism sentence that names an action the product takes or a
248
+ system it connects.
249
+
250
+ **Example of a violation** (Ambral winner):
251
+
252
+ > "I woke up at 4am for a year delivering customer breakfasts at Wonder.
253
+ > The intimacy that was impossible to staff at scale is what Cortex
254
+ > does." — the bridge ("intimacy... is what Cortex does") is corporate
255
+ > abstraction; it does NOT tell the reader what Cortex actually does.
256
+
257
+ **Example that would PASS** (IncidentFox equivalent):
258
+
259
+ > "I spent 2.5 years on-call at Roblox hunting the same chaos. That's
260
+ > why IncidentFox ships a pod that debugs your network in production,
261
+ > not a dashboard." — the bridge names a concrete mechanism
262
+ > (ships-a-pod, debugs-network-in-pod), tied directly to the prior-role
263
+ > pain.
264
+
265
+ **Route.** REJECT. Two acceptable rewrites:
266
+ (a) drop the founder-origin line entirely and lead with the mechanism;
267
+ (b) rewrite the bridge to name a concrete action the product takes,
268
+ tied causally to the origin experience.
269
+ Route to `revise-message`.
270
+
271
+ ### Filter 6 — Anti-AI-tell filter
272
+
273
+ **Rule.** Reject the candidate if the body contains ANY of the
274
+ following AI-tell patterns. These are the signals that make a cold
275
+ message read as "AI-generated from a scrape" instead of "a founder
276
+ typed this about something they're working on."
277
+
278
+ 1. **Date or duration references about the recipient.** Phrases that
279
+ name a specific date, month, or tenure length on the recipient's
280
+ timeline — "last month", "yesterday", "six months ago", "six months
281
+ into your CTO seat", "since 2023", verbatim post dates
282
+ ("2026-03-16"). Cold operators don't say this. Drop the date or
283
+ re-cast as a natural callback ("saw your recent post about..." →
284
+ not "on March 16 you posted...").
285
+ 2. **Block-quoted verbatim recipient phrases longer than 4 words
286
+ inside quotation marks.** Long quote-backs announce that the sender
287
+ scraped the recipient. Compress to 2-4 words OR paraphrase into
288
+ natural language ("you wrote about runbook troubleshooters" beats
289
+ `"deployment fixer and troubleshooter ... runbook troubleshooter"`
290
+ with the block-quote punctuation).
291
+ 3. **Explicit-source-citation phrases.** `"Your bio says"`, `"your
292
+ profile shows"`, `"the post you wrote"`, `"the req you posted"`,
293
+ `"saw on your LinkedIn"`, `"your X-page mentions"`. These
294
+ phrases are the prose equivalent of showing your work — humans
295
+ don't narrate the source of their knowledge when DMing a peer.
296
+ Drop the source-narration frame; reference the signal as if it
297
+ were obvious background.
298
+ 4. **Stack/feature lists in parentheses.** `"(Datadog / PagerDuty /
299
+ GitHub / AWS)"`, `"(Slack + Linear + GitHub)"`, `"(Stripe,
300
+ HubSpot, Zendesk)"`. Parenthetical tool dumps read as a
301
+ machine-assembled integration list. Either name ONE relevant tool
302
+ inline or drop the parenthetical entirely.
303
+ 5. **Over-precise numerics that a human wouldn't type.** `"2 Q1 2026
304
+ pilot partners"`, `"spent the last eight years"`, `"three failure
305
+ modes"`, `"27% of our sample"`. Founders write casually: "a couple
306
+ pilots this quarter", "you've been at this a while", "the same
307
+ kinds of failures", "about a quarter". Round, hedge, or drop.
308
+
309
+ **Example of a violation** (IncidentFox rerun-4 winner):
310
+
311
+ > "Nedko — your BioRender post last month on the internal governance
312
+ > dashboard called out "deployment fixer and troubleshooter" and
313
+ > "runbook troubleshooter" as high-value use cases."
314
+
315
+ Three tells stacked in one sentence: date reference ("last month"),
316
+ two block-quoted phrases >4 words, and implicit source-citation
317
+ frame ("your BioRender post").
318
+
319
+ **Natural alternative** (same signal, no tells):
320
+
321
+ > "Saw you've been writing about runbook troubleshooters and
322
+ > deployment fixers — that's almost exactly the class of work we
323
+ > built the product around."
324
+
325
+ **Route.** REJECT. Route to `revise-message`.
326
+
327
+ ### Filter 7 — Anti-talk-at filter
328
+
329
+ **Rule.** The OPENING SENTENCE must not lead with a
330
+ recipient-specific reference. The opener must establish sender
331
+ reality FIRST — what we're building, who we are, why we're reaching
332
+ out — before any callback to the recipient. A "talk-at" opener reads
333
+ like a cold-scraper addressing a target; a "sender-first" opener
334
+ reads like a founder introducing themselves.
335
+
336
+ **Detection patterns to REJECT as the opener (non-exhaustive):**
337
+
338
+ - `{{first_name}} — saw you...`
339
+ - `{{first_name}} — your [X]...`
340
+ - `Your [X] is...`
341
+ - `Saw the [X] you posted...`
342
+ - `I noticed your...`
343
+ - `Six months into your...`
344
+ - `Your [company] post last month...`
345
+ - `Just read your [X]...`
346
+
347
+ Recipient-specific gravity MAY appear in body sentences 2-4 as a
348
+ LIGHT callback (one casual sentence, no verbatim quote, no date
349
+ reference), but never as the lede. The first impression must read
350
+ as "a founder reaching out about something they built" — not
351
+ "someone who scraped me."
352
+
353
+ **Acceptable opener shapes (sender-first):**
354
+
355
+ - `Hey {{first_name}}, I'm building [X] — [what it does in one plain
356
+ sentence].`
357
+ - `Hey {{first_name}}, quick one. My co-founder and I left [prior
358
+ role] to build [X].`
359
+ - `Hey {{first_name}}, reaching out because [one-line sender
360
+ context] and [one-line what we're building].`
361
+ - `Hey {{first_name}}, [product] — [one-sentence mechanism].`
362
+
363
+ Then, sentences 2-4 may include a LIGHT callback to the recipient:
364
+ `"Thought of [company] because..."`, `"Feels relevant to the
365
+ [recipient's area] work you've been doing."`, `"Saw you're working on
366
+ [area]."` (NOT `"saw the [X] you posted on [date]"`.)
367
+
368
+ **Example of a violation** (IncidentFox rerun-4 winner opener):
369
+
370
+ > "Nedko — your BioRender post last month on the internal governance
371
+ > dashboard called out [...]"
372
+
373
+ Opens with recipient reference + date + source citation. Reads as
374
+ "I scraped you." Zero sender context before the first sentence
375
+ ends.
376
+
377
+ **Natural alternative** (sender-first, light callback in sentence 3):
378
+
379
+ > "Hey Nedko, my co-founder and I left Roblox to build IncidentFox —
380
+ > an AI SRE that lives in Slack and posts a root-cause hypothesis
381
+ > before a human wakes up.
382
+ >
383
+ > It auto-builds its integrations from your codebase, so deployment
384
+ > goes from "paste the API keys" to "it's already reading your repo."
385
+ >
386
+ > Thought of BioRender because you've been writing publicly about
387
+ > runbook troubleshooters, which is almost exactly the class of
388
+ > work we built around."
389
+
390
+ Opener establishes who's sending and what they built. The
391
+ recipient callback arrives in sentence 3, paraphrased, no date, no
392
+ block quote.
393
+
394
+ **Route.** REJECT. Route to `revise-message`.
395
+
396
+ ### Filter 8 — AI-tells filter (catalog-backed)
397
+
398
+ **Rule.** Reject if the candidate contains any pattern marked
399
+ `REJECT` in the canonical catalog at
400
+ `references/ai-tells.md`. Surface as a warning (do not auto-block)
401
+ if the candidate contains any pattern marked `WARN`.
402
+
403
+ The canonical catalog lives at
404
+ `mcp/sellable/skills/create-campaign-v2/references/ai-tells.md`. It
405
+ is the single source of truth for all AI-tell patterns. Add new
406
+ tells to that file directly — do NOT bloat this filter spec.
407
+
408
+ **Mandatory check.** For each candidate in the sample:
409
+
410
+ 1. Read `references/ai-tells.md`.
411
+ 2. For each `Tell #N` entry, run the pattern against the candidate
412
+ body.
413
+ 3. If any `REJECT`-severity tell matches, mark the candidate BLOCKED
414
+ with `Filter 8 — AI-tells (Tell #N: [title])` and move on.
415
+ 4. If any `WARN`-severity tell matches, surface in the candidate's
416
+ Substance Filter gate table but do NOT auto-block.
417
+ 5. A candidate blocked by Filter 8 cannot be selected as the winner.
418
+
419
+ **Note on Filter 3 and Filter 6 overlap.** Several tells in the
420
+ catalog (Tell #3 date refs, Tell #4 block-quotes, Tell #5 source-
421
+ citation, Tell #6 parenthetical stack, Tell #7 over-precise numerics,
422
+ Tell #8 hardcoded signoff, Tell #10 vague-proof) overlap with
423
+ Filters 3 and 6. This is intentional — the catalog is the canonical
424
+ enumeration; Filter 8 is the catalog-driven check. Filters 3 and 6
425
+ remain as human-readable explanations of specific high-priority
426
+ tell classes, but the actual detection logic in Phase-84 reads from
427
+ `ai-tells.md`.
428
+
429
+ **Adding new tells.** When UAT surfaces a new AI-tell pattern, add
430
+ it as `Tell #N` in `ai-tells.md` with Pattern / Why / Allowed
431
+ alternative / Severity. The next Phase-84 run picks it up
432
+ automatically. Update
433
+ `tests/scripts/skill-references-substance-filters.test.ts` if the
434
+ new tell needs regression coverage.
435
+
436
+ **Example of a violation** (Plan 05 Patientdesk winner, pre-catalog —
437
+ Tell #1 body-level self-introduction):
438
+
439
+ > "Hey Bonnie — Oncel here. My co-founders and I ran our own dental
440
+ > call center for two years before building Patientdesk..."
441
+
442
+ The `"Oncel here."` clause is the Tell #1 pattern. The recipient
443
+ already sees "Oncel Ozgul / Co-founder & CEO @ Patientdesk.ai" from
444
+ the LinkedIn sender strip. Repeating the name in the body adds
445
+ nothing except a template-aroma.
446
+
447
+ **Acceptable rewrites** (same substance, no Tell #1):
448
+
449
+ > "Hey Bonnie — pre-Patientdesk, my co-founders and I ran our own
450
+ > dental call center for two years; every missed call was a real
451
+ > number on our P&L."
452
+
453
+ > "Hey Bonnie — quick one. We built Patientdesk out of running our
454
+ > own dental call center for two years, where every missed call hit
455
+ > the P&L directly."
456
+
457
+ > "Hey Bonnie — building a thing for group dentistry I think you'd
458
+ > have an opinion on. My co-founders and I ran our own dental call
459
+ > center for two years before this..."
460
+
461
+ All three open with substance (what we built / the reality that led
462
+ to it) and trust the LinkedIn UI to handle sender identity.
463
+
464
+ **Route.** REJECT. Route to `revise-message`.
465
+
466
+ ---
467
+
468
+ Note: filters 1-4 are evaluated PER CANDIDATE. Filter 5 only applies
469
+ when a founder-origin line is present in the candidate. Filters 6, 7,
470
+ and 8 are evaluated PER CANDIDATE and check for AI-tells, talk-at
471
+ openers, and self-introduction tells respectively. A candidate that
472
+ passes all 8 filters plus the Automatic Revisions list may proceed to
473
+ the Finalizer Pass. A candidate that fails any of the 8 is marked
474
+ BLOCKED in the Candidate Messages section (with the filter name
475
+ cited) and cannot be selected as the winner.
476
+
477
+ ## Revision Direction
478
+
479
+ When the message is weak, decide which layer is actually wrong:
480
+
481
+ - revise-message
482
+ - the structure, wording, or CTA is off
483
+ - revise-filter
484
+ - the signal is too noisy or the sample lead is not a clean buyer
485
+ - revise-brief
486
+ - the proof boundary or message thesis in `brief.md` is the real problem
487
+
488
+ Do not patch a weak message by inventing stronger personalization.
489
+
490
+ ## Dry-Mode Discipline
491
+
492
+ Phase 84 dry mode is validation, not production drafting.
493
+
494
+ - use only `brief.md`, `lead-filter.md`, and `lead-sample.json`
495
+ - do not fetch new research
496
+ - do not mutate campaign state
497
+ - if the personalization source is missing, remove the token or route back for revision
498
+ - synthesize the structure from the closest archived gold-standard winner
499
+ before reaching for a generic framework
500
+ - retrieve broadly, but preserve one primary example and borrow from at most one
501
+ narrow secondary influence
502
+
503
+ ## Plan 85-03 Critique Addendum
504
+
505
+ These filters are also the gate on any critique rewrite shipped by the
506
+ Plan 85-03 parallel critique protocol (see
507
+ `references/parallel-critique-protocol.md`). When the critique flag is
508
+ on, a rewritten message must still clear:
509
+
510
+ - every "Automatic Revisions" check above (jargon, resume-list PS, stacked
511
+ sentences, weak hooks, etc.)
512
+ - the Phase 84 token contract: only supported tokens from the brief, no
513
+ unresolved `{{token}}`, no invented proof, no personalization that can't
514
+ be traced to `lead-sample.json`
515
+ - the Phase 84 finalizer pass when `synthesis.enforceFinalizerPass` is
516
+ `true` (default)
517
+
518
+ If a rewrite fails any of these, the critique pass falls back to the
519
+ plain generated message for that row. Nothing about these filters
520
+ changes because critique is running; they remain the bar the copy
521
+ must clear before persistence.