From 1d985991d19d38f3617d1466049f35e248168feb Mon Sep 17 00:00:00 2001 From: Kjell Tore Guttormsen Date: Thu, 18 Jun 2026 12:19:48 +0200 Subject: [PATCH] =?UTF-8?q?chore(linkedin-studio):=20M0-13=20=E2=80=94=204?= =?UTF-8?q?=20D2=20templates=20+=20scrub=20leak=20+=20scaffold=20fallback?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../demographics-template.md | 211 ++++++++++++++ .../engagement-patterns-template.md | 267 ++++++++++++++++++ .../high-engagement-posts-template.md | 60 ++++ .../templates/my-post-templates-template.md | 249 ++++++++++++++++ docs/m0/log.md | 59 ++++ .../__tests__/personalization-score.test.mjs | 50 ++++ 6 files changed, 896 insertions(+) create mode 100644 assets/audience-insights/demographics-template.md create mode 100644 assets/audience-insights/engagement-patterns-template.md create mode 100644 assets/examples/high-engagement-posts-template.md create mode 100644 assets/templates/my-post-templates-template.md create mode 100644 docs/m0/log.md diff --git a/assets/audience-insights/demographics-template.md b/assets/audience-insights/demographics-template.md new file mode 100644 index 0000000..e1d1ea3 --- /dev/null +++ b/assets/audience-insights/demographics-template.md @@ -0,0 +1,211 @@ +# Audience Demographics + +Track WHO is actually engaging with your content. LinkedIn Analytics provides this data for free - use it to understand your real audience vs. your intended audience. + +## How to Access This Data + +1. Go to LinkedIn Analytics: https://www.linkedin.com/analytics/ +2. Click on any post +3. Navigate to "Demographics" tab +4. Review data monthly and update this file + +--- + +## Current Demographics (Last Updated: [Date]) + +### Industries (Top 10) + +Based on LinkedIn Analytics → Post Analytics → Demographics + +| Rank | Industry | % of Engagement | Trend | +|------|----------|----------------|--------| +| 1 | [Industry name] | [X]% | [↑/→/↓] | +| 2 | [Industry name] | [X]% | [↑/→/↓] | +| 3 | [Industry name] | [X]% | [↑/→/↓] | +| 4 | [Industry name] | [X]% | [↑/→/↓] | +| 5 | [Industry name] | [X]% | [↑/→/↓] | +| 6 | [Industry name] | [X]% | [↑/→/↓] | +| 7 | [Industry name] | [X]% | [↑/→/↓] | +| 8 | [Industry name] | [X]% | [↑/→/↓] | +| 9 | [Industry name] | [X]% | [↑/→/↓] | +| 10 | [Industry name] | [X]% | [↑/→/↓] | + +**Key insights:** +- [Observation 1 - e.g., "60% from government sector, higher than expected"] +- [Observation 2 - e.g., "Tech companies underrepresented vs. my assumptions"] +- [Implication - e.g., "Should increase public sector case studies"] + +--- + +### Job Functions (Top 10) + +| Rank | Function | % of Engagement | Trend | +|------|----------|----------------|--------| +| 1 | [Function] | [X]% | [↑/→/↓] | +| 2 | [Function] | [X]% | [↑/→/↓] | +| 3 | [Function] | [X]% | [↑/→/↓] | +| 4 | [Function] | [X]% | [↑/→/↓] | +| 5 | [Function] | [X]% | [↑/→/↓] | +| 6 | [Function] | [X]% | [↑/→/↓] | +| 7 | [Function] | [X]% | [↑/→/↓] | +| 8 | [Function] | [X]% | [↑/→/↓] | +| 9 | [Function] | [X]% | [↑/→/↓] | +| 10 | [Function] | [X]% | [↑/→/↓] | + +**Key insights:** +- [Who is actually engaging] +- [Implication for content framing] + +--- + +### Seniority Levels + +| Level | % of Engagement | Change vs. Last Month | +|-------|----------------|----------------------| +| Entry level | [X]% | [+/-X%] | +| Individual contributor | [X]% | [+/-X%] | +| Manager | [X]% | [+/-X%] | +| Director | [X]% | [+/-X%] | +| VP | [X]% | [+/-X%] | +| C-level | [X]% | [+/-X%] | +| Owner/Partner | [X]% | [+/-X%] | + +**Key insights:** +- **Dominant level:** [Which level engages most] +- **Decision-maker presence:** [% at Director+ level] +- **Content implication:** [How technical/strategic should content be?] + +--- + +### Geographic Distribution (Top 10 Countries) + +| Rank | Country | % of Engagement | Trend | +|------|---------|----------------|--------| +| 1 | [Country] | [X]% | [↑/→/↓] | +| 2 | [Country] | [X]% | [↑/→/↓] | +| 3 | [Country] | [X]% | [↑/→/↓] | +| 4 | [Country] | [X]% | [↑/→/↓] | +| 5 | [Country] | [X]% | [↑/→/↓] | +| 6 | [Country] | [X]% | [↑/→/↓] | +| 7 | [Country] | [X]% | [↑/→/↓] | +| 8 | [Country] | [X]% | [↑/→/↓] | +| 9 | [Country] | [X]% | [↑/→/↓] | +| 10 | [Country] | [X]% | [↑/→/↓] | + +**Key insights:** +- **Primary market:** [Where most engagement comes from] +- **Time zone implications:** [Optimal posting times] +- **Regional context:** [Does content need localization?] + +--- + +### Company Size (Of Engagers) + +| Size | % of Engagement | Trend | +|------|----------------|--------| +| 1-10 employees | [X]% | [↑/→/↓] | +| 11-50 | [X]% | [↑/→/↓] | +| 51-200 | [X]% | [↑/→/↓] | +| 201-500 | [X]% | [↑/→/↓] | +| 501-1000 | [X]% | [↑/→/↓] | +| 1001-5000 | [X]% | [↑/→/↓] | +| 5001-10000 | [X]% | [↑/→/↓] | +| 10000+ | [X]% | [↑/→/↓] | + +**Key insights:** +- **Dominant segment:** [Enterprise/Mid-market/SMB] +- **Content implication:** [Scale of examples, budget assumptions] +- **Opportunity:** [Underserved segment to target] + +--- + +## Intended vs. Actual Audience + +### Who I Thought My Audience Was +- **Industries:** [Your original assumptions] +- **Roles:** [Your original assumptions] +- **Seniority:** [Your original assumptions] +- **Geography:** [Your original assumptions] + +### Who My Audience Actually Is +- **Industries:** [Reality from data above] +- **Roles:** [Reality from data above] +- **Seniority:** [Reality from data above] +- **Geography:** [Reality from data above] + +### Strategic Implications + +**Content adjustments needed:** +1. [Adjustment 1 - e.g., "Increase public sector examples, decrease startup references"] +2. [Adjustment 2 - e.g., "Frame for Director-level, not just technical ICs"] +3. [Adjustment 3 - e.g., "Add European regulatory context"] + +**Opportunities identified:** +1. [Opportunity 1 - e.g., "Large enterprise segment underserved by competitors"] +2. [Opportunity 2 - e.g., "Growing Nordic audience interested in topic X"] + +--- + +## Follower vs. Engager Analysis + +**Important distinction:** +- Your followers = who follows you +- Your engagers = who actually interacts with content + +Often these are different groups. LinkedIn prioritizes showing your content to engagers, not just followers. + +### Follower Demographics +[If you have LinkedIn Premium, note follower demographics here] +- [Key differences from engager demographics] + +### Insight +[What the difference between followers and engagers tells you] + +--- + +## Competitive Audience Analysis + +How does your audience compare to key competitors/peers? + +| Peer | Their Primary Industry | Their Seniority Level | Difference from Mine | +|------|----------------------|---------------------|---------------------| +| [Name] | [Industry] | [Level] | [What's different] | +| [Name] | [Industry] | [Level] | [What's different] | +| [Name] | [Industry] | [Level] | [What's different] | + +**Content gap opportunity:** +[Where your unique audience positioning creates content opportunities] + +--- + +## Month-over-Month Trends + +### [Current Month] vs. [Previous Month] + +**Industry shifts:** +- [What changed and why] + +**Seniority shifts:** +- [What changed and why] + +**Geographic shifts:** +- [What changed and why] + +**Analysis:** +[What these trends indicate about content resonance and audience evolution] + +--- + +## Update Schedule + +- **Monthly:** Update all demographics from LinkedIn Analytics +- **Quarterly:** Deep analysis of trends and strategic implications +- **Yearly:** Major review of intended vs. actual audience fit + +--- + +## Update Log + +- **[Date]:** Initial demographics captured +- **[Date]:** Observed [significant change] in [demographic category] +- **[Date]:** Shifted content strategy based on [insight] diff --git a/assets/audience-insights/engagement-patterns-template.md b/assets/audience-insights/engagement-patterns-template.md new file mode 100644 index 0000000..fd261d8 --- /dev/null +++ b/assets/audience-insights/engagement-patterns-template.md @@ -0,0 +1,267 @@ +# My Audience Engagement Patterns + +Track YOUR audience's specific behaviors and preferences here. This data is more valuable than generic "best practices" because it's based on YOUR actual results. + +## Update Frequency + +**Weekly (5 minutes):** Update posting times and add best-performing topic from the week +**Monthly (15 minutes):** Deep dive into patterns, update demographics, analyze format performance + +--- + +## Best Posting Times (Based on MY Data) + +**Important:** These should be YOUR times based on YOUR analytics, not generic advice. Track this in LinkedIn Analytics under "Post impressions by time of day." + +### Primary Posting Windows +1. **[Day] at [Time]:** Avg. impressions: [X] | Avg. engagement: [Y] + - Why this works: [e.g., "My audience (public sector leaders) checks LinkedIn during lunch break"] + +2. **[Day] at [Time]:** Avg. impressions: [X] | Avg. engagement: [Y] + - Why this works: [Your analysis] + +3. **[Day] at [Time]:** Avg. impressions: [X] | Avg. engagement: [Y] + - Why this works: [Your analysis] + +### Worst Posting Times (To Avoid) +- [Day/Time]: [Why it underperforms for YOUR audience] +- [Day/Time]: [Why it underperforms for YOUR audience] + +**Update Log:** +- [Date]: [Change observed - e.g., "Tuesday 2pm now outperforms Friday 8am"] + +--- + +## Top-Performing Topics (Last 90 Days) + +Track which topics YOUR audience actually engages with, not what you think they should care about. + +1. **[Topic]:** Avg. engagement: [X] | Posts: [Y] + - Best-performing post example: [Brief description] + - Why it resonates: [Your analysis] + +2. **[Topic]:** Avg. engagement: [X] | Posts: [Y] + - Best-performing post example: [Brief description] + - Why it resonates: [Your analysis] + +3. **[Topic]:** Avg. engagement: [X] | Posts: [Y] + - Best-performing post example: [Brief description] + - Why it resonates: [Your analysis] + +### Topics That Surprisingly Underperformed +- **[Topic]:** [Why you thought it would work] → [Why it didn't] +- **[Topic]:** [Analysis] + +**Implication for content strategy:** +[What you'll do differently based on this data] + +--- + +## Format Performance (MY Audience) + +Based on YOUR analytics, not generic benchmarks. Track in LinkedIn Analytics and your own spreadsheet. + +### Format Rankings (By Engagement) +1. **[Format - e.g., "Story-based posts"]:** + - Avg. impressions: [X] + - Avg. engagement rate: [Y%] + - Best time to post: [When] + - Character sweet spot: [Range] + +2. **[Format - e.g., "Framework posts"]:** + - Avg. impressions: [X] + - Avg. engagement rate: [Y%] + - Best time to post: [When] + - Character sweet spot: [Range] + +3. **[Format - e.g., "Data/research posts"]:** + - [Same metrics] + +4. **[Format - e.g., "Case study posts"]:** + - [Same metrics] + +### Visual Content Performance +- **Posts with images:** Avg. engagement: [X] vs text-only: [Y] +- **Posts with documents:** Avg. engagement: [X] +- **Posts with carousels:** Avg. engagement: [X] +- **Video posts:** Avg. engagement: [X] + +**Your insights:** +[What format performs best for YOUR audience and why] + +--- + +## Hook Types That Work for ME + +Not all hook styles work for all audiences. Track which hooks YOUR audience responds to. + +### Top-Performing Hook Styles +1. **[Hook type - e.g., "Counterintuitive stat"]** + - Example: [Actual hook you used] + - Avg. engagement: [X] + - Why it works for your audience: [Analysis] + +2. **[Hook type - e.g., "Bold contrarian statement"]** + - Example: [Actual hook] + - Avg. engagement: [X] + - Why it works: [Analysis] + +3. **[Hook type - e.g., "Personal story opening"]** + - Example: [Actual hook] + - Avg. engagement: [X] + - Why it works: [Analysis] + +### Hook Styles That Don't Work for YOUR Audience +- **[Hook type]:** [Why it underperforms with your specific audience] +- **[Hook type]:** [Why it underperforms] + +--- + +## CTA Performance Analysis + +Which calls-to-action actually drive engagement from YOUR audience? + +### High-Performing CTAs +1. **[CTA type - e.g., "Specific implementation question"]** + - Example: "Which stage is your organization in?" + - Avg. comments generated: [X] + +2. **[CTA type]** + - Example: [Actual CTA] + - Avg. comments generated: [X] + +### Low-Performing CTAs (To Avoid) +- **[CTA type]:** [Why YOUR audience doesn't respond to this] + +--- + +## Audience Demographics (Who Actually Engages) + +Based on LinkedIn Analytics → Analytics → Demographics of people who interacted with your posts + +### Industries (Top 5) +1. [Industry]: [% of engagement] +2. [Industry]: [% of engagement] +3. [Industry]: [% of engagement] +4. [Industry]: [% of engagement] +5. [Industry]: [% of engagement] + +**Insight:** [What this means for content focus] + +### Job Functions (Top 5) +1. [Function]: [% of engagement] +2. [Function]: [% of engagement] +3. [Function]: [% of engagement] +4. [Function]: [% of engagement] +5. [Function]: [% of engagement] + +**Insight:** [How this should shape your content] + +### Seniority Levels +- C-level: [%] +- VP/Director: [%] +- Manager: [%] +- Individual contributor: [%] +- Entry level: [%] + +**Insight:** [Technical depth and framing implications] + +### Geographic Distribution (Top 5 Countries) +1. [Country]: [%] +2. [Country]: [%] +3. [Country]: [%] +4. [Country]: [%] +5. [Country]: [%] + +**Insight:** [Time zone and regional context considerations] + +### Company Size (Of Engagers) +- 1-10 employees: [%] +- 11-50: [%] +- 51-200: [%] +- 201-500: [%] +- 501-1000: [%] +- 1001-5000: [%] +- 5001-10000: [%] +- 10000+: [%] + +**Insight:** [Scale and organizational context implications] + +--- + +## Content Length Performance (YOUR Data) + +Track the optimal length for YOUR audience, not generic advice. + +- **800-1000 characters:** Avg. engagement: [X] +- **1000-1200 characters:** Avg. engagement: [X] +- **1200-1500 characters:** Avg. engagement: [X] +- **1500-1900 characters:** Avg. engagement: [X] +- **1900+ characters:** Avg. engagement: [X] + +**Your sweet spot:** [Range that consistently performs best] +**Why:** [Your analysis of why this works for your audience] + +--- + +## Engagement Velocity Patterns + +How quickly does YOUR content gain traction? + +### First Hour Performance +- **Average engagement in first 60 minutes:** [X] likes, [Y] comments +- **Threshold for algorithm boost:** [Based on your data, when does reach accelerate?] +- **Your current hit rate:** [% of posts that hit the threshold] + +### 24-Hour Patterns +- **Most engagement happens in:** [Time window - e.g., "First 3 hours"] +- **Secondary surge times:** [If applicable] +- **Typical engagement curve:** [Description of how your posts perform over 24 hours] + +--- + +## Strategic Insights (The "So What") + +Based on all the data above, what should you do differently? + +### Content Strategy Adjustments +1. **More of this:** [What data says you should double down on] +2. **Less of this:** [What data says isn't working] +3. **Test this:** [New hypotheses based on patterns] + +### Audience Alignment +- **Who you thought your audience was:** [Original assumption] +- **Who actually engages:** [Reality based on data] +- **Strategic implication:** [How content should shift] + +### Competitive Edge Opportunities +Based on YOUR unique audience makeup: +- **Gap 1:** [Underserved need you could fill] +- **Gap 2:** [Content angle competitors miss] +- **Gap 3:** [Format opportunity] + +--- + +## Monthly Comparison + +Track month-over-month to see if patterns are stable or shifting. + +### [Current Month] +- Avg. impressions per post: [X] +- Avg. engagement per post: [Y] +- Follower growth: [+X] +- Best-performing topic: [Topic] +- Best-performing format: [Format] + +### [Previous Month] +- [Same metrics for comparison] + +**Key changes:** [What's different and why] + +--- + +## Update Log + +- **[Date]:** [Significant finding - e.g., "Discovered Thursday posts now outperform Tuesday"] +- **[Date]:** [Pattern shift - e.g., "Framework posts have overtaken story posts in engagement"] +- **[Date]:** [Audience insight - e.g., "Realize 60% of engagers are from enterprise, not SMB"] diff --git a/assets/examples/high-engagement-posts-template.md b/assets/examples/high-engagement-posts-template.md new file mode 100644 index 0000000..0da1b71 --- /dev/null +++ b/assets/examples/high-engagement-posts-template.md @@ -0,0 +1,60 @@ +# High-Engagement Posts Collection + +Store your top-performing posts here for pattern analysis. Add 5-10 of your best posts to identify what consistently works for YOUR audience. + +> **Placeholder seed.** Your real, per-user collection lives in your external data +> dir (`~/.claude/linkedin-studio/examples/high-engagement-posts.md`). Replace the +> example structure below with your own posts. + +## How to Use This File + +After each successful post (high engagement relative to your baseline): + +1. Add a new `## Post N` section (where N is a number) per saved post +2. Note engagement metrics and timing +3. Analyze WHY it worked (hook, angle, timing, CTA) +4. Document the replicable pattern + +Claude studies these to learn your successful patterns and apply them to new content. + +## Entry Format + +Each saved post is one `## Post N` section with these fields: + +- **Posted:** date, time, timezone +- **Engagement:** likes / comments / shares +- **Reach:** impressions and engagement rate +- **The Post:** the full post text +- **Why It Worked:** hook, angle, timing, CTA +- **Pattern to Replicate:** the elements you want to reuse + +(Add your first `## Post N` section above this line once you have a high performer.) + +## Patterns Across All High-Performing Posts + +**Common Elements:** + +- [ ] [Element you notice across your best posts] +- [ ] [Element 2] + +**Audience Preferences (What YOUR Audience Responds To):** + +- Format: [Your best-performing format] +- Length: [Your best-performing length] +- Tone: [Your best-performing tone] +- CTAs: [What drives replies for you] + +**Topics That Resonate:** + +1. [Topic] +2. [Topic] + +**Best Posting Times (Based on YOUR Data):** + +- Primary: [Time] +- Secondary: [Time] +- Avoid: [Time] + +## Update Log + +- [Date]: [What you added or learned] diff --git a/assets/templates/my-post-templates-template.md b/assets/templates/my-post-templates-template.md new file mode 100644 index 0000000..a0b03a4 --- /dev/null +++ b/assets/templates/my-post-templates-template.md @@ -0,0 +1,249 @@ +# My Custom Post Templates + +Save your proven post structures here. When you find a format that works consistently, document it so Claude can replicate the pattern. + +--- + +## Template 1: [Name - e.g., "My Framework Introduction Template"] + +**When to use:** [e.g., "When introducing a new framework or model I've developed"] + +**Structure:** + +``` +[HOOK - Counterintuitive stat or bold statement] +(1-2 lines, <110 characters) + +[CONTEXT - The problem this framework solves] +(2-3 lines explaining why people struggle) + +[FRAMEWORK INTRODUCTION] +"I developed [Framework Name] to solve this." +(Brief one-line description) + +[COMPONENT BREAKDOWN] +Stage 1: [Name] +→ [Key characteristic in one line] + +Stage 2: [Name] +→ [Key characteristic in one line] + +Stage 3: [Name] +→ [Key characteristic in one line] + +[IMPLICATION] +"Most organizations are stuck at Stage 1. +Here's what moving to Stage 2 unlocks..." +(2-3 lines on practical value) + +[CTA] +"Which stage is your organization in?" +``` + +**Why this works for me:** +- [Reason 1 - e.g., "My audience loves actionable frameworks"] +- [Reason 2 - e.g., "The diagnostic question always generates 15+ comments"] + +**Example posts using this template:** +- [Link to post 1] +- [Link to post 2] + +**Average engagement:** [Metrics] + +--- + +## Template 2: [Name - e.g., "My Before/After Transformation Story"] + +**When to use:** [e.g., "When sharing case study or project results"] + +**Structure:** + +``` +[HOOK - The transformation in numbers] +"6 months ago: [painful metric] +Today: [improved metric]" + +[THE BEFORE] +[Organization] was struggling with [specific problem]. +(Paint picture of pain - 3-4 lines) + +[THE TURNING POINT] +We decided to [key decision]. +Most teams choose [alternative]. Here's why we didn't... + +[THE APPROACH] +"Three things mattered: +• [Element 1] +• [Element 2] +• [Element 3]" + +[THE AFTER] +Results: +→ [Metric 1]: [Before] → [After] +→ [Metric 2]: [Before] → [After] +→ [Metric 3]: [Before] → [After] + +[KEY LESSON] +"The real breakthrough wasn't [expected thing]. +It was [non-obvious insight]." + +[CTA] +"What's been YOUR biggest lesson in [topic]?" +``` + +**Why this works for me:** +- [Reason 1] +- [Reason 2] + +**Average engagement:** [Metrics] + +--- + +## Template 3: [Name - e.g., "My Contrarian Take"] + +**When to use:** [e.g., "When challenging conventional wisdom in my field"] + +**Structure:** + +``` +[HOOK - Bold contrarian statement] +"Everyone says [conventional wisdom]. +I think that's wrong." + +[THE CONVENTIONAL APPROACH] +Most [target audience] believe [common belief]. +(Explain the mainstream view fairly - 2-3 lines) + +[WHY IT FAILS] +But here's the problem... +(2-3 specific reasons with examples) + +[THE ALTERNATIVE] +Instead, try this: +→ [Alternative approach 1] +→ [Alternative approach 2] +→ [Alternative approach 3] + +[EVIDENCE] +"I've seen this play out across [X] projects: +[Specific result/pattern you've observed]" + +[NUANCE] +"To be clear: [conventional wisdom] works if [specific condition]. +But for [your context], [your approach] is better because..." + +[CTA] +"What's your experience? Am I missing something?" +``` + +**Why this works for me:** +- [Reason 1] +- [Reason 2] + +**Average engagement:** [Metrics] + +--- + +## Template 4: [Name - e.g., "My Failure Lesson Post"] + +**When to use:** [e.g., "When sharing what didn't work to build trust"] + +**Structure:** + +``` +[HOOK - Admission of failure] +"[Approach] should have worked. +It failed spectacularly." + +[SETUP] +We were trying to [goal]. +The plan: [what you intended to do] +On paper, perfect. + +[THE FAILURE] +"Here's what actually happened..." +(Specific description of what went wrong - 3-4 lines) + +[WHY IT FAILED] +Looking back, three mistakes: +1. [Mistake 1] - We assumed [wrong assumption] +2. [Mistake 2] - We underestimated [factor] +3. [Mistake 3] - We ignored [warning sign] + +[THE PIVOT] +"So we tried [different approach] instead. +That worked because..." + +[THE LEARNING] +"Key lesson: +[Non-obvious insight that only came from the failure]" + +[CTA] +"Have you failed at [topic] too? What did you learn?" +``` + +**Why this works for me:** +- [Reason 1] +- [Reason 2] + +**Average engagement:** [Metrics] + +--- + +## Template 5: [Name - Your custom template] + +**When to use:** [Context] + +**Structure:** +[Your proven structure] + +**Why this works for me:** +[Analysis] + +**Average engagement:** [Metrics] + +--- + +## Template Performance Comparison + +| Template | Avg. Likes | Avg. Comments | Avg. Reach | Best Use Case | +|----------|-----------|---------------|------------|---------------| +| Framework Intro | [X] | [Y] | [Z] | [When] | +| Before/After | [X] | [Y] | [Z] | [When] | +| Contrarian | [X] | [Y] | [Z] | [When] | +| Failure Lesson | [X] | [Y] | [Z] | [When] | + +**Insights:** +[What these patterns tell you about your audience preferences] + +--- + +## Template Selection Guide + +**Use Framework template when:** +- Introducing new model/system +- Teaching actionable process +- Want high saves (reference value) + +**Use Before/After template when:** +- Have strong results to share +- Building credibility +- Want case study authority + +**Use Contrarian template when:** +- Challenging assumptions +- Positioning unique POV +- Want engagement/debate + +**Use Failure template when:** +- Building trust/authenticity +- Sharing hard-won lessons +- Want vulnerable connection + +--- + +## Update Log + +- [Date]: Created template 1 based on [successful posts] +- [Date]: Refined template 2 after [pattern observation] +- [Date]: Added template 3 for [new content type] diff --git a/docs/m0/log.md b/docs/m0/log.md new file mode 100644 index 0000000..cbc6812 --- /dev/null +++ b/docs/m0/log.md @@ -0,0 +1,59 @@ +# M0 — Implementation Log + +Running record of decisions, deviations, and out-of-scope follow-ups discovered +during M0 execution. Plan: `docs/m0/plan.md` (18 steps). History → git; this file +captures only what the commit messages cannot. + +## Session 3 — Steps 11–13 (2026-06-18) + +### Environment reality vs. plan assumptions + +The plan was authored assuming the operator's **real `.local.md` runtime data** +sat in the plugin tree (227-line voice profile, analytics exports, draft queue). +On this machine that data is **absent** — it is a clean clone: + +- `assets/voice-samples/`: only the PII-free placeholder `authentic-voice-samples.md` + (+ `.template.md`). No `.local.md` source. +- `assets/drafts/`: only `.gitkeep`. `assets/analytics/`: only `README.md` + empty `ab-tests/`. +- The 4 tracked D2 scaffold instances DO exist (`high-engagement-posts.md`, + `demographics.md`, `engagement-patterns.md`, `my-post-templates.md`). + +**Consequence for Step 12 (live migration):** every `MOVE_FILES` / `MOVE_DIRS` +entry resolved to an absent source → clean no-op. Only the 4 `COPY_FILES` +scaffolds were relocated. Result: `migrated — moved 0, copied 4, skipped 0`; +`.migrated` marker written; idempotent re-run confirmed `already-migrated`. + +**Step 12 verify adapted:** the plan's literal check +(`test -f .../voice-samples/authentic-voice-samples.md`) cannot pass without a +`.local.md` source to move, so it was replaced with the achievable post-condition: +`.migrated` marker present + 4 scaffolds external + `migrateData` wired into +`session-start.mjs` + idempotency. The voice MOVE correctly no-op'd. (Operator +pre-approved this adaptation before the run.) + +### OUT OF M0 SCOPE — git-history scrub of the leaked post (FOLLOW-UP) + +`assets/examples/high-engagement-posts.md` held the operator's **real** LinkedIn +post at HEAD (the "Ralph Wiggum / vibe-coding" post — real names, real engagement +metrics, real personal narrative). Step 13 scrubbed the **working-tree** content +to a generic placeholder (0 `## Post N` sections → personalization score 0, no +PII). **This does not remove the post from git history.** A history rewrite +(`git filter-repo` / BFG) on `assets/examples/high-engagement-posts.md` is a +separate, explicit operation — **deferred, not done here** (brief §13: out of M0 +scope). Track until the repo is published. + +### Note — external instance retains pre-scrub content (by design) + +The Step-12 migration copied `high-engagement-posts.md` to +`~/.claude/linkedin-studio/examples/high-engagement-posts.md` **before** Step 13 +scrubbed the in-plugin file (B3 ordering). The external copy therefore still holds +the migrated content. That is the operator's private data dir (outside any repo) — +the operator may curate it; not an M0 concern. + +### D2 scaffolds completed + +All 6 scaffolds now have a read-only `*-template.*` seed: `case-study-template.md` +and `framework-template.md` already shipped; Step 13 added the 4 missing ones +(`high-engagement-posts-template.md`, `demographics-template.md`, +`engagement-patterns-template.md`, `my-post-templates-template.md`). The 3 +already-generic instances seeded their templates verbatim; the high-engagement +template is a freshly-authored generic seed (the old instance was the leak). diff --git a/hooks/scripts/__tests__/personalization-score.test.mjs b/hooks/scripts/__tests__/personalization-score.test.mjs index 18fbdc0..dfc9ab2 100644 --- a/hooks/scripts/__tests__/personalization-score.test.mjs +++ b/hooks/scripts/__tests__/personalization-score.test.mjs @@ -83,3 +83,53 @@ describe('calculateScore — reads external instance data (M0-7)', () => { assert.equal(score, 0); }); }); + +describe('calculateScore — scaffold categories read the external instance (M0-13)', () => { + let dataDir, pluginRoot; + const saved = { LINKEDIN_STUDIO_DATA: process.env.LINKEDIN_STUDIO_DATA }; + + afterEach(() => { + for (const d of [dataDir, pluginRoot]) { + if (d && existsSync(d)) rmSync(d, { recursive: true, force: true }); + } + dataDir = pluginRoot = undefined; + if (saved.LINKEDIN_STUDIO_DATA === undefined) delete process.env.LINKEDIN_STUDIO_DATA; + else process.env.LINKEDIN_STUDIO_DATA = saved.LINKEDIN_STUDIO_DATA; + }); + + test('a populated external high-engagement-posts instance earns the 10 points', () => { + ({ dataDir, pluginRoot } = makeRoots()); + process.env.LINKEDIN_STUDIO_DATA = dataDir; + mkdirSync(join(dataDir, 'examples'), { recursive: true }); + const posts = ['## Post 1', '## Post 2', '## Post 3'].join('\n\n'); + writeFileSync(join(dataDir, 'examples', 'high-engagement-posts.md'), `# Posts\n\n${posts}\n`, 'utf-8'); + + const { score, personalized } = calculateScore(pluginRoot); + + assert.equal(score, 10, '3+ saved posts in the external instance earn the 10 points'); + assert.equal(personalized, 1); + }); + + test('the generic placeholder seed (no line-leading ## Post N) scores 0 — no crash', () => { + ({ dataDir, pluginRoot } = makeRoots()); + process.env.LINKEDIN_STUDIO_DATA = dataDir; + mkdirSync(join(dataDir, 'examples'), { recursive: true }); + writeFileSync(join(dataDir, 'examples', 'high-engagement-posts.md'), `# Posts\n\nPlaceholder — add a ## Post N section per saved post.\n`, 'utf-8'); + + const { score, personalized } = calculateScore(pluginRoot); + + assert.equal(score, 0, 'a placeholder with no ## Post [0-9] section scores 0'); + assert.equal(personalized, 0); + }); + + test('scaffold absent at the external root → 0, no crash (graceful degradation)', () => { + ({ dataDir, pluginRoot } = makeRoots()); + process.env.LINKEDIN_STUDIO_DATA = dataDir; + // examples/ dir never created — the category is simply skipped, never throws + + const { score, personalized } = calculateScore(pluginRoot); + + assert.equal(score, 0); + assert.equal(personalized, 0); + }); +});