@sellable/mcp 0.1.238 → 0.1.239
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/tools/content-posts.d.ts +8 -0
- package/dist/tools/content-posts.js +23 -7
- package/dist/tools/registry.d.ts +2 -0
- package/package.json +2 -1
- package/skills/create-campaign-v2/references/validation-criteria.md +4 -4
- package/skills/create-post/SKILL.md +52 -10
- package/skills/create-post/references/gold-standard-post-pack.md +8 -0
- package/skills/create-post/references/hook-research-playbook.md +42 -1
- package/skills/create-post/references/post-file-contract.md +5 -1
- package/skills/create-post/references/post-validation.md +28 -7
- package/skills/generate-messages/SKILL.md +5 -0
|
@@ -58,6 +58,7 @@ export declare const contentPostToolDefinitions: ({
|
|
|
58
58
|
keywords?: undefined;
|
|
59
59
|
sourcePosts?: undefined;
|
|
60
60
|
selectedPatterns?: undefined;
|
|
61
|
+
previewBudget?: undefined;
|
|
61
62
|
notes?: undefined;
|
|
62
63
|
createdAt?: undefined;
|
|
63
64
|
draftId?: undefined;
|
|
@@ -130,6 +131,11 @@ export declare const contentPostToolDefinitions: ({
|
|
|
130
131
|
type: string;
|
|
131
132
|
};
|
|
132
133
|
};
|
|
134
|
+
previewBudget: {
|
|
135
|
+
type: string;
|
|
136
|
+
description: string;
|
|
137
|
+
additionalProperties: boolean;
|
|
138
|
+
};
|
|
133
139
|
notes: {
|
|
134
140
|
type: string;
|
|
135
141
|
};
|
|
@@ -192,6 +198,7 @@ export declare const contentPostToolDefinitions: ({
|
|
|
192
198
|
keywords?: undefined;
|
|
193
199
|
sourcePosts?: undefined;
|
|
194
200
|
selectedPatterns?: undefined;
|
|
201
|
+
previewBudget?: undefined;
|
|
195
202
|
notes?: undefined;
|
|
196
203
|
};
|
|
197
204
|
required: string[];
|
|
@@ -245,6 +252,7 @@ export declare function saveHookResearchTool(input: {
|
|
|
245
252
|
keywords?: string[];
|
|
246
253
|
sourcePosts?: Array<Record<string, unknown>>;
|
|
247
254
|
selectedPatterns?: string[];
|
|
255
|
+
previewBudget?: Record<string, unknown>;
|
|
248
256
|
notes?: string;
|
|
249
257
|
createdAt?: string;
|
|
250
258
|
}): {
|
|
@@ -66,6 +66,11 @@ export const contentPostToolDefinitions = [
|
|
|
66
66
|
type: "array",
|
|
67
67
|
items: { type: "string" },
|
|
68
68
|
},
|
|
69
|
+
previewBudget: {
|
|
70
|
+
type: "object",
|
|
71
|
+
description: "Optional structured LinkedIn preview-budget summary for studied hooks and selected patterns.",
|
|
72
|
+
additionalProperties: true,
|
|
73
|
+
},
|
|
69
74
|
notes: { type: "string" },
|
|
70
75
|
createdAt: { type: "string" },
|
|
71
76
|
},
|
|
@@ -202,11 +207,14 @@ export function capturePostIdeaTool(input) {
|
|
|
202
207
|
const markdown = buildMarkdown(metadata, [
|
|
203
208
|
["Distilled Brief", input.distilledBrief || ""],
|
|
204
209
|
["Raw Source", rawBlock(input.rawSource)],
|
|
205
|
-
[
|
|
210
|
+
[
|
|
211
|
+
"Source Metadata",
|
|
212
|
+
jsonBlock(stripUndefined({
|
|
206
213
|
sourceType: input.sourceType,
|
|
207
214
|
sourceUrl: input.sourceUrl,
|
|
208
215
|
capturedAt: now,
|
|
209
|
-
}))
|
|
216
|
+
})),
|
|
217
|
+
],
|
|
210
218
|
]);
|
|
211
219
|
writeArtifact(relativePath, markdown);
|
|
212
220
|
return {
|
|
@@ -246,6 +254,7 @@ export function saveHookResearchTool(input) {
|
|
|
246
254
|
const markdown = buildMarkdown(metadata, [
|
|
247
255
|
["Keywords", listBlock(input.keywords ?? [])],
|
|
248
256
|
["Selected Patterns", listBlock(input.selectedPatterns ?? [])],
|
|
257
|
+
["Preview Budget", jsonBlock(input.previewBudget ?? {})],
|
|
249
258
|
["Source Posts", jsonBlock(input.sourcePosts ?? [])],
|
|
250
259
|
["Notes", input.notes || ""],
|
|
251
260
|
]);
|
|
@@ -332,18 +341,24 @@ export function markPostPublishedTool(input) {
|
|
|
332
341
|
};
|
|
333
342
|
const markdown = buildMarkdown(metadata, [
|
|
334
343
|
["Final Text", input.finalText || ""],
|
|
335
|
-
[
|
|
344
|
+
[
|
|
345
|
+
"Publish Metadata",
|
|
346
|
+
jsonBlock(stripUndefined({
|
|
336
347
|
draftId: safeDraftId,
|
|
337
348
|
publishUrl: input.publishUrl,
|
|
338
349
|
activityId: activityId || undefined,
|
|
339
350
|
publishedAt,
|
|
340
|
-
}))
|
|
341
|
-
|
|
351
|
+
})),
|
|
352
|
+
],
|
|
353
|
+
[
|
|
354
|
+
"Future Metrics",
|
|
355
|
+
jsonBlock({
|
|
342
356
|
impressions: null,
|
|
343
357
|
reactions: null,
|
|
344
358
|
comments: null,
|
|
345
359
|
snapshots: [],
|
|
346
|
-
})
|
|
360
|
+
}),
|
|
361
|
+
],
|
|
347
362
|
]);
|
|
348
363
|
writeArtifact(relativePath, markdown);
|
|
349
364
|
return {
|
|
@@ -483,7 +498,8 @@ function validateRelativePath(relativePath) {
|
|
|
483
498
|
}
|
|
484
499
|
function isPathInside(candidate, root) {
|
|
485
500
|
const relative = path.relative(root, candidate);
|
|
486
|
-
return relative === "" ||
|
|
501
|
+
return (relative === "" ||
|
|
502
|
+
(!relative.startsWith("..") && !path.isAbsolute(relative)));
|
|
487
503
|
}
|
|
488
504
|
function normalizeArtifactId(id, label) {
|
|
489
505
|
requireString(id, label);
|
package/dist/tools/registry.d.ts
CHANGED
|
@@ -1214,6 +1214,7 @@ export declare const allTools: ({
|
|
|
1214
1214
|
keywords?: undefined;
|
|
1215
1215
|
sourcePosts?: undefined;
|
|
1216
1216
|
selectedPatterns?: undefined;
|
|
1217
|
+
previewBudget?: undefined;
|
|
1217
1218
|
notes?: undefined;
|
|
1218
1219
|
createdAt?: undefined;
|
|
1219
1220
|
draftId?: undefined;
|
|
@@ -1279,6 +1280,7 @@ export declare const allTools: ({
|
|
|
1279
1280
|
keywords?: undefined;
|
|
1280
1281
|
sourcePosts?: undefined;
|
|
1281
1282
|
selectedPatterns?: undefined;
|
|
1283
|
+
previewBudget?: undefined;
|
|
1282
1284
|
notes?: undefined;
|
|
1283
1285
|
};
|
|
1284
1286
|
required: string[];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sellable/mcp",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.239",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Sellable MCP server for Claude Code and Codex campaign workflows",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
},
|
|
12
12
|
"scripts": {
|
|
13
13
|
"build": "tsc",
|
|
14
|
+
"prepack": "npm run build",
|
|
14
15
|
"start": "node dist/index.js",
|
|
15
16
|
"start:dev": "node dist/index-dev.js",
|
|
16
17
|
"dev": "ts-node src/index.ts"
|
|
@@ -93,10 +93,10 @@ Required:
|
|
|
93
93
|
select a blocked candidate. If all 3 candidates fail, route to
|
|
94
94
|
`revise-message` with the failure reasons enumerated per candidate (cite the
|
|
95
95
|
rule/filter name and the offending line).
|
|
96
|
-
- **Concrete Language Audit.** The selected draft must highlight every abstract
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
96
|
+
- **Concrete Language Audit.** The selected draft must highlight every abstract verb,
|
|
97
|
+
abstract noun, abstract adjective, abstract adverb, and cliche, then either
|
|
98
|
+
replace it with a buyer-visible action, object, workflow, artifact, risk,
|
|
99
|
+
result, or next step from the approved evidence, cut it, or block with
|
|
100
100
|
`revise-message` / `revise-filter`. Do not leave a vague phrase in place just
|
|
101
101
|
because it sounds polished.
|
|
102
102
|
|
|
@@ -20,7 +20,7 @@ Hard fail patterns:
|
|
|
20
20
|
- drafts that skip raw idea capture
|
|
21
21
|
- hooks copied verbatim from another creator
|
|
22
22
|
- using `~/.sellable/configs/content/linkedin-posts-drafts.md` as the normal save target
|
|
23
|
-
</role>
|
|
23
|
+
</role>
|
|
24
24
|
|
|
25
25
|
<scope>
|
|
26
26
|
V1 is posts-only and hooks-first.
|
|
@@ -42,7 +42,7 @@ Do not:
|
|
|
42
42
|
- optimize a comment workflow
|
|
43
43
|
- move identity/proof/story memory into content files
|
|
44
44
|
- append new drafts to the legacy flat file
|
|
45
|
-
</scope>
|
|
45
|
+
</scope>
|
|
46
46
|
|
|
47
47
|
<required_assets>
|
|
48
48
|
Before drafting, load all required assets with `mcp__sellable__get_subskill_asset`:
|
|
@@ -90,7 +90,7 @@ Do not call outbound/campaign tools from this skill. Do not call message-generat
|
|
|
90
90
|
Load user memory before hook generation or drafting:
|
|
91
91
|
|
|
92
92
|
1. Call `mcp__sellable__get_engage_memory`.
|
|
93
|
-
2. Read or use the returned core memory fields:
|
|
93
|
+
2. Read or use the returned core identity memory and company memory fields:
|
|
94
94
|
- `core/about-me.md`
|
|
95
95
|
- `core/my-company.md`
|
|
96
96
|
- `core/anti-ai-writing-style.md`
|
|
@@ -115,6 +115,30 @@ Load user memory before hook generation or drafting:
|
|
|
115
115
|
New users may have blank or missing core files until they run `$sellable:interview`. If story, proof, identity, or company memory is missing, ask for the missing material or ask the user to run `$sellable:interview`. Never fabricate what should have come from `story-bank.md`, `proof-ledger.md`, or `answer-bank.md`.
|
|
116
116
|
|
|
117
117
|
The interview/core memory files remain the durable source for stories and proof. This skill may read them, but it must not move them into `~/.sellable/content/**`.
|
|
118
|
+
|
|
119
|
+
Use core context modes as drafting constraints, not decorative labels. If a
|
|
120
|
+
post idea exposes durable memory that should help future writing, propose a
|
|
121
|
+
core write-back before saving it. The user must be able to approve, edit, or
|
|
122
|
+
reject each proposed memory update before it is written.
|
|
123
|
+
|
|
124
|
+
Durable write-back targets:
|
|
125
|
+
|
|
126
|
+
- `core/answer-bank.md` for reusable user answers, positioning, or taste rules
|
|
127
|
+
- `core/wins-ledger.md` for verified wins and outcomes
|
|
128
|
+
- `core/change-log.md` for approved corrections, rejected-but-useful proposals,
|
|
129
|
+
privacy decisions, and downstream prompt/operator notes
|
|
130
|
+
- `core/story-bank.md` for reusable first-person stories
|
|
131
|
+
- `core/transcripts/INDEX.md` for source interview or voice-memo references
|
|
132
|
+
- `core/references/linkedin-posts/INDEX.md` and adjacent files for approved
|
|
133
|
+
gold-standard post references
|
|
134
|
+
|
|
135
|
+
Write-backs must use stable source keys such as
|
|
136
|
+
`create-post:{date}:{ideaId}:{slug}` or
|
|
137
|
+
`create-post-research:{date}:{sourcePostHash}:{slug}`. Check existing copied
|
|
138
|
+
paths before adding references, create no duplicate reference rows, and keep
|
|
139
|
+
manual sections preserved. If the user says to mark private, store only the
|
|
140
|
+
minimum private-safe metadata and do not promote it into public proof,
|
|
141
|
+
references, or examples.
|
|
118
142
|
</memory_contract>
|
|
119
143
|
|
|
120
144
|
<modes>
|
|
@@ -221,6 +245,7 @@ Record provenance:
|
|
|
221
245
|
- lead-magnet or engagement-bait penalties
|
|
222
246
|
- story mechanism when relevant
|
|
223
247
|
- full-text match status
|
|
248
|
+
- source hook preview measurements and whether they came from full text or a search preview
|
|
224
249
|
- selected hook patterns
|
|
225
250
|
- why each pattern fits the user's idea and voice
|
|
226
251
|
|
|
@@ -233,8 +258,14 @@ Each hook must include:
|
|
|
233
258
|
- source hook pattern
|
|
234
259
|
- why it fits this idea
|
|
235
260
|
- `charCount`
|
|
261
|
+
- `charCountIncludingNewlines`
|
|
236
262
|
- `firstLineChars`
|
|
237
263
|
- `firstTwoLinesChars`
|
|
264
|
+
- `physicalLineCount`
|
|
265
|
+
- `contentLineCount`
|
|
266
|
+
- `longestNonblankLineChars`
|
|
267
|
+
- `blankLineVisualRisk`
|
|
268
|
+
- `previewBudgetStatus`
|
|
238
269
|
- `mobilePreviewFit`
|
|
239
270
|
- `desktopPreviewFit`
|
|
240
271
|
- `lineCountEstimate`
|
|
@@ -246,14 +277,22 @@ Each hook must include:
|
|
|
246
277
|
|
|
247
278
|
Do not copy source wording. Copy only the structure.
|
|
248
279
|
|
|
249
|
-
Use
|
|
280
|
+
Use this conservative mobile-first LinkedIn preview gate. LinkedIn does not
|
|
281
|
+
publish exact "see more" cutoff rules, and rendering varies by device, app
|
|
282
|
+
version, font, media, and line break. These are v1 safety budgets, not claims
|
|
283
|
+
about an official LinkedIn limit:
|
|
284
|
+
|
|
285
|
+
- `pass`: hook is <= 110 chars including newlines, every nonblank line is <= 45 chars, and the hook's core point lands before likely truncation.
|
|
286
|
+
- `warn`: hook is 111-140 chars including newlines, any nonblank line is 46-55 chars, or blank lines create visual-line risk. Blank lines are allowed, but they count as physical lines.
|
|
287
|
+
- `fail`: hook is > 140 chars including newlines, any nonblank line is > 55 chars, or the hook's point depends on text after likely truncation.
|
|
250
288
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
- desktop first line: 70-90 chars
|
|
254
|
-
- desktop first two lines: 140-180 chars
|
|
289
|
+
Desktop preview usually has more room. Still record `desktopPreviewFit`, but
|
|
290
|
+
never let desktop fit compensate for a mobile `fail`.
|
|
255
291
|
|
|
256
|
-
If a hook's point depends on text after the likely preview, rewrite it before
|
|
292
|
+
If a hook's point depends on text after the likely preview, rewrite it before
|
|
293
|
+
selecting it. A selected hook may carry a `warn` only when the warning is about
|
|
294
|
+
intentional blank-line rhythm or a slight line-length overage; include a compact
|
|
295
|
+
fallback in the validation receipt.
|
|
257
296
|
|
|
258
297
|
## Step 3: Draft
|
|
259
298
|
|
|
@@ -287,6 +326,7 @@ Every saved draft needs a validation receipt with:
|
|
|
287
326
|
- voice audit findings
|
|
288
327
|
- anti-AI audit findings
|
|
289
328
|
- outbound AI-tell audit findings
|
|
329
|
+
- hook preview pass/warn/fail status and compact fallback when warned
|
|
290
330
|
- finalizer changes
|
|
291
331
|
- blocked/retry-needed reasons, if any
|
|
292
332
|
|
|
@@ -319,6 +359,7 @@ Published post records live under:
|
|
|
319
359
|
```text
|
|
320
360
|
~/.sellable/content/linkedin/published/
|
|
321
361
|
```
|
|
362
|
+
|
|
322
363
|
</pipeline>
|
|
323
364
|
|
|
324
365
|
<legacy>
|
|
@@ -341,8 +382,9 @@ validation_summary:
|
|
|
341
382
|
proof: pass | needs_user_input | blocked
|
|
342
383
|
voice: pass | needs_revision
|
|
343
384
|
anti_ai: pass | needs_revision
|
|
344
|
-
linkedin_preview: pass | needs_revision
|
|
385
|
+
linkedin_preview: pass | warn | needs_revision
|
|
345
386
|
concrete_language: pass | needs_revision
|
|
346
387
|
next_step: <what the user should do next>
|
|
347
388
|
```
|
|
389
|
+
|
|
348
390
|
</response_shape>
|
|
@@ -78,6 +78,7 @@ Show compact candidate cards. Include:
|
|
|
78
78
|
- engagement breakdown when available
|
|
79
79
|
- why it might belong in the pack
|
|
80
80
|
- hook mechanism
|
|
81
|
+
- hook preview budget status and measurement basis
|
|
81
82
|
- content/body mechanism
|
|
82
83
|
- rhythm notes
|
|
83
84
|
- sentence-structure notes
|
|
@@ -144,8 +145,15 @@ Tags:
|
|
|
144
145
|
|
|
145
146
|
- hook text:
|
|
146
147
|
- char count:
|
|
148
|
+
- char count including newlines:
|
|
147
149
|
- first-line chars:
|
|
148
150
|
- first-two-line chars:
|
|
151
|
+
- physical line count:
|
|
152
|
+
- content line count:
|
|
153
|
+
- longest nonblank line chars:
|
|
154
|
+
- blank-line visual risk:
|
|
155
|
+
- source text basis:
|
|
156
|
+
- preview budget status:
|
|
149
157
|
- mobile preview fit:
|
|
150
158
|
- desktop preview fit:
|
|
151
159
|
- hook mechanism:
|
|
@@ -74,6 +74,47 @@ When full hook/body text matters:
|
|
|
74
74
|
|
|
75
75
|
If full text is unavailable, record `full_text_unavailable`. Use only the preview for hook analysis and do not infer missing body details.
|
|
76
76
|
|
|
77
|
+
## Opening Preview Measurement
|
|
78
|
+
|
|
79
|
+
Measure the visible opening for every shortlisted source post before extracting
|
|
80
|
+
the hook pattern. This makes the study useful for LinkedIn, not just generally
|
|
81
|
+
"good writing."
|
|
82
|
+
|
|
83
|
+
LinkedIn does not publish exact "see more" cutoff rules. Treat these as
|
|
84
|
+
conservative v1 planning budgets:
|
|
85
|
+
|
|
86
|
+
- `pass`: opening hook is <= 110 chars including newlines, every nonblank line
|
|
87
|
+
is <= 45 chars, and the hook's core point lands before likely truncation.
|
|
88
|
+
- `warn`: opening hook is 111-140 chars including newlines, any nonblank line is
|
|
89
|
+
46-55 chars, or blank lines create visual-line risk. Blank lines are allowed,
|
|
90
|
+
but they count as physical lines.
|
|
91
|
+
- `fail`: opening hook is > 140 chars including newlines, any nonblank line is
|
|
92
|
+
> 55 chars, or the hook's core point depends on text after likely truncation.
|
|
93
|
+
|
|
94
|
+
Desktop preview has more room, so record it separately, but never let desktop
|
|
95
|
+
fit compensate for a mobile `fail`.
|
|
96
|
+
|
|
97
|
+
For each source, record:
|
|
98
|
+
|
|
99
|
+
- `sourceTextBasis`: `full_text`, `search_preview`, or `manual_user_source`
|
|
100
|
+
- `openingTextUsed`
|
|
101
|
+
- `charCountIncludingNewlines`
|
|
102
|
+
- `physicalLineCount`
|
|
103
|
+
- `contentLineCount`
|
|
104
|
+
- `firstLineChars`
|
|
105
|
+
- `firstTwoPhysicalLinesChars`
|
|
106
|
+
- `firstTwoContentLinesChars`
|
|
107
|
+
- `longestNonblankLineChars`
|
|
108
|
+
- `blankLineCountBeforeFold`
|
|
109
|
+
- `mobilePreviewBudget`: `pass`, `warn`, or `fail`
|
|
110
|
+
- `desktopPreviewBudget`: `pass`, `warn`, or `fail`
|
|
111
|
+
- `blankLineVisualRisk`
|
|
112
|
+
- `corePointBeforeLikelyTruncation`
|
|
113
|
+
|
|
114
|
+
If only a search preview is available, do not pretend the opening is complete.
|
|
115
|
+
Record `sourceTextBasis: search_preview` and lower confidence when the hook
|
|
116
|
+
appears cut off or body context is unavailable.
|
|
117
|
+
|
|
77
118
|
## Hook Extraction
|
|
78
119
|
|
|
79
120
|
Extract structure, not wording.
|
|
@@ -85,7 +126,7 @@ For each shortlisted source post, record:
|
|
|
85
126
|
- engagement totals and available likes/comments/shares breakdown
|
|
86
127
|
- creator repeat evidence
|
|
87
128
|
- visible hook text or preview
|
|
88
|
-
-
|
|
129
|
+
- opening preview measurement fields from the section above
|
|
89
130
|
- hook mechanism
|
|
90
131
|
- story mechanism when the post is a story
|
|
91
132
|
- internal question created
|
|
@@ -59,6 +59,9 @@ Hook research files must preserve:
|
|
|
59
59
|
- author/profile URLs
|
|
60
60
|
- engagement totals
|
|
61
61
|
- full-text availability
|
|
62
|
+
- source hook preview measurements, including text basis, char count including
|
|
63
|
+
newlines, physical/content line counts, longest nonblank line, blank-line
|
|
64
|
+
visual risk, and mobile/desktop preview budget status
|
|
62
65
|
- extracted hook patterns
|
|
63
66
|
- selected hook basis
|
|
64
67
|
|
|
@@ -67,7 +70,8 @@ Draft files must preserve:
|
|
|
67
70
|
- source idea ID
|
|
68
71
|
- hook research ID
|
|
69
72
|
- draft body
|
|
70
|
-
- validation receipt
|
|
73
|
+
- validation receipt, including LinkedIn preview pass/warn/fail status and
|
|
74
|
+
compact fallback when the selected hook carries a warning
|
|
71
75
|
- status: `draft`, `ready`, or `needs_revision`
|
|
72
76
|
|
|
73
77
|
Published files must preserve:
|
|
@@ -32,7 +32,9 @@ Each candidate should include:
|
|
|
32
32
|
- hook text
|
|
33
33
|
- source pattern
|
|
34
34
|
- score
|
|
35
|
-
- char count and first-line / first-two-line preview measurements
|
|
35
|
+
- char count including newlines and first-line / first-two-line preview measurements
|
|
36
|
+
- physical line count, content line count, longest nonblank line, and blank-line risk
|
|
37
|
+
- `previewBudgetStatus`: `pass`, `warn`, or `fail`
|
|
36
38
|
- mobile and desktop preview fit
|
|
37
39
|
- proof/story dependency
|
|
38
40
|
- AI-tell risk
|
|
@@ -52,25 +54,44 @@ After the first draft:
|
|
|
52
54
|
|
|
53
55
|
## LinkedIn Preview Audit
|
|
54
56
|
|
|
55
|
-
Audit the selected hook and top candidates against conservative LinkedIn
|
|
57
|
+
Audit the selected hook and top candidates against conservative LinkedIn
|
|
58
|
+
preview budgets. LinkedIn does not publish exact "see more" cutoff rules, and
|
|
59
|
+
rendering varies by device, app version, font, media, and line break. This audit
|
|
60
|
+
is a mobile-first safety gate, not a claim about an official LinkedIn limit.
|
|
56
61
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
-
|
|
60
|
-
-
|
|
62
|
+
Use:
|
|
63
|
+
|
|
64
|
+
- `pass`: hook is <= 110 chars including newlines, every nonblank line is <= 45 chars, and the hook's core point lands before likely truncation.
|
|
65
|
+
- `warn`: hook is 111-140 chars including newlines, any nonblank line is 46-55 chars, or blank lines create visual-line risk. Blank lines are allowed, but they count as physical lines.
|
|
66
|
+
- `fail`: hook is > 140 chars including newlines, any nonblank line is > 55 chars, or the hook's point depends on text after likely truncation.
|
|
67
|
+
|
|
68
|
+
Desktop preview usually has more room. Still record desktop fit, but never let
|
|
69
|
+
desktop fit compensate for a mobile `fail`.
|
|
61
70
|
|
|
62
71
|
Record:
|
|
63
72
|
|
|
64
73
|
- `charCount`
|
|
74
|
+
- `charCountIncludingNewlines`
|
|
65
75
|
- `firstLineChars`
|
|
66
76
|
- `firstTwoLinesChars`
|
|
77
|
+
- `physicalLineCount`
|
|
78
|
+
- `contentLineCount`
|
|
79
|
+
- `longestNonblankLineChars`
|
|
80
|
+
- `blankLineCountBeforeFold`
|
|
81
|
+
- `blankLineVisualRisk`
|
|
82
|
+
- `corePointBeforeLikelyTruncation`
|
|
83
|
+
- `previewBudgetStatus`
|
|
67
84
|
- `mobilePreviewFit`
|
|
68
85
|
- `desktopPreviewFit`
|
|
69
86
|
- `lineCountEstimate`
|
|
70
87
|
- `truncationRisk`
|
|
71
88
|
- `rewriteIfTruncated`
|
|
89
|
+
- `compactFallback` when `previewBudgetStatus` is `warn`
|
|
72
90
|
|
|
73
|
-
|
|
91
|
+
If the hook only works after likely truncation, rewrite it. A draft cannot be
|
|
92
|
+
`ready` with `previewBudgetStatus: fail`. A draft may be `ready` with
|
|
93
|
+
`previewBudgetStatus: warn` only when the warning is explicit, usually because
|
|
94
|
+
the user prefers blank-line rhythm, and the receipt includes a compact fallback.
|
|
74
95
|
|
|
75
96
|
## Simplifier / Concrete-Language Audit
|
|
76
97
|
|
|
@@ -45,6 +45,11 @@ message assets through Sellable MCP tools:
|
|
|
45
45
|
After candidate generation and revision, and before returning `ready`, load
|
|
46
46
|
`create-campaign-v2-validation` through Sellable MCP as the final gate.
|
|
47
47
|
|
|
48
|
+
Both live and dry runs must produce or validate `message-validation.md` with
|
|
49
|
+
the same quality gates. The selected winner must pass the `Concrete Language Audit`:
|
|
50
|
+
identify and replace or cut abstract verbs, abstract nouns, abstract adjectives,
|
|
51
|
+
abstract adverbs, and cliches before the message is treated as ready.
|
|
52
|
+
|
|
48
53
|
Never reconstruct that branch from `brief.md`, `lead-review.md`,
|
|
49
54
|
`lead-sample.json`, `lead-filter.md`, `message-validation.md`, local files, or
|
|
50
55
|
direct database reads. If any required message asset cannot be loaded through MCP
|