@sellable/mcp 0.1.259 → 0.1.261

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 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?.topPostsForLLM)
91
- ? response.topPostsForLLM
92
- : Array.isArray(response?.posts)
93
- ? response.posts
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
- // Sort by total engagement desc for a predictable shortlist.
143
- kept.sort((a, b) => b.engagement.total - a.engagement.total);
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
  };
@@ -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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sellable/mcp",
3
- "version": "0.1.259",
3
+ "version": "0.1.261",
4
4
  "type": "module",
5
5
  "description": "Sellable MCP server for Claude Code and Codex campaign workflows",
6
6
  "main": "dist/index.js",
@@ -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
 
@@ -283,6 +284,173 @@ Space benchmark research:
283
284
  7. For each approved tracked person, call `mcp__sellable__upsert_engage_tracked_person`.
284
285
 
285
286
  The approved pack is capped at 20 gold standards. If adding new approved examples would exceed 20, ask which existing item to replace or skip the overflow. Candidate lists can be longer, but approved saved standards cannot exceed 20.
287
+
288
+ ## Visible Whole-Flow Debug Mode
289
+
290
+ Use when the user asks to see the whole flow, run the flow step by step,
291
+ validate that the workflow is working, debug a bad output, explain why a draft
292
+ is bad, or otherwise asks for the process to be explicit.
293
+
294
+ This mode exists because a validation receipt hidden inside a saved draft is not
295
+ enough. The user must be able to inspect the reasoning path before the system
296
+ claims the workflow worked.
297
+
298
+ In visible whole-flow debug mode:
299
+
300
+ 1. Run the normal create-post workflow.
301
+ 2. Show a `Visible Flow Trace` with every checkpoint below.
302
+ 3. Mark each checkpoint `pass`, `weak`, `fail`, or `blocked`.
303
+ 4. Include the concrete output from that checkpoint, not only a label.
304
+ 5. If any checkpoint is weak or failed, name the exact quality break and the
305
+ downstream effect on the draft.
306
+ 6. Do not call the run successful just because files were saved.
307
+ 7. Do not hide premise cards, source-template selection, hook candidates,
308
+ pre-draft structure, body-expression candidates, or validation findings only
309
+ inside the saved artifact.
310
+
311
+ The trace must use this order:
312
+
313
+ ```text
314
+ Visible Flow Trace
315
+
316
+ 0. Bootstrap
317
+ - auth/workspace:
318
+ - canonical prompt loaded:
319
+ - required assets loaded:
320
+ - memory loaded:
321
+ - package/version notes:
322
+
323
+ 1. Raw Idea Capture
324
+ - idea id:
325
+ - raw source preserved:
326
+ - distilled brief:
327
+ - claims added or avoided:
328
+
329
+ 2. Research Search Log
330
+ - search window:
331
+ - keywords:
332
+ - result count:
333
+ - full-text fetches:
334
+ - search gaps:
335
+
336
+ 3. Weighted Source Selection
337
+ - kept sources:
338
+ - rejected sources:
339
+ - keeper scores:
340
+ - lead-magnet / engagement-bait penalties:
341
+ - why the top source actually fits:
342
+
343
+ 4. Hook Autopsies
344
+ - source hook:
345
+ - rendered/mobile preview or preview-basis:
346
+ - see-more tension:
347
+ - curiosity debt:
348
+ - body promise:
349
+ - why it works:
350
+ - why it may not transfer:
351
+
352
+ 5. Post Positioning Breakdowns
353
+ - source:
354
+ - positioning sequence:
355
+ - line-level narrative techniques:
356
+ - reusable template lines:
357
+ - adaptation guards:
358
+
359
+ 6. Viral-Post Outlines
360
+ - source template:
361
+ - hook job:
362
+ - see-more trigger:
363
+ - beat sequence:
364
+ - body payoff:
365
+ - close job:
366
+
367
+ 7. Market Belief Map
368
+ - resonating ideas:
369
+ - implicit beliefs:
370
+ - wants / resentments / fears:
371
+ - selected controversy:
372
+ - why this user can credibly say it:
373
+ - what not to claim:
374
+
375
+ 8. Premise Cards
376
+ - 3-5 cards:
377
+ - story/scene:
378
+ - tension:
379
+ - reader value:
380
+ - proof available:
381
+ - proof missing:
382
+ - source-template fit:
383
+ - score:
384
+ - selected premise:
385
+
386
+ 9. Source Template Selection
387
+ - selected template:
388
+ - positioning sequence to borrow:
389
+ - hook move to borrow:
390
+ - body outline to borrow:
391
+ - required user proof:
392
+ - forbidden borrowing:
393
+ - no-template rationale if rejected:
394
+
395
+ 10. Hook Lab
396
+ - at least 12 hooks:
397
+ - rendered preview record or explicit preview-basis:
398
+ - hook-to-body promise:
399
+ - score:
400
+ - selected hook:
401
+ - rejected winning-looking hooks and why:
402
+
403
+ 11. Pre-Draft Narrative Outline
404
+ - hierarchical outline shown before draft:
405
+ - I. hook and click debt:
406
+ - II. thesis the post will defend:
407
+ - III. operating model:
408
+ - IV. body shape borrowed from posts that worked:
409
+ - V. narrative beats:
410
+ - VI. scan path and proof safety:
411
+ - user corrections applied before prose:
412
+
413
+ 12. Body Expression Lab
414
+ - 5-8 body-expression candidates:
415
+ - best lines:
416
+ - weak lines:
417
+ - hook promise repayment:
418
+ - proof risk:
419
+ - score:
420
+ - combined body plan:
421
+
422
+ 13. Draft
423
+ - draft body:
424
+ - lines intentionally copied from user source:
425
+ - outside-source wording copied: yes/no:
426
+ - known weak lines:
427
+
428
+ 14. Validation And Save
429
+ - ready status:
430
+ - proof gate:
431
+ - voice gate:
432
+ - anti-AI gate:
433
+ - mobile preview gate:
434
+ - template adaptation gate:
435
+ - hook-to-body repayment:
436
+ - quality break, if any:
437
+ - saved idea path:
438
+ - saved research path:
439
+ - saved draft path:
440
+ ```
441
+
442
+ Quality-break rules:
443
+
444
+ - If the draft is bad, say which checkpoint produced the bad draft. For example:
445
+ weak raw story, wrong selected source template, hook promise not repaid,
446
+ body expression lab too generic, proof missing, or voice mismatch.
447
+ - A draft with a coherent validation receipt but poor body copy is not a
448
+ successful run. Mark it `needs_revision` and show the checkpoint where the
449
+ body lost the premise.
450
+ - If the source idea is too abstract, stop at premise development or save a
451
+ `needs_revision` draft only after showing the missing story/proof.
452
+ - If the user is judging the workflow itself, prefer showing the trace over
453
+ optimizing for a polished final post.
286
454
  </modes>
