@sellable/mcp 0.1.259 → 0.1.260
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/index-dev.js +0 -0
- package/dist/index.js +0 -0
- package/dist/tools/engage-discovery.d.ts +24 -0
- package/dist/tools/engage-discovery.js +114 -9
- package/dist/tools/registry.d.ts +8 -0
- package/package.json +1 -1
- package/skills/create-post/SKILL.md +126 -5
- package/skills/create-post/references/hook-research-playbook.md +269 -5
- package/skills/create-post/references/linkedin-preview-rendering.md +176 -0
- package/skills/create-post/references/post-file-contract.md +13 -0
- package/skills/create-post/references/post-validation.md +129 -8
- package/skills/create-post/references/premise-development.md +250 -5
- package/skills/research/config.json +9 -0
package/dist/index-dev.js
CHANGED
|
File without changes
|
package/dist/index.js
CHANGED
|
File without changes
|
|
@@ -1,3 +1,14 @@
|
|
|
1
|
+
type FollowerBandFit = "in_target_band" | "below_target_band" | "above_target_band" | "unknown";
|
|
2
|
+
type ReachSignals = {
|
|
3
|
+
targetFollowerMin?: number;
|
|
4
|
+
targetFollowerMax?: number;
|
|
5
|
+
followerBandFit: FollowerBandFit;
|
|
6
|
+
engagementPer1kFollowers: number | null;
|
|
7
|
+
weightedEngagementPer1kFollowers: number | null;
|
|
8
|
+
reachPenaltyMultiplier: number;
|
|
9
|
+
reachAdjustedScore: number;
|
|
10
|
+
confidence: "high" | "medium" | "low";
|
|
11
|
+
};
|
|
1
12
|
export type EngagementPost = {
|
|
2
13
|
postId: string;
|
|
3
14
|
url: string;
|
|
@@ -7,6 +18,7 @@ export type EngagementPost = {
|
|
|
7
18
|
name: string;
|
|
8
19
|
headline: string;
|
|
9
20
|
profileUrl: string;
|
|
21
|
+
followerCount?: number;
|
|
10
22
|
};
|
|
11
23
|
engagement: {
|
|
12
24
|
likes: number;
|
|
@@ -14,6 +26,7 @@ export type EngagementPost = {
|
|
|
14
26
|
shares: number;
|
|
15
27
|
total: number;
|
|
16
28
|
};
|
|
29
|
+
reachSignals?: ReachSignals;
|
|
17
30
|
contentPreview: string;
|
|
18
31
|
};
|
|
19
32
|
export type SearchEngagementPostsInput = {
|
|
@@ -23,6 +36,8 @@ export type SearchEngagementPostsInput = {
|
|
|
23
36
|
minTotalEngagement?: number;
|
|
24
37
|
maxPosts?: number;
|
|
25
38
|
excludePostUrls?: string[];
|
|
39
|
+
targetFollowerMin?: number;
|
|
40
|
+
targetFollowerMax?: number;
|
|
26
41
|
};
|
|
27
42
|
export type SearchEngagementPostsResponse = {
|
|
28
43
|
success: boolean;
|
|
@@ -70,9 +85,18 @@ export declare const engageDiscoveryToolDefinitions: {
|
|
|
70
85
|
};
|
|
71
86
|
description: string;
|
|
72
87
|
};
|
|
88
|
+
targetFollowerMin: {
|
|
89
|
+
type: string;
|
|
90
|
+
description: string;
|
|
91
|
+
};
|
|
92
|
+
targetFollowerMax: {
|
|
93
|
+
type: string;
|
|
94
|
+
description: string;
|
|
95
|
+
};
|
|
73
96
|
};
|
|
74
97
|
required: string[];
|
|
75
98
|
additionalProperties: boolean;
|
|
76
99
|
};
|
|
77
100
|
}[];
|
|
78
101
|
export declare function searchEngagementPosts(input: SearchEngagementPostsInput): Promise<SearchEngagementPostsResponse>;
|
|
102
|
+
export {};
|
|
@@ -32,6 +32,14 @@ export const engageDiscoveryToolDefinitions = [
|
|
|
32
32
|
items: { type: "string" },
|
|
33
33
|
description: "Optional list of post URLs to exclude (e.g. already engaged/scheduled).",
|
|
34
34
|
},
|
|
35
|
+
targetFollowerMin: {
|
|
36
|
+
type: "number",
|
|
37
|
+
description: "Optional lower bound for creator follower count when reach-normalizing hook/source quality.",
|
|
38
|
+
},
|
|
39
|
+
targetFollowerMax: {
|
|
40
|
+
type: "number",
|
|
41
|
+
description: "Optional upper bound for creator follower count when reach-normalizing hook/source quality.",
|
|
42
|
+
},
|
|
35
43
|
},
|
|
36
44
|
required: ["keywords"],
|
|
37
45
|
additionalProperties: false,
|
|
@@ -63,6 +71,79 @@ function safeNumber(value) {
|
|
|
63
71
|
const n = typeof value === "number" ? value : Number(value);
|
|
64
72
|
return Number.isFinite(n) ? n : 0;
|
|
65
73
|
}
|
|
74
|
+
function round3(value) {
|
|
75
|
+
return Number(value.toFixed(3));
|
|
76
|
+
}
|
|
77
|
+
function parseFollowerCount(author) {
|
|
78
|
+
const direct = safeNumber(author?.followerCount);
|
|
79
|
+
if (direct > 0)
|
|
80
|
+
return direct;
|
|
81
|
+
const text = String(author?.followers || author?.followerCountText || "");
|
|
82
|
+
const match = text.match(/([\d,.]+)\s*([kKmM])?/);
|
|
83
|
+
if (!match)
|
|
84
|
+
return undefined;
|
|
85
|
+
const base = Number(match[1].replace(/,/g, ""));
|
|
86
|
+
if (!Number.isFinite(base) || base <= 0)
|
|
87
|
+
return undefined;
|
|
88
|
+
const suffix = match[2]?.toLowerCase();
|
|
89
|
+
if (suffix === "m")
|
|
90
|
+
return Math.round(base * 1_000_000);
|
|
91
|
+
if (suffix === "k")
|
|
92
|
+
return Math.round(base * 1_000);
|
|
93
|
+
return Math.round(base);
|
|
94
|
+
}
|
|
95
|
+
function reachPenaltyMultiplier(followerCount, targetFollowerMin, targetFollowerMax) {
|
|
96
|
+
if (!followerCount || !targetFollowerMin || !targetFollowerMax)
|
|
97
|
+
return 0.4;
|
|
98
|
+
if (followerCount >= targetFollowerMin && followerCount <= targetFollowerMax) {
|
|
99
|
+
return 1;
|
|
100
|
+
}
|
|
101
|
+
if (followerCount < targetFollowerMin)
|
|
102
|
+
return 0.75;
|
|
103
|
+
if (followerCount <= targetFollowerMax * 2)
|
|
104
|
+
return 0.65;
|
|
105
|
+
if (followerCount <= targetFollowerMax * 5)
|
|
106
|
+
return 0.35;
|
|
107
|
+
return 0.15;
|
|
108
|
+
}
|
|
109
|
+
function followerBandFit(followerCount, targetFollowerMin, targetFollowerMax) {
|
|
110
|
+
if (!followerCount || !targetFollowerMin || !targetFollowerMax) {
|
|
111
|
+
return "unknown";
|
|
112
|
+
}
|
|
113
|
+
if (followerCount >= targetFollowerMin && followerCount <= targetFollowerMax) {
|
|
114
|
+
return "in_target_band";
|
|
115
|
+
}
|
|
116
|
+
if (followerCount < targetFollowerMin)
|
|
117
|
+
return "below_target_band";
|
|
118
|
+
return "above_target_band";
|
|
119
|
+
}
|
|
120
|
+
function buildReachSignals(params) {
|
|
121
|
+
const { followerCount, likes, comments, shares, total } = params;
|
|
122
|
+
const hasTarget = Boolean(params.targetFollowerMin && params.targetFollowerMax);
|
|
123
|
+
if (!hasTarget && !followerCount)
|
|
124
|
+
return undefined;
|
|
125
|
+
const weightedEngagement = likes + comments * 4 + shares * 12;
|
|
126
|
+
const penalty = reachPenaltyMultiplier(followerCount, params.targetFollowerMin, params.targetFollowerMax);
|
|
127
|
+
const engagementPer1kFollowers = followerCount
|
|
128
|
+
? round3((total / followerCount) * 1000)
|
|
129
|
+
: null;
|
|
130
|
+
const weightedEngagementPer1kFollowers = followerCount
|
|
131
|
+
? round3((weightedEngagement / followerCount) * 1000)
|
|
132
|
+
: null;
|
|
133
|
+
const reachAdjustedScore = weightedEngagementPer1kFollowers === null
|
|
134
|
+
? 0
|
|
135
|
+
: round3(weightedEngagementPer1kFollowers * penalty);
|
|
136
|
+
return {
|
|
137
|
+
targetFollowerMin: params.targetFollowerMin,
|
|
138
|
+
targetFollowerMax: params.targetFollowerMax,
|
|
139
|
+
followerBandFit: followerBandFit(followerCount, params.targetFollowerMin, params.targetFollowerMax),
|
|
140
|
+
engagementPer1kFollowers,
|
|
141
|
+
weightedEngagementPer1kFollowers,
|
|
142
|
+
reachPenaltyMultiplier: penalty,
|
|
143
|
+
reachAdjustedScore,
|
|
144
|
+
confidence: followerCount ? (hasTarget ? "high" : "medium") : "low",
|
|
145
|
+
};
|
|
146
|
+
}
|
|
66
147
|
export async function searchEngagementPosts(input) {
|
|
67
148
|
const api = getApi();
|
|
68
149
|
const keywords = (input.keywords || [])
|
|
@@ -79,6 +160,12 @@ export async function searchEngagementPosts(input) {
|
|
|
79
160
|
const maxPosts = typeof input.maxPosts === "number" && input.maxPosts > 0
|
|
80
161
|
? Math.min(50, input.maxPosts)
|
|
81
162
|
: 25;
|
|
163
|
+
const targetFollowerMin = typeof input.targetFollowerMin === "number" && input.targetFollowerMin > 0
|
|
164
|
+
? input.targetFollowerMin
|
|
165
|
+
: undefined;
|
|
166
|
+
const targetFollowerMax = typeof input.targetFollowerMax === "number" && input.targetFollowerMax > 0
|
|
167
|
+
? input.targetFollowerMax
|
|
168
|
+
: undefined;
|
|
82
169
|
const exclude = new Set((input.excludePostUrls || [])
|
|
83
170
|
.map((u) => normalizePostUrl(u))
|
|
84
171
|
.filter(Boolean));
|
|
@@ -87,10 +174,10 @@ export async function searchEngagementPosts(input) {
|
|
|
87
174
|
keywords: keywords.map((keyword) => ({ keyword })),
|
|
88
175
|
page,
|
|
89
176
|
});
|
|
90
|
-
const rawPosts = Array.isArray(response?.
|
|
91
|
-
? response.
|
|
92
|
-
: Array.isArray(response?.
|
|
93
|
-
? response.
|
|
177
|
+
const rawPosts = Array.isArray(response?.posts)
|
|
178
|
+
? response.posts
|
|
179
|
+
: Array.isArray(response?.topPostsForLLM)
|
|
180
|
+
? response.topPostsForLLM
|
|
94
181
|
: [];
|
|
95
182
|
const now = Date.now();
|
|
96
183
|
const oldestMs = now - maxAgeDays * 24 * 60 * 60 * 1000;
|
|
@@ -121,6 +208,16 @@ export async function searchEngagementPosts(input) {
|
|
|
121
208
|
tooOld += 1;
|
|
122
209
|
continue;
|
|
123
210
|
}
|
|
211
|
+
const followerCount = parseFollowerCount(p?.author);
|
|
212
|
+
const reachSignals = buildReachSignals({
|
|
213
|
+
followerCount,
|
|
214
|
+
likes,
|
|
215
|
+
comments,
|
|
216
|
+
shares,
|
|
217
|
+
total,
|
|
218
|
+
targetFollowerMin,
|
|
219
|
+
targetFollowerMax,
|
|
220
|
+
});
|
|
124
221
|
kept.push({
|
|
125
222
|
postId: String(p?.id || ""),
|
|
126
223
|
url,
|
|
@@ -132,18 +229,26 @@ export async function searchEngagementPosts(input) {
|
|
|
132
229
|
name: String(p?.author?.name || ""),
|
|
133
230
|
headline: String(p?.author?.headline || ""),
|
|
134
231
|
profileUrl: String(p?.author?.profileUrl || ""),
|
|
232
|
+
...(followerCount ? { followerCount } : {}),
|
|
135
233
|
},
|
|
136
234
|
engagement: { likes, comments, shares, total },
|
|
235
|
+
...(reachSignals ? { reachSignals } : {}),
|
|
137
236
|
contentPreview: previewText(String(p?.content || ""), 220),
|
|
138
237
|
});
|
|
139
|
-
if (kept.length >= maxPosts)
|
|
140
|
-
break;
|
|
141
238
|
}
|
|
142
|
-
|
|
143
|
-
kept.sort((a, b) =>
|
|
239
|
+
const hasReachTarget = Boolean(targetFollowerMin && targetFollowerMax);
|
|
240
|
+
kept.sort((a, b) => {
|
|
241
|
+
if (hasReachTarget) {
|
|
242
|
+
const reachDelta = (b.reachSignals?.reachAdjustedScore || 0) -
|
|
243
|
+
(a.reachSignals?.reachAdjustedScore || 0);
|
|
244
|
+
if (reachDelta !== 0)
|
|
245
|
+
return reachDelta;
|
|
246
|
+
}
|
|
247
|
+
return b.engagement.total - a.engagement.total;
|
|
248
|
+
});
|
|
144
249
|
return {
|
|
145
250
|
success: true,
|
|
146
|
-
posts: kept,
|
|
251
|
+
posts: kept.slice(0, maxPosts),
|
|
147
252
|
totalReturned: rawPosts.length,
|
|
148
253
|
filteredOut: { tooOld, excluded, tooLowEngagement },
|
|
149
254
|
};
|
package/dist/tools/registry.d.ts
CHANGED
|
@@ -1749,6 +1749,14 @@ export declare const allTools: ({
|
|
|
1749
1749
|
};
|
|
1750
1750
|
description: string;
|
|
1751
1751
|
};
|
|
1752
|
+
targetFollowerMin: {
|
|
1753
|
+
type: string;
|
|
1754
|
+
description: string;
|
|
1755
|
+
};
|
|
1756
|
+
targetFollowerMax: {
|
|
1757
|
+
type: string;
|
|
1758
|
+
description: string;
|
|
1759
|
+
};
|
|
1752
1760
|
};
|
|
1753
1761
|
required: string[];
|
|
1754
1762
|
additionalProperties: boolean;
|
package/package.json
CHANGED
|
@@ -84,6 +84,7 @@ Before drafting, load all required assets with `mcp__sellable__get_subskill_asse
|
|
|
84
84
|
3. `subskillName: "create-post", assetPath: "references/premise-development.md"`
|
|
85
85
|
4. `subskillName: "create-post", assetPath: "references/post-validation.md"`
|
|
86
86
|
5. `subskillName: "create-post", assetPath: "references/gold-standard-post-pack.md"`
|
|
87
|
+
6. `subskillName: "create-post", assetPath: "references/linkedin-preview-rendering.md"`
|
|
87
88
|
|
|
88
89
|
If any required asset is missing, unreadable, truncated without continuation, or internally inconsistent, return:
|
|
89
90
|
|
|
@@ -320,6 +321,12 @@ The research worker must return a compact packet only:
|
|
|
320
321
|
- market belief map: resonating ideas, implicit beliefs, audience wants, resentments, fears, and credible controversy angles
|
|
321
322
|
- premise inputs: real scenes, observed tensions, reader value openings, and proof gaps
|
|
322
323
|
- exact phrase patterns and sentence shapes
|
|
324
|
+
- post positioning breakdowns that label each meaningful line or phrase by
|
|
325
|
+
narrative job and category
|
|
326
|
+
- viral-post outlines for the best source posts
|
|
327
|
+
- line-to-template conversions that turn source structures into reusable
|
|
328
|
+
templates without source wording
|
|
329
|
+
- hook-to-body promise maps that show how each hook tells the body
|
|
323
330
|
- body structures and exact body language moves
|
|
324
331
|
- preview measurements
|
|
325
332
|
- track-person and gold-standard recommendations
|
|
@@ -343,7 +350,15 @@ Default flow:
|
|
|
343
350
|
10. For story posts, extract the story mechanism that made the post work, not just the first line.
|
|
344
351
|
11. Extract hook structures plus specific reusable words, phrases, sentence
|
|
345
352
|
shapes, transitions, and body language patterns.
|
|
346
|
-
12.
|
|
353
|
+
12. Create a post positioning breakdown for each keeper post: line/phrase,
|
|
354
|
+
category, narrative technique, tension created, reader question opened,
|
|
355
|
+
proof dependency, and reusable template line.
|
|
356
|
+
13. Convert each keeper into a viral-post outline: hook job, see-more trigger,
|
|
357
|
+
body payoff, close job, and beat-by-beat narrative structure.
|
|
358
|
+
14. Convert the best outlines into reusable post templates with positioning
|
|
359
|
+
sequences, required story/proof inputs, forbidden borrowing, and
|
|
360
|
+
Sellable-specific adaptation instructions.
|
|
361
|
+
15. Save the research with `mcp__sellable__save_hook_research`.
|
|
347
362
|
|
|
348
363
|
Record provenance:
|
|
349
364
|
|
|
@@ -362,6 +377,10 @@ Record provenance:
|
|
|
362
377
|
- market belief map and selected controversy
|
|
363
378
|
- premise cards and selected premise
|
|
364
379
|
- exact phrase patterns and sentence shapes
|
|
380
|
+
- post positioning breakdowns
|
|
381
|
+
- viral-post outlines
|
|
382
|
+
- reusable post templates
|
|
383
|
+
- hook-to-body promise maps
|
|
365
384
|
- body structures and body language patterns
|
|
366
385
|
- why each pattern fits the user's idea and voice
|
|
367
386
|
|
|
@@ -410,7 +429,10 @@ Hook patterns learned:
|
|
|
410
429
|
1. full adapted hook block
|
|
411
430
|
- source mechanism:
|
|
412
431
|
- preview budget:
|
|
432
|
+
- see-more tension:
|
|
433
|
+
- curiosity debt:
|
|
413
434
|
- internal question:
|
|
435
|
+
- hook-to-body promise:
|
|
414
436
|
- why it fits / why it does not:
|
|
415
437
|
|
|
416
438
|
Specific words and phrase shapes:
|
|
@@ -421,9 +443,35 @@ Specific words and phrase shapes:
|
|
|
421
443
|
- adapted Sellable form:
|
|
422
444
|
- do not copy:
|
|
423
445
|
|
|
446
|
+
Post positioning breakdown templates:
|
|
447
|
+
1. source + template name
|
|
448
|
+
- positioning sequence:
|
|
449
|
+
- line-level narrative techniques:
|
|
450
|
+
- tension created:
|
|
451
|
+
- reader questions opened:
|
|
452
|
+
- reusable template lines:
|
|
453
|
+
- Sellable adaptation:
|
|
454
|
+
|
|
455
|
+
Viral-post outlines:
|
|
456
|
+
1. outline name
|
|
457
|
+
- hook job:
|
|
458
|
+
- see-more trigger:
|
|
459
|
+
- body payoff:
|
|
460
|
+
- close job:
|
|
461
|
+
- beat sequence:
|
|
462
|
+
|
|
463
|
+
Line-to-template conversion:
|
|
464
|
+
1. source line/beat
|
|
465
|
+
- narrative job:
|
|
466
|
+
- template line shape:
|
|
467
|
+
- required user story/proof:
|
|
468
|
+
- forbidden borrowing:
|
|
469
|
+
- adapted Sellable expression:
|
|
470
|
+
|
|
424
471
|
Body structures learned:
|
|
425
472
|
1. structure name
|
|
426
473
|
- source:
|
|
474
|
+
- positioning sequence:
|
|
427
475
|
- sequence:
|
|
428
476
|
- exact language moves:
|
|
429
477
|
- adapted Sellable body move:
|
|
@@ -437,9 +485,9 @@ Save recommendations:
|
|
|
437
485
|
- gold-standard candidates:
|
|
438
486
|
|
|
439
487
|
Recommended draft directions:
|
|
440
|
-
1. premise card + hook block + body structure
|
|
441
|
-
2. premise card + hook block + body structure
|
|
442
|
-
3. premise card + hook block + body structure
|
|
488
|
+
1. premise card + source template + hook block + viral outline + body structure
|
|
489
|
+
2. premise card + source template + hook block + viral outline + body structure
|
|
490
|
+
3. premise card + source template + hook block + viral outline + body structure
|
|
443
491
|
```
|
|
444
492
|
|
|
445
493
|
Keep this report concise enough to read, but concrete enough that another agent
|
|
@@ -453,7 +501,9 @@ Generate 3-5 `Premise Card` candidates from the raw idea, market research, core
|
|
|
453
501
|
memory, story/proof files, and current-session user feedback. Each card must
|
|
454
502
|
include a real story/scene or observed pattern, target reader, common belief,
|
|
455
503
|
contrarian truth, tension, reader value, proof available, proof missing, and a
|
|
456
|
-
score.
|
|
504
|
+
score. Each card must also evaluate which source template or no-template path
|
|
505
|
+
fits, which positioning sequence to test, and how the hook promise will be
|
|
506
|
+
repaid in the body.
|
|
457
507
|
|
|
458
508
|
Select the strongest premise before hook generation. The selected premise must
|
|
459
509
|
pass:
|
|
@@ -465,12 +515,63 @@ pass:
|
|
|
465
515
|
- `credible_speaker`
|
|
466
516
|
- `proof_safety`
|
|
467
517
|
- `market_heat`
|
|
518
|
+
- `template_fit`
|
|
519
|
+
- `hook_to_body_repayment`
|
|
468
520
|
|
|
469
521
|
If the idea has market heat but no real scene, ask for the missing scene unless
|
|
470
522
|
the user explicitly requested an immediate draft. For immediate draft mode, use
|
|
471
523
|
only source-backed observed patterns and save the draft as `needs_revision`
|
|
472
524
|
unless the premise still has concrete reader value.
|
|
473
525
|
|
|
526
|
+
## Step 1.9: Pre-Draft Structure Brief
|
|
527
|
+
|
|
528
|
+
Before generating final draft prose, create and show a compact `Pre-Draft
|
|
529
|
+
Structure Brief`. In other words, show a compact Pre-Draft Structure Brief
|
|
530
|
+
before the final draft body. This is the checkpoint that prevents the agent from
|
|
531
|
+
jumping from a good hook into a messy body.
|
|
532
|
+
|
|
533
|
+
The brief must be concise, mobile-scanable, and concrete enough for the user to
|
|
534
|
+
approve or correct. Do not hide it inside the validation receipt after the
|
|
535
|
+
draft. Show it before writing the draft body unless the user explicitly says to
|
|
536
|
+
skip outline/structure and write immediately. Even when the user skips the
|
|
537
|
+
visible checkpoint, still include the brief in the validation receipt.
|
|
538
|
+
|
|
539
|
+
The `Pre-Draft Structure Brief` must include:
|
|
540
|
+
|
|
541
|
+
```text
|
|
542
|
+
Pre-Draft Structure Brief
|
|
543
|
+
- hook:
|
|
544
|
+
- thesis:
|
|
545
|
+
- reader being taught:
|
|
546
|
+
- why this reader cares now:
|
|
547
|
+
- core equation or mechanism:
|
|
548
|
+
- key definitions:
|
|
549
|
+
- proof claims:
|
|
550
|
+
- proof source / risk:
|
|
551
|
+
- source template or no-template rationale:
|
|
552
|
+
- mobile scan path:
|
|
553
|
+
- section outline:
|
|
554
|
+
- body promise after see more:
|
|
555
|
+
- concrete examples to include:
|
|
556
|
+
- abstractions to remove:
|
|
557
|
+
- draft risks:
|
|
558
|
+
```
|
|
559
|
+
|
|
560
|
+
Rules:
|
|
561
|
+
|
|
562
|
+
- The hook must already have a rendered mobile and desktop preview record.
|
|
563
|
+
- The thesis must be one sentence the post can defend.
|
|
564
|
+
- The reader must be specific enough to guide what gets cut.
|
|
565
|
+
- Key definitions must name concrete examples when the post teaches an
|
|
566
|
+
operating concept. For example, define `lead source` as how the list was
|
|
567
|
+
built, not as a persona label.
|
|
568
|
+
- `mobile scan path` must say what a reader understands if they read only the
|
|
569
|
+
hook, separators, section labels, numbers, and final line.
|
|
570
|
+
- `abstractions to remove` must list abstract phrases and the concrete words
|
|
571
|
+
that will replace them.
|
|
572
|
+
- If the user corrects the brief, update the brief first, then draft. Do not
|
|
573
|
+
patch the final prose while leaving the brief stale.
|
|
574
|
+
|
|
474
575
|
## Step 2: Hook Candidates
|
|
475
576
|
|
|
476
577
|
Generate at least 12 hook candidates from the selected premise unless the user
|
|
@@ -528,7 +629,12 @@ Draft from:
|
|
|
528
629
|
- exact raw idea
|
|
529
630
|
- selected premise card
|
|
530
631
|
- selected hook
|
|
632
|
+
- approved or latest `Pre-Draft Structure Brief`
|
|
531
633
|
- hook research artifact
|
|
634
|
+
- selected source template or no-template rationale
|
|
635
|
+
- viral-post outline
|
|
636
|
+
- post positioning breakdown template
|
|
637
|
+
- body expression candidates and combined body plan
|
|
532
638
|
- user's core memory
|
|
533
639
|
- story/proof files
|
|
534
640
|
- post writing rules
|
|
@@ -549,11 +655,20 @@ Every saved draft needs a validation receipt with:
|
|
|
549
655
|
- selected premise card
|
|
550
656
|
- candidate hooks considered
|
|
551
657
|
- selected hook and why
|
|
658
|
+
- pre-draft structure brief
|
|
659
|
+
- selected source template and no-copy adaptation rationale
|
|
660
|
+
- post positioning breakdown
|
|
661
|
+
- viral-post outline
|
|
662
|
+
- hook-to-body promise map
|
|
663
|
+
- body expression candidates and combined body plan
|
|
552
664
|
- proof claims used and source
|
|
553
665
|
- story/proof files consulted
|
|
554
666
|
- gold standards consulted
|
|
555
667
|
- LinkedIn preview audit
|
|
556
668
|
- premise/value audit findings
|
|
669
|
+
- mobile scanability audit findings
|
|
670
|
+
- template-adaptation audit findings
|
|
671
|
+
- abstraction-to-concrete rewrite audit findings
|
|
557
672
|
- simplifier/concrete-language audit findings
|
|
558
673
|
- voice audit findings
|
|
559
674
|
- anti-AI audit findings
|
|
@@ -648,8 +763,14 @@ iteration:
|
|
|
648
763
|
score: <compact score object>
|
|
649
764
|
selected_premise: <premise or none>
|
|
650
765
|
selected_hook: <hook>
|
|
766
|
+
pre_draft_structure_brief: <compact structure summary or none>
|
|
767
|
+
selected_source_template: <template name/source or none>
|
|
651
768
|
validation_summary:
|
|
652
769
|
premise_value: pass | needs_user_input | needs_revision
|
|
770
|
+
mobile_scanability: pass | needs_revision
|
|
771
|
+
template_adaptation: pass | needs_revision | blocked
|
|
772
|
+
abstraction_to_concrete: pass | needs_revision
|
|
773
|
+
hook_to_body_repayment: pass | needs_revision
|
|
653
774
|
proof: pass | needs_user_input | blocked
|
|
654
775
|
voice: pass | needs_revision
|
|
655
776
|
anti_ai: pass | needs_revision
|