@sellable/mcp 0.1.262 → 0.1.264
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/server.js +7 -1
- package/dist/tools/content-posts.d.ts +421 -1
- package/dist/tools/content-posts.js +802 -0
- package/dist/tools/registry.d.ts +68 -0
- package/package.json +1 -1
- package/skills/create-post/SKILL.md +35 -11
- package/skills/create-post/references/hook-research-playbook.md +27 -11
- package/skills/create-post/references/linkedin-preview-rendering.md +58 -13
- package/skills/create-post/references/post-validation.md +27 -9
package/dist/tools/registry.d.ts
CHANGED
|
@@ -1260,6 +1260,19 @@ export declare const allTools: ({
|
|
|
1260
1260
|
body?: undefined;
|
|
1261
1261
|
validationReceipt?: undefined;
|
|
1262
1262
|
status?: undefined;
|
|
1263
|
+
postText?: undefined;
|
|
1264
|
+
sourceLabel?: undefined;
|
|
1265
|
+
measurementMode?: undefined;
|
|
1266
|
+
requireBrowserMeasurement?: undefined;
|
|
1267
|
+
clampLines?: undefined;
|
|
1268
|
+
mobileClampLines?: undefined;
|
|
1269
|
+
desktopClampLines?: undefined;
|
|
1270
|
+
mobileTextWidthPx?: undefined;
|
|
1271
|
+
desktopTextWidthPx?: undefined;
|
|
1272
|
+
renderId?: undefined;
|
|
1273
|
+
renderScreenshots?: undefined;
|
|
1274
|
+
requireScreenshots?: undefined;
|
|
1275
|
+
reviewClampLines?: undefined;
|
|
1263
1276
|
updatedAt?: undefined;
|
|
1264
1277
|
publishUrl?: undefined;
|
|
1265
1278
|
activityId?: undefined;
|
|
@@ -1295,6 +1308,7 @@ export declare const allTools: ({
|
|
|
1295
1308
|
properties: {
|
|
1296
1309
|
draftId: {
|
|
1297
1310
|
type: string;
|
|
1311
|
+
description?: undefined;
|
|
1298
1312
|
};
|
|
1299
1313
|
ideaId: {
|
|
1300
1314
|
type: string;
|
|
@@ -1339,6 +1353,19 @@ export declare const allTools: ({
|
|
|
1339
1353
|
selectedPatterns?: undefined;
|
|
1340
1354
|
previewBudget?: undefined;
|
|
1341
1355
|
notes?: undefined;
|
|
1356
|
+
postText?: undefined;
|
|
1357
|
+
sourceLabel?: undefined;
|
|
1358
|
+
measurementMode?: undefined;
|
|
1359
|
+
requireBrowserMeasurement?: undefined;
|
|
1360
|
+
clampLines?: undefined;
|
|
1361
|
+
mobileClampLines?: undefined;
|
|
1362
|
+
desktopClampLines?: undefined;
|
|
1363
|
+
mobileTextWidthPx?: undefined;
|
|
1364
|
+
desktopTextWidthPx?: undefined;
|
|
1365
|
+
renderId?: undefined;
|
|
1366
|
+
renderScreenshots?: undefined;
|
|
1367
|
+
requireScreenshots?: undefined;
|
|
1368
|
+
reviewClampLines?: undefined;
|
|
1342
1369
|
updatedAt?: undefined;
|
|
1343
1370
|
publishUrl?: undefined;
|
|
1344
1371
|
activityId?: undefined;
|
|
@@ -1361,6 +1388,7 @@ export declare const allTools: ({
|
|
|
1361
1388
|
properties: {
|
|
1362
1389
|
draftId: {
|
|
1363
1390
|
type: string;
|
|
1391
|
+
description?: undefined;
|
|
1364
1392
|
};
|
|
1365
1393
|
hookResearchId: {
|
|
1366
1394
|
type: string;
|
|
@@ -1403,6 +1431,19 @@ export declare const allTools: ({
|
|
|
1403
1431
|
previewBudget?: undefined;
|
|
1404
1432
|
notes?: undefined;
|
|
1405
1433
|
createdAt?: undefined;
|
|
1434
|
+
postText?: undefined;
|
|
1435
|
+
sourceLabel?: undefined;
|
|
1436
|
+
measurementMode?: undefined;
|
|
1437
|
+
requireBrowserMeasurement?: undefined;
|
|
1438
|
+
clampLines?: undefined;
|
|
1439
|
+
mobileClampLines?: undefined;
|
|
1440
|
+
desktopClampLines?: undefined;
|
|
1441
|
+
mobileTextWidthPx?: undefined;
|
|
1442
|
+
desktopTextWidthPx?: undefined;
|
|
1443
|
+
renderId?: undefined;
|
|
1444
|
+
renderScreenshots?: undefined;
|
|
1445
|
+
requireScreenshots?: undefined;
|
|
1446
|
+
reviewClampLines?: undefined;
|
|
1406
1447
|
publishUrl?: undefined;
|
|
1407
1448
|
activityId?: undefined;
|
|
1408
1449
|
publishedAt?: undefined;
|
|
@@ -1424,6 +1465,7 @@ export declare const allTools: ({
|
|
|
1424
1465
|
properties: {
|
|
1425
1466
|
draftId: {
|
|
1426
1467
|
type: string;
|
|
1468
|
+
description?: undefined;
|
|
1427
1469
|
};
|
|
1428
1470
|
publishUrl: {
|
|
1429
1471
|
type: string;
|
|
@@ -1464,6 +1506,19 @@ export declare const allTools: ({
|
|
|
1464
1506
|
body?: undefined;
|
|
1465
1507
|
validationReceipt?: undefined;
|
|
1466
1508
|
status?: undefined;
|
|
1509
|
+
postText?: undefined;
|
|
1510
|
+
sourceLabel?: undefined;
|
|
1511
|
+
measurementMode?: undefined;
|
|
1512
|
+
requireBrowserMeasurement?: undefined;
|
|
1513
|
+
clampLines?: undefined;
|
|
1514
|
+
mobileClampLines?: undefined;
|
|
1515
|
+
desktopClampLines?: undefined;
|
|
1516
|
+
mobileTextWidthPx?: undefined;
|
|
1517
|
+
desktopTextWidthPx?: undefined;
|
|
1518
|
+
renderId?: undefined;
|
|
1519
|
+
renderScreenshots?: undefined;
|
|
1520
|
+
requireScreenshots?: undefined;
|
|
1521
|
+
reviewClampLines?: undefined;
|
|
1467
1522
|
updatedAt?: undefined;
|
|
1468
1523
|
publishedPostId?: undefined;
|
|
1469
1524
|
year?: undefined;
|
|
@@ -1516,6 +1571,19 @@ export declare const allTools: ({
|
|
|
1516
1571
|
body?: undefined;
|
|
1517
1572
|
validationReceipt?: undefined;
|
|
1518
1573
|
status?: undefined;
|
|
1574
|
+
postText?: undefined;
|
|
1575
|
+
sourceLabel?: undefined;
|
|
1576
|
+
measurementMode?: undefined;
|
|
1577
|
+
requireBrowserMeasurement?: undefined;
|
|
1578
|
+
clampLines?: undefined;
|
|
1579
|
+
mobileClampLines?: undefined;
|
|
1580
|
+
desktopClampLines?: undefined;
|
|
1581
|
+
mobileTextWidthPx?: undefined;
|
|
1582
|
+
desktopTextWidthPx?: undefined;
|
|
1583
|
+
renderId?: undefined;
|
|
1584
|
+
renderScreenshots?: undefined;
|
|
1585
|
+
requireScreenshots?: undefined;
|
|
1586
|
+
reviewClampLines?: undefined;
|
|
1519
1587
|
updatedAt?: undefined;
|
|
1520
1588
|
publishUrl?: undefined;
|
|
1521
1589
|
activityId?: undefined;
|
package/package.json
CHANGED
|
@@ -108,6 +108,8 @@ Use these MCP tools when available:
|
|
|
108
108
|
- `mcp__sellable__get_post_idea`
|
|
109
109
|
- `mcp__sellable__list_post_ideas`
|
|
110
110
|
- `mcp__sellable__save_hook_research`
|
|
111
|
+
- `mcp__sellable__calculate_linkedin_hook_preview`
|
|
112
|
+
- `mcp__sellable__render_linkedin_post_preview`
|
|
111
113
|
- `mcp__sellable__save_post_draft`
|
|
112
114
|
- `mcp__sellable__update_post_draft`
|
|
113
115
|
- `mcp__sellable__list_post_draft_iterations`
|
|
@@ -342,7 +344,9 @@ Visible Flow Trace
|
|
|
342
344
|
|
|
343
345
|
4. Hook Autopsies
|
|
344
346
|
- source hook:
|
|
345
|
-
-
|
|
347
|
+
- calculated mobile/desktop visible blocks from
|
|
348
|
+
`calculate_linkedin_hook_preview` or authenticated LinkedIn screenshot:
|
|
349
|
+
- optional visual artifact from `render_linkedin_post_preview`:
|
|
346
350
|
- see-more tension:
|
|
347
351
|
- curiosity debt:
|
|
348
352
|
- body promise:
|
|
@@ -402,7 +406,8 @@ Visible Flow Trace
|
|
|
402
406
|
|
|
403
407
|
11. Hook Lab
|
|
404
408
|
- at least 12 hooks:
|
|
405
|
-
-
|
|
409
|
+
- calculated preview from `calculate_linkedin_hook_preview`:
|
|
410
|
+
- optional visual artifact from `render_linkedin_post_preview` for finalists:
|
|
406
411
|
- hook-to-body promise:
|
|
407
412
|
- score:
|
|
408
413
|
- selected hook:
|
|
@@ -829,17 +834,36 @@ Each hook must include:
|
|
|
829
834
|
|
|
830
835
|
Do not copy source wording. Copy only the structure.
|
|
831
836
|
|
|
832
|
-
Use
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
837
|
+
Use the rendered-preview contract from
|
|
838
|
+
`references/linkedin-preview-rendering.md`. LinkedIn does not publish exact
|
|
839
|
+
"see more" cutoff rules, and rendering varies by device, app version, font,
|
|
840
|
+
media, and line break. Treat character counts as diagnostics only, not as proof
|
|
841
|
+
that the hook will render before "see more."
|
|
842
|
+
|
|
843
|
+
The selected hook and top candidates must include literal mobile and desktop
|
|
844
|
+
visible blocks from `mcp__sellable__calculate_linkedin_hook_preview`.
|
|
845
|
+
Observed LinkedIn screenshots and current third-party preview tools support a
|
|
846
|
+
line-count model: review the first 3 rendered visual lines, not the first 210
|
|
847
|
+
characters. Blank lines and `--` separators consume visible preview lines.
|
|
848
|
+
Use `mcp__sellable__render_linkedin_post_preview` only when a visual QA artifact
|
|
849
|
+
is useful.
|
|
850
|
+
|
|
851
|
+
Use:
|
|
852
|
+
|
|
853
|
+
- `pass`: rendered mobile preview shows the pain, proof, or curiosity by the end
|
|
854
|
+
of the first 3 rendered lines, and either the core point is visible or a
|
|
855
|
+
specific intentional open loop is visible with immediate body payoff planned
|
|
856
|
+
- `warn`: rendered mobile preview creates useful curiosity but wrapping,
|
|
857
|
+
blank-line rhythm, media risk, or one missing context word weakens it; include
|
|
858
|
+
a compact fallback
|
|
859
|
+
- `fail`: the hook's real point appears after the rendered mobile review clamp,
|
|
860
|
+
the visible open loop is vague, blank lines consume the preview before the
|
|
861
|
+
point, or desktop fit is the only reason it looks good
|
|
840
862
|
|
|
841
863
|
Desktop preview usually has more room. Still record `desktopPreviewFit`, but
|
|
842
|
-
never let desktop fit compensate for a mobile `fail`.
|
|
864
|
+
never let desktop fit compensate for a mobile `fail`. Do not tell the user "we
|
|
865
|
+
know" how LinkedIn will render unless there is an authenticated LinkedIn
|
|
866
|
+
screenshot; say it passes the renderer and show the visible blocks.
|
|
843
867
|
|
|
844
868
|
If a hook's point depends on text after the likely preview, rewrite it before
|
|
845
869
|
selecting it. A selected hook may carry a `warn` only when the warning is about
|
|
@@ -256,19 +256,33 @@ Measure the visible opening for every shortlisted source post before extracting
|
|
|
256
256
|
the hook pattern. This makes the study useful for LinkedIn, not just generally
|
|
257
257
|
"good writing."
|
|
258
258
|
|
|
259
|
-
LinkedIn does not publish exact "see more" cutoff rules. Treat
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
-
|
|
268
|
-
|
|
259
|
+
LinkedIn does not publish exact "see more" cutoff rules. Treat preview fit as a
|
|
260
|
+
rendered-line problem first, not a fixed character-count rule. Current
|
|
261
|
+
third-party preview tools and 2026 creator references generally cluster around:
|
|
262
|
+
|
|
263
|
+
- mobile feed: about 2-3 visible text lines, often around 140 characters when
|
|
264
|
+
there is no media
|
|
265
|
+
- desktop feed: about 3-4 visible text lines, often around 210 characters when
|
|
266
|
+
there is no media
|
|
267
|
+
- media posts can show fewer text lines before truncation
|
|
268
|
+
- blank lines and `--` style separators consume visible preview lines
|
|
269
|
+
|
|
270
|
+
Use `references/linkedin-preview-rendering.md` as the required gate. Character
|
|
271
|
+
budgets are diagnostics only:
|
|
272
|
+
|
|
273
|
+
- `pass`: rendered mobile preview shows the pain, proof, or curiosity by the end
|
|
274
|
+
of the first 3 rendered lines, and the hook creates a specific click reason
|
|
275
|
+
- `warn`: rendered mobile preview creates useful curiosity but loses one useful
|
|
276
|
+
context word, wraps awkwardly, or spends a visible line on blank space or a
|
|
277
|
+
separator; include a compact fallback
|
|
278
|
+
- `fail`: the hook's real point appears after the rendered mobile review clamp,
|
|
279
|
+
the visible open loop is vague, blank lines consume the preview before the
|
|
280
|
+
point, or desktop fit is the only reason it looks good
|
|
269
281
|
|
|
270
282
|
Desktop preview has more room, so record it separately, but never let desktop
|
|
271
|
-
fit compensate for a mobile `fail`.
|
|
283
|
+
fit compensate for a mobile `fail`. Never say "we know" how LinkedIn will render
|
|
284
|
+
the hook unless there is an authenticated LinkedIn screenshot. Say the hook
|
|
285
|
+
`passes the renderer` and show the mobile/desktop visible blocks.
|
|
272
286
|
|
|
273
287
|
For each source, record:
|
|
274
288
|
|
|
@@ -282,6 +296,8 @@ For each source, record:
|
|
|
282
296
|
- `firstTwoContentLinesChars`
|
|
283
297
|
- `longestNonblankLineChars`
|
|
284
298
|
- `blankLineCountBeforeFold`
|
|
299
|
+
- `renderedPreview`: literal mobile and desktop visible blocks from
|
|
300
|
+
`references/linkedin-preview-rendering.md`
|
|
285
301
|
- `mobilePreviewBudget`: `pass`, `warn`, or `fail`
|
|
286
302
|
- `desktopPreviewBudget`: `pass`, `warn`, or `fail`
|
|
287
303
|
- `blankLineVisualRisk`
|
|
@@ -5,14 +5,56 @@ research, hook candidate generation, gold-standard decomposition, and draft
|
|
|
5
5
|
validation.
|
|
6
6
|
|
|
7
7
|
Character budgets are only diagnostics. They are not the preview gate. A hook is
|
|
8
|
-
not studied, selected, or ready until
|
|
9
|
-
or
|
|
8
|
+
not studied, selected, or ready until its visible mobile and desktop blocks come
|
|
9
|
+
from `mcp__sellable__calculate_linkedin_hook_preview` or from a stricter
|
|
10
|
+
authenticated LinkedIn screenshot.
|
|
11
|
+
|
|
12
|
+
Do not let the LLM imagine wrapping. The normal path is a function call that
|
|
13
|
+
returns rendered mobile and desktop lines. HTML and PNG outputs from
|
|
14
|
+
`mcp__sellable__render_linkedin_post_preview` are optional visual QA artifacts,
|
|
15
|
+
not the primary interface.
|
|
10
16
|
|
|
11
17
|
## Rendering Basis
|
|
12
18
|
|
|
13
19
|
LinkedIn does not publish exact feed truncation rules, and rendering can vary by
|
|
14
20
|
surface, app version, device, media attachment, and account state. Use this
|
|
15
|
-
deterministic
|
|
21
|
+
deterministic function as the MCP review gate for unpublished drafts.
|
|
22
|
+
|
|
23
|
+
Simple operating policy: judge the hook by the first 3 rendered lines. Use ~140
|
|
24
|
+
characters on mobile and ~210 characters on desktop as guardrails only. If the
|
|
25
|
+
3-line preview and the character guardrail disagree, trust the rendered lines.
|
|
26
|
+
|
|
27
|
+
Call:
|
|
28
|
+
|
|
29
|
+
```json
|
|
30
|
+
mcp__sellable__calculate_linkedin_hook_preview({
|
|
31
|
+
"postText": "<full post text or candidate hook block>",
|
|
32
|
+
"sourceLabel": "<draft id, hook id, or source label>",
|
|
33
|
+
"measurementMode": "browser"
|
|
34
|
+
})
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
When local Chrome is available, browser measurement is the preferred functional
|
|
38
|
+
basis because it asks the browser layout engine to wrap the text under the
|
|
39
|
+
contract. When Chrome is unavailable, the tool falls back to the deterministic
|
|
40
|
+
width model and must mark that basis.
|
|
41
|
+
|
|
42
|
+
Treat "see more" as a rendered-line problem first and a character-count problem
|
|
43
|
+
second. Observed LinkedIn screenshots show the current desktop feed behaving
|
|
44
|
+
like a first-3-rendered-lines clamp: a first line, a blank line, and a third
|
|
45
|
+
line can be all that appears before `... more`.
|
|
46
|
+
|
|
47
|
+
- review gate: first 3 rendered visual lines
|
|
48
|
+
- mobile feed: tighter because the text column is narrower
|
|
49
|
+
- desktop feed: more characters can fit per line, but still review line count
|
|
50
|
+
- posts with media may show fewer text lines before truncation
|
|
51
|
+
- blank lines and intentional separators consume visible preview lines
|
|
52
|
+
- device width, font scaling, app version, browser, and profile/page context can
|
|
53
|
+
move the cutoff
|
|
54
|
+
|
|
55
|
+
Because of that variation, never say a hook is guaranteed to render before
|
|
56
|
+
"see more." Say `pass`, `warn`, or `fail` under the rendered-preview contract,
|
|
57
|
+
and show the visible mobile and desktop blocks.
|
|
16
58
|
|
|
17
59
|
When an authenticated LinkedIn feed/composer/browser screenshot is available,
|
|
18
60
|
that screenshot is the strongest evidence. Still record the fields below so
|
|
@@ -48,13 +90,16 @@ selected hook must include a `renderedPreview` record:
|
|
|
48
90
|
|
|
49
91
|
```text
|
|
50
92
|
renderedPreview:
|
|
51
|
-
basis:
|
|
52
|
-
cssContractVersion: linkedin-preview-rendering/
|
|
93
|
+
basis: linkedin_rendering_rule_function | authenticated_linkedin_screenshot | manual_user_source
|
|
94
|
+
cssContractVersion: linkedin-preview-rendering/v2
|
|
53
95
|
mobile:
|
|
54
96
|
textWidthPx: 308
|
|
55
97
|
fontSizePx: 14
|
|
56
98
|
lineHeightPx: 21
|
|
57
|
-
|
|
99
|
+
clampLines: 3
|
|
100
|
+
charGuardrail: 140
|
|
101
|
+
measurementBasis: local_chrome_browser_layout | estimated_width_model
|
|
102
|
+
visibleTextBlock: <literal first 3 rendered lines>
|
|
58
103
|
renderedLines:
|
|
59
104
|
- <line 1 exactly as wrapped>
|
|
60
105
|
- <line 2 exactly as wrapped>
|
|
@@ -68,12 +113,14 @@ renderedPreview:
|
|
|
68
113
|
payoffPlannedImmediatelyAfterClamp: true | false
|
|
69
114
|
seeMoreClickReason: <why a reader would click see more>
|
|
70
115
|
seeMoreRisk: pass | warn | fail
|
|
71
|
-
screenshotPath: <optional local path>
|
|
72
116
|
desktop:
|
|
73
117
|
textWidthPx: 582
|
|
74
118
|
fontSizePx: 14
|
|
75
119
|
lineHeightPx: 21
|
|
76
|
-
|
|
120
|
+
clampLines: 3
|
|
121
|
+
charGuardrail: 210
|
|
122
|
+
measurementBasis: local_chrome_browser_layout | estimated_width_model
|
|
123
|
+
visibleTextBlock: <literal first 3 rendered lines>
|
|
77
124
|
renderedLines:
|
|
78
125
|
- <line 1 exactly as wrapped>
|
|
79
126
|
- <line 2 exactly as wrapped>
|
|
@@ -83,7 +130,6 @@ renderedPreview:
|
|
|
83
130
|
corePainProofOrCuriosityVisible: true | false
|
|
84
131
|
corePointVisible: true | false
|
|
85
132
|
seeMoreRisk: pass | warn | fail
|
|
86
|
-
screenshotPath: <optional local path>
|
|
87
133
|
diagnostics:
|
|
88
134
|
charCount: <number>
|
|
89
135
|
charCountIncludingNewlines: <number>
|
|
@@ -96,10 +142,9 @@ renderedPreview:
|
|
|
96
142
|
rewriteIfTruncated: <short fallback>
|
|
97
143
|
```
|
|
98
144
|
|
|
99
|
-
If a host cannot produce
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
passed preview validation from character counts alone.
|
|
145
|
+
If a host cannot produce literal wrapped line blocks from
|
|
146
|
+
`calculate_linkedin_hook_preview`, return `blocked` or `needs_revision`; do not
|
|
147
|
+
claim the hook passed preview validation from character counts alone.
|
|
103
148
|
|
|
104
149
|
## Study Rules
|
|
105
150
|
|
|
@@ -320,22 +320,38 @@ proof from another creator, save as `needs_revision`.
|
|
|
320
320
|
|
|
321
321
|
## LinkedIn Preview Audit
|
|
322
322
|
|
|
323
|
-
Audit the selected hook and top candidates against
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
is a mobile-first safety gate, not a claim about an
|
|
323
|
+
Audit the selected hook and top candidates against the rendered-preview
|
|
324
|
+
contract. LinkedIn does not publish exact "see more" cutoff rules, and rendering
|
|
325
|
+
varies by device, app version, font, media, line break, and whether the post has
|
|
326
|
+
media attached. This audit is a mobile-first safety gate, not a claim about an
|
|
327
|
+
official LinkedIn limit.
|
|
328
|
+
|
|
329
|
+
Treat character counts as diagnostics only. The gate is the literal rendered
|
|
330
|
+
mobile and desktop visible block from `references/linkedin-preview-rendering.md`
|
|
331
|
+
or an authenticated LinkedIn screenshot.
|
|
327
332
|
|
|
328
333
|
Use:
|
|
329
334
|
|
|
330
|
-
- `pass`:
|
|
331
|
-
|
|
332
|
-
|
|
335
|
+
- `pass`: the rendered mobile preview shows the pain, proof, or curiosity by the
|
|
336
|
+
end of the first 3 rendered lines, and either the core point is visible or a
|
|
337
|
+
specific intentional open loop is visible with immediate body payoff planned
|
|
338
|
+
- `warn`: the rendered mobile preview creates useful curiosity but wrapping,
|
|
339
|
+
blank-line rhythm, media risk, or one missing context word weakens it; include
|
|
340
|
+
a compact fallback
|
|
341
|
+
- `fail`: the hook's real point appears after the rendered mobile review clamp,
|
|
342
|
+
the visible open loop is vague, blank lines consume the preview before the
|
|
343
|
+
point, or desktop fit is the only reason it looks good
|
|
333
344
|
|
|
334
345
|
Desktop preview usually has more room. Still record desktop fit, but never let
|
|
335
346
|
desktop fit compensate for a mobile `fail`.
|
|
336
347
|
|
|
337
348
|
Record:
|
|
338
349
|
|
|
350
|
+
- `renderedPreview`
|
|
351
|
+
- `renderedPreviewBasis`: `linkedin_css_contract` |
|
|
352
|
+
`authenticated_linkedin_screenshot` | `manual_user_source`
|
|
353
|
+
- literal mobile visible block
|
|
354
|
+
- literal desktop visible block
|
|
339
355
|
- `charCount`
|
|
340
356
|
- `charCountIncludingNewlines`
|
|
341
357
|
- `firstLineChars`
|
|
@@ -355,9 +371,11 @@ Record:
|
|
|
355
371
|
- `compactFallback` when `previewBudgetStatus` is `warn`
|
|
356
372
|
|
|
357
373
|
If the hook only works after likely truncation, rewrite it. A draft cannot be
|
|
358
|
-
`ready` with `previewBudgetStatus: fail
|
|
374
|
+
`ready` with `previewBudgetStatus: fail`, with no rendered preview block, or
|
|
375
|
+
with only a character-count claim. A draft may be `ready` with
|
|
359
376
|
`previewBudgetStatus: warn` only when the warning is explicit, usually because
|
|
360
|
-
the user prefers blank-line rhythm,
|
|
377
|
+
the user prefers blank-line rhythm, separators, or a deliberate open loop, and
|
|
378
|
+
the receipt includes a compact fallback.
|
|
361
379
|
|
|
362
380
|
## Simplifier / Concrete-Language Audit
|
|
363
381
|
|