287
455
 
288
456
  <pipeline>
@@ -320,6 +488,12 @@ The research worker must return a compact packet only:
320
488
  - market belief map: resonating ideas, implicit beliefs, audience wants, resentments, fears, and credible controversy angles
321
489
  - premise inputs: real scenes, observed tensions, reader value openings, and proof gaps
322
490
  - exact phrase patterns and sentence shapes
491
+ - post positioning breakdowns that label each meaningful line or phrase by
492
+ narrative job and category
493
+ - viral-post outlines for the best source posts
494
+ - line-to-template conversions that turn source structures into reusable
495
+ templates without source wording
496
+ - hook-to-body promise maps that show how each hook tells the body
323
497
  - body structures and exact body language moves
324
498
  - preview measurements
325
499
  - track-person and gold-standard recommendations
@@ -343,7 +517,15 @@ Default flow:
343
517
  10. For story posts, extract the story mechanism that made the post work, not just the first line.
344
518
  11. Extract hook structures plus specific reusable words, phrases, sentence
345
519
  shapes, transitions, and body language patterns.
346
- 12. Save the research with `mcp__sellable__save_hook_research`.
520
+ 12. Create a post positioning breakdown for each keeper post: line/phrase,
521
+ category, narrative technique, tension created, reader question opened,
522
+ proof dependency, and reusable template line.
523
+ 13. Convert each keeper into a viral-post outline: hook job, see-more trigger,
524
+ body payoff, close job, and beat-by-beat narrative structure.
525
+ 14. Convert the best outlines into reusable post templates with positioning
526
+ sequences, required story/proof inputs, forbidden borrowing, and
527
+ Sellable-specific adaptation instructions.
528
+ 15. Save the research with `mcp__sellable__save_hook_research`.
347
529
 
348
530
  Record provenance:
349
531
 
@@ -362,6 +544,10 @@ Record provenance:
362
544
  - market belief map and selected controversy
363
545
  - premise cards and selected premise
364
546
  - exact phrase patterns and sentence shapes
547
+ - post positioning breakdowns
548
+ - viral-post outlines
549
+ - reusable post templates
550
+ - hook-to-body promise maps
365
551
  - body structures and body language patterns
366
552
  - why each pattern fits the user's idea and voice
367
553
 
@@ -410,7 +596,10 @@ Hook patterns learned:
410
596
  1. full adapted hook block
411
597
  - source mechanism:
412
598
  - preview budget:
599
+ - see-more tension:
600
+ - curiosity debt:
413
601
  - internal question:
602
+ - hook-to-body promise:
414
603
  - why it fits / why it does not:
415
604
 
416
605
  Specific words and phrase shapes:
@@ -421,9 +610,35 @@ Specific words and phrase shapes:
421
610
  - adapted Sellable form:
422
611
  - do not copy:
423
612
 
613
+ Post positioning breakdown templates:
614
+ 1. source + template name
615
+ - positioning sequence:
616
+ - line-level narrative techniques:
617
+ - tension created:
618
+ - reader questions opened:
619
+ - reusable template lines:
620
+ - Sellable adaptation:
621
+
622
+ Viral-post outlines:
623
+ 1. outline name
624
+ - hook job:
625
+ - see-more trigger:
626
+ - body payoff:
627
+ - close job:
628
+ - beat sequence:
629
+
630
+ Line-to-template conversion:
631
+ 1. source line/beat
632
+ - narrative job:
633
+ - template line shape:
634
+ - required user story/proof:
635
+ - forbidden borrowing:
636
+ - adapted Sellable expression:
637
+
424
638
  Body structures learned:
425
639
  1. structure name
426
640
  - source:
641
+ - positioning sequence:
427
642
  - sequence:
428
643
  - exact language moves:
429
644
  - adapted Sellable body move:
@@ -437,9 +652,9 @@ Save recommendations:
437
652
  - gold-standard candidates:
438
653
 
439
654
  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
655
+ 1. premise card + source template + hook block + viral outline + body structure
656
+ 2. premise card + source template + hook block + viral outline + body structure
657
+ 3. premise card + source template + hook block + viral outline + body structure
443
658
  ```
444
659
 
445
660
  Keep this report concise enough to read, but concrete enough that another agent
@@ -453,7 +668,9 @@ Generate 3-5 `Premise Card` candidates from the raw idea, market research, core
453
668
  memory, story/proof files, and current-session user feedback. Each card must
454
669
  include a real story/scene or observed pattern, target reader, common belief,
455
670
  contrarian truth, tension, reader value, proof available, proof missing, and a
456
- score.
671
+ score. Each card must also evaluate which source template or no-template path
672
+ fits, which positioning sequence to test, and how the hook promise will be
673
+ repaid in the body.
457
674
 
458
675
  Select the strongest premise before hook generation. The selected premise must
459
676
  pass:
@@ -465,12 +682,103 @@ pass:
465
682
  - `credible_speaker`
466
683
  - `proof_safety`
467
684
  - `market_heat`
685
+ - `template_fit`
686
+ - `hook_to_body_repayment`
468
687
 
469
688
  If the idea has market heat but no real scene, ask for the missing scene unless
470
689
  the user explicitly requested an immediate draft. For immediate draft mode, use
471
690
  only source-backed observed patterns and save the draft as `needs_revision`
472
691
  unless the premise still has concrete reader value.
473
692
 
693
+ ## Step 1.9: Pre-Draft Narrative Outline
694
+
695
+ Before generating final draft prose, create and show a compact `Pre-Draft
696
+ Narrative Outline`. This is not a checklist. It is the user-visible argument
697
+ skeleton that lets the user confirm what the post will say, in what order, and
698
+ which proven body shapes will guide the draft before the system writes body
699
+ copy.
700
+
701
+ The outline must be concise, mobile-scanable, and concrete enough for the user
702
+ to approve or correct. Do not hide it inside the validation receipt after the
703
+ draft. Show it before writing the draft body unless the user explicitly says to
704
+ skip outline/structure and write immediately. Even when the user skips the
705
+ visible checkpoint, still include the outline in the validation receipt.
706
+
707
+ The `Pre-Draft Narrative Outline` must use hierarchical outline notation:
708
+ Roman numerals for major narrative beats, letters for sub-beats, and lowercase
709
+ roman numerals for proof/examples/body moves. Do not replace this with a flat
710
+ field list.
711
+
712
+ The `Pre-Draft Narrative Outline` must include:
713
+
714
+ ```text
715
+ Pre-Draft Narrative Outline
716
+ I. Hook and click debt
717
+ A. Selected hook:
718
+ B. Rendered mobile preview verdict:
719
+ C. What "see more" must repay:
720
+
721
+ II. Thesis the post will defend
722
+ A. One-sentence thesis:
723
+ B. Reader being taught:
724
+ C. Why this reader cares now:
725
+
726
+ III. Operating model
727
+ A. Core equation or mechanism:
728
+ B. Key definitions:
729
+ i. <term>: <plain definition + concrete examples>
730
+ ii. <term>: <plain definition + concrete examples>
731
+
732
+ IV. Body shape borrowed from posts that worked
733
+ A. Selected source template or no-template rationale:
734
+ B. Working body pattern(s) being adapted:
735
+ i. <pattern name>: <beat order and why it works>
736
+ ii. <pattern name>: <beat order and why it works>
737
+ C. What gets borrowed:
738
+ i. <narrative job, sequence, transition, or proof order>
739
+ D. What must not be copied:
740
+ i. <source wording, creator-specific proof, joke, or context>
741
+
742
+ V. Narrative beats
743
+ A. Beat 1: <job, reader state before/after, example/proof>
744
+ i. Line shape or section label:
745
+ ii. Concrete examples:
746
+ B. Beat 2: <job, reader state before/after, example/proof>
747
+ i. Line shape or section label:
748
+ ii. Concrete examples:
749
+ C. Beat 3: <job, reader state before/after, example/proof>
750
+ i. Line shape or section label:
751
+ ii. Concrete examples:
752
+
753
+ VI. Scan path and proof safety
754
+ A. Mobile scan path:
755
+ B. Proof claims:
756
+ i. <claim>: <source + public-safety status>
757
+ C. Abstractions to remove:
758
+ i. <abstract phrase> -> <concrete replacement>
759
+ D. Draft risks:
760
+ ```
761
+
762
+ Rules:
763
+
764
+ - The hook must already have a rendered mobile and desktop preview record.
765
+ - The thesis must be one sentence the post can defend.
766
+ - The reader must be specific enough to guide what gets cut.
767
+ - `Body shape borrowed from posts that worked` must name the body pattern or
768
+ explain why no-template is stronger. It must say what narrative job is being
769
+ borrowed, not just "make it like this creator."
770
+ - `Narrative beats` must show the order of the argument with `I.`, `A.`, and
771
+ `i.`-style hierarchy. A flat checklist fails this step.
772
+ - Key definitions must name concrete examples when the post teaches an
773
+ operating concept. For example, define `lead source` as how the list was
774
+ built, not as a persona label.
775
+ - `mobile scan path` must say what a reader understands if they read only the
776
+ hook, separators, section labels, numbers, and final line.
777
+ - `abstractions to remove` must list abstract phrases and the concrete words
778
+ that will replace them.
779
+ - If the user corrects the outline, update the outline first, then draft. Do
780
+ not patch the final prose while leaving the outline stale.
781
+
474
782
  ## Step 2: Hook Candidates
475
783
 
476
784
  Generate at least 12 hook candidates from the selected premise unless the user
@@ -528,7 +836,12 @@ Draft from:
528
836
  - exact raw idea
529
837
  - selected premise card
530
838
  - selected hook
839
+ - approved or latest `Pre-Draft Narrative Outline`
531
840
  - hook research artifact
841
+ - selected source template or no-template rationale
842
+ - viral-post outline
843
+ - post positioning breakdown template
844
+ - body expression candidates and combined body plan
532
845
  - user's core memory
533
846
  - story/proof files
534
847
  - post writing rules
@@ -549,11 +862,20 @@ Every saved draft needs a validation receipt with:
549
862
  - selected premise card
550
863
  - candidate hooks considered
551
864
  - selected hook and why
865
+ - pre-draft narrative outline
866
+ - selected source template and no-copy adaptation rationale
867
+ - post positioning breakdown
868
+ - viral-post outline
869
+ - hook-to-body promise map
870
+ - body expression candidates and combined body plan
552
871
  - proof claims used and source
553
872
  - story/proof files consulted
554
873
  - gold standards consulted
555
874
  - LinkedIn preview audit
556
875
  - premise/value audit findings
876
+ - mobile scanability audit findings
877
+ - template-adaptation audit findings
878
+ - abstraction-to-concrete rewrite audit findings
557
879
  - simplifier/concrete-language audit findings
558
880
  - voice audit findings
559
881
  - anti-AI audit findings
@@ -648,8 +970,15 @@ iteration:
648
970
  score: <compact score object>
649
971
  selected_premise: <premise or none>
650
972
  selected_hook: <hook>
973
+ pre_draft_narrative_outline: <compact I/A/i outline summary or none>
974
+ selected_source_template: <template name/source or none>
975
+ visible_flow_trace: <required when user asked for whole-flow/debug/step-by-step mode; include checkpoint statuses and quality break>
651
976
  validation_summary:
652
977
  premise_value: pass | needs_user_input | needs_revision
978
+ mobile_scanability: pass | needs_revision
979
+ template_adaptation: pass | needs_revision | blocked
980
+ abstraction_to_concrete: pass | needs_revision
981
+ hook_to_body_repayment: pass | needs_revision
653
982
  proof: pass | needs_user_input | blocked
654
983
  voice: pass | needs_revision
655
984
  anti_ai: pass | needs_revision