diff --git a/plugins/linkedin-studio/agents/analytics-interpreter.md b/plugins/linkedin-studio/agents/analytics-interpreter.md index 0b370dc..9f807c0 100644 --- a/plugins/linkedin-studio/agents/analytics-interpreter.md +++ b/plugins/linkedin-studio/agents/analytics-interpreter.md @@ -125,7 +125,7 @@ Generic advice gets to baseline. Their patterns get to exceptional. - Save rate patterns? - Share rate vs. reaction rate? -**Signal weights:** Saves (10x) > Shares (8x) > Expert comments (7-9x) > Quality comments (2.5x) > Reactions (0.2x) +**Signal order (not coefficients):** saves > shares > quality comments (15+ words) > reactions/likes. Directional single-vendor estimates: a save ≈ 5x a like, a quality comment ≈ 2x a like — trust the order, test the number. See `references/algorithm-signals-reference.md`. ### 5. Growth Indicators diff --git a/plugins/linkedin-studio/agents/content-optimizer.md b/plugins/linkedin-studio/agents/content-optimizer.md index 6c029a0..04ffa76 100644 --- a/plugins/linkedin-studio/agents/content-optimizer.md +++ b/plugins/linkedin-studio/agents/content-optimizer.md @@ -70,10 +70,10 @@ When you receive content to optimize, analyze it through these lenses: ### 3. Algorithm Signal Analysis -**Positive signals to maximize:** -- Content that encourages saves (10x weight) -- Content that prompts expert comments (7-9x weight) -- Content that drives 15+ word comments (2.5x weight) +**Positive signals to maximize** (order, not coefficients — see `references/algorithm-signals-reference.md`): +- Content that earns **saves** — top of the engagement order (a save ≈ 5x a like, directional) +- Content that earns **shares** — strong distribution / endorsement signal +- Content that earns **substantive 15+ word comments** — a quality comment ≈ 2x a like; substance over volume - Dwell time optimization (>30s = +25%) **Penalties to avoid:** diff --git a/plugins/linkedin-studio/agents/engagement-coach.md b/plugins/linkedin-studio/agents/engagement-coach.md index cf6da36..1561f0c 100644 --- a/plugins/linkedin-studio/agents/engagement-coach.md +++ b/plugins/linkedin-studio/agents/engagement-coach.md @@ -145,7 +145,7 @@ When deciding who to comment on, score each opportunity: ### The CEA Comment Method -Every comment follows the CEA structure. Minimum 15 words (2.5x more algorithmic value than shorter comments). Target 25-50 words for maximum impact. +Every comment follows the CEA structure. Minimum 15 words (substantive comments outweigh short ones — a quality comment ≈ 2x a like in single-vendor data; see `references/algorithm-signals-reference.md`). Target 25-50 words for maximum impact. **The Formula:** 1. **Compliment** — Specific point you appreciated (NOT generic praise) diff --git a/plugins/linkedin-studio/assets/checklists/quality-scorecard.md b/plugins/linkedin-studio/assets/checklists/quality-scorecard.md index 743bc6c..6990334 100644 --- a/plugins/linkedin-studio/assets/checklists/quality-scorecard.md +++ b/plugins/linkedin-studio/assets/checklists/quality-scorecard.md @@ -93,9 +93,9 @@ Rate each criterion 0-3: --- -## 360Brew Validation (Critical) +## Profile/Topic Relevance Validation (Critical) -Before posting, verify your profile passes the 360Brew test: +Before posting, verify your profile supports the post's topic (topic/interest relevance is a confirmed 2026 ranking input — see `references/algorithm-signals-reference.md`): - [ ] Profile clearly shows expertise in post topic - [ ] Headline includes relevant keywords diff --git a/plugins/linkedin-studio/commands/newsletter.md b/plugins/linkedin-studio/commands/newsletter.md index fffd7a0..40b68e2 100644 --- a/plugins/linkedin-studio/commands/newsletter.md +++ b/plugins/linkedin-studio/commands/newsletter.md @@ -658,8 +658,8 @@ turning-points the spine already named. 3. **Expand with the `content-repurposer` muscle.** Reuse `agents/content-repurposer.md` (its article→long-form conversion discipline) - for individual section expansions — invoke it via `Task` (`subagent_type: - linkedin-studio:content-repurposer`) when useful, *from this + for individual section expansions — invoke it via `Task` + (`subagent_type: linkedin-studio:content-repurposer`) when useful, *from this command layer* (foreground, principle 4). The command owns assembly and voice; the agent assists with conversion. The draft is voice-matched by THIS session, not self-certified for voice — voice-match remains an diff --git a/plugins/linkedin-studio/commands/pipeline.md b/plugins/linkedin-studio/commands/pipeline.md index 7d13d1b..885b1fe 100644 --- a/plugins/linkedin-studio/commands/pipeline.md +++ b/plugins/linkedin-studio/commands/pipeline.md @@ -68,8 +68,8 @@ Reference `${CLAUDE_PLUGIN_ROOT}/references/engagement-frameworks.md` for hooks Run the draft through optimization checks: **Algorithm signals** (from `references/algorithm-signals-reference.md`): -- Save-worthy content (10x weight) -- Comment-provoking (7-9x weight) +- Save-worthy content (saves rank highest in the engagement order) +- Comment-provoking content (a substantive 15+ word comment ≈ 2x a like) - Dwell time >30s (+25%) **Quality scorecard** (from `assets/checklists/quality-scorecard.md`): diff --git a/plugins/linkedin-studio/commands/profile.md b/plugins/linkedin-studio/commands/profile.md index d458231..ff2319c 100644 --- a/plugins/linkedin-studio/commands/profile.md +++ b/plugins/linkedin-studio/commands/profile.md @@ -28,7 +28,7 @@ Read `references/algorithm-signals-reference.md` for algorithm mechanics. ## The Profile/Topic Relevance Factors -LinkedIn's 150B parameter foundation model evaluates five criteria: +The 2026 relevance-ranking model evaluates five criteria (see `references/algorithm-signals-reference.md`): | Criteria | What It Checks | Impact if Missing | |----------|----------------|-------------------| diff --git a/plugins/linkedin-studio/commands/setup.md b/plugins/linkedin-studio/commands/setup.md index 39a6054..aa5784a 100644 --- a/plugins/linkedin-studio/commands/setup.md +++ b/plugins/linkedin-studio/commands/setup.md @@ -18,7 +18,7 @@ allowed-tools: # LinkedIn Plugin Setup & Personalization -You are a setup assistant for the LinkedIn thought leadership plugin. Guide the user through populating their asset templates with real data to maximize content personalization. +You are a setup assistant for the LinkedIn Studio plugin. Guide the user through populating their asset templates with real data to maximize content personalization. ## Step 0: Calculate Personalization Score diff --git a/plugins/linkedin-studio/hooks/scripts/__tests__/state-updater.test.mjs b/plugins/linkedin-studio/hooks/scripts/__tests__/state-updater.test.mjs index 6bd82e4..45e046e 100644 --- a/plugins/linkedin-studio/hooks/scripts/__tests__/state-updater.test.mjs +++ b/plugins/linkedin-studio/hooks/scripts/__tests__/state-updater.test.mjs @@ -72,6 +72,29 @@ target_date: "2026-12-31" `; +// A hand-corrupted / pre-migration state missing EVERY date anchor +// (last_post_date, last_firsthour_date, last_outreach_date). No production +// template ships like this — every template carries last_post_date — but it is +// the exact else-fall-through the S8 date-scalar gate protects: the scalar must +// NOT be inserted and must NOT be reported in `changes`, while the section append +// still runs and the function still returns non-null (log-accuracy branch). +const NO_ANCHOR_STATE = `--- +first_post_date: "2026-01-15" +last_post_topic: "AI strategy" +posts_this_week: 2 +weekly_goal: 3 +follower_count: 850 +follower_target: 10000 +target_date: "2026-12-31" +--- + +# LinkedIn Session State + +## Recent Posts + +- [2026-04-05] "AI governance is not about..." (1450) - AI strategy +`; + describe('updatePostTracking', () => { test('sets last_post_date to provided date', () => { const result = updatePostTracking(SAMPLE_STATE, { @@ -422,6 +445,25 @@ describe('recordFirstHourPlan', () => { assert.ok(section.includes('[2026-05-30 09:00]'), 'entry must land inside the First-Hour Plans section'); assert.ok(section.includes('AI governance')); }); + + test('no-anchor fall-through: neither last_firsthour_date nor last_post_date — scalar not written, not reported; section still appended', () => { + // Exercises the else-fall-through of the date-scalar gate + // (state-updater.mjs:225-231): with NO anchor field, the scalar cannot be + // inserted and must NOT be reported in `changes` (the exact branch the S8 + // fix protects) — yet the section append always runs, so result is non-null. + assert.ok(!/^last_firsthour_date:/m.test(NO_ANCHOR_STATE)); + assert.ok(!/^last_post_date:/m.test(NO_ANCHOR_STATE)); + const result = recordFirstHourPlan(NO_ANCHOR_STATE, PLAN_OPTS); + assert.notEqual(result, null); + // Scalar NOT inserted (no anchor to insert after) + assert.ok(!/^last_firsthour_date:/m.test(result.content), 'scalar must not be inserted without an anchor'); + // ...and NOT reported as changed + assert.ok(!result.changes.some((c) => /last_firsthour_date/.test(c)), 'no date-scalar change entry without an anchor'); + // The section entry IS still written + assert.ok(result.changes.some((c) => /^First-Hour Plans \+=/.test(c)), 'section entry must still be recorded'); + assert.ok(result.content.includes('## First-Hour Plans')); + assert.ok(result.content.includes('[2026-05-30 09:00]')); + }); }); describe('recordOutreachContact', () => { @@ -514,4 +556,21 @@ describe('recordOutreachContact', () => { assert.ok(section.includes('[2026-05-30 14:00]'), 'entry must land inside the Outreach Pipeline section'); assert.ok(section.includes('@bigvoice')); }); + + test('no-anchor fall-through: none of last_outreach_date/last_firsthour_date/last_post_date — scalar not written, not reported; section still appended', () => { + // Exercises the else-fall-through of the date-scalar gate + // (state-updater.mjs:284-293): with NONE of the three anchors, the scalar + // cannot be inserted and must NOT be reported — the section append still + // runs, so result is non-null. + assert.ok(!/^last_outreach_date:/m.test(NO_ANCHOR_STATE)); + assert.ok(!/^last_firsthour_date:/m.test(NO_ANCHOR_STATE)); + assert.ok(!/^last_post_date:/m.test(NO_ANCHOR_STATE)); + const result = recordOutreachContact(NO_ANCHOR_STATE, CONTACT_OPTS); + assert.notEqual(result, null); + assert.ok(!/^last_outreach_date:/m.test(result.content), 'scalar must not be inserted without an anchor'); + assert.ok(!result.changes.some((c) => /last_outreach_date/.test(c)), 'no date-scalar change entry without an anchor'); + assert.ok(result.changes.some((c) => /^Outreach Pipeline \+=/.test(c)), 'section entry must still be recorded'); + assert.ok(result.content.includes('## Outreach Pipeline')); + assert.ok(result.content.includes('[2026-05-30 14:00]')); + }); }); diff --git a/plugins/linkedin-studio/references/engagement-frameworks.md b/plugins/linkedin-studio/references/engagement-frameworks.md index e4016fb..20a0c05 100644 --- a/plugins/linkedin-studio/references/engagement-frameworks.md +++ b/plugins/linkedin-studio/references/engagement-frameworks.md @@ -335,16 +335,15 @@ The lesson was expensive but clear. [MEDIUM - transition]" ### Engagement Quality Hierarchy -Not all engagement is equal. LinkedIn's algorithm weights different interactions based on their signal value: +Not all engagement is equal. The defensible spine is the **order**, not a fixed multiplier — LinkedIn publishes no coefficient table, so trust the order and test the number: -1. **Saves** (Highest signal - indicates content worth returning to) -2. **Shares** (High signal - amplification and endorsement) -3. **Comments 15+ words** (2.5x more valuable than short comments) -4. **Expert comments** (7-9x multiplier - comments from verified experts in your field) -5. **Comments <15 words** (Moderate signal) -6. **Reactions** (Lowest signal - minimal effort) +1. **Saves** (top signal — content worth returning to; a save ≈ 5x a like in single-vendor data) +2. **Shares** (high signal — amplification and endorsement) +3. **Comments 15+ words** (substantive comments outweigh short ones; a quality comment ≈ 2x a like) +4. **Comments <15 words** (moderate signal) +5. **Reactions** (baseline engagement unit) -**Key insight:** One save or expert comment is worth significantly more than dozens of reactions. Focus on creating content that people want to save and share, and cultivate engagement from recognized experts in your domain. +**Key insight:** One save or substantive comment is worth more than many reactions. Focus on content people want to save and share, and cultivate genuine substantive comments. See `references/algorithm-signals-reference.md` (cite, don't restate magnitudes). ### First Hour Critical - Aim for 15+ engagements in first 60 minutes diff --git a/plugins/linkedin-studio/references/glossary.md b/plugins/linkedin-studio/references/glossary.md index 7d94ef8..20c8dc2 100644 --- a/plugins/linkedin-studio/references/glossary.md +++ b/plugins/linkedin-studio/references/glossary.md @@ -98,7 +98,7 @@ Coordinated group of accounts that artificially boost each other's posts. Active **Used in:** `references/linkedin-growth-playbook-2025-2026.md`, `commands/outreach.md` (collab absorbed in v2.0.0), `agents/network-builder.md` ### Engagement Quality Hierarchy -Weighted valuation system for different engagement types: Saves (10x) > Shares (8x) > Expert Comments (7-9x) > 15+ word comments (2.5x) > Short comments (1x) > Reactions (0.2x). Quality over quantity. +The defensible **ordering** of engagement signals — **saves > shares > quality comments (15+ words) > reactions/likes** — not a fixed coefficient table (LinkedIn publishes no such weights). Directional single-vendor estimates: a save ≈ 5x a like (≈ 2x a comment); a quality comment ≈ 2x a like. Trust the order, test the number — these are not hard multipliers to optimize against. **Used in:** `references/algorithm-signals-reference.md`, `references/engagement-frameworks.md` @@ -213,7 +213,7 @@ Unexpected statement or data point that breaks normal thought patterns and captu ## S ### Save Signal -Highest-value algorithmic signal (10x weight). Saves indicate content worth returning to; posts with saves get 130% higher follow probability. Only ~3% of posts reach save-worthy status. +Highest-value engagement signal — top of the engagement order. A save ≈ 5x a like (≈ 2x a comment) in single-vendor data — directional, not a fixed weight. Saves indicate content worth returning to; posts with saves get 130% higher follow probability. Only ~3% of posts reach save-worthy status. **Used in:** `references/algorithm-signals-reference.md`, `references/linkedin-growth-playbook-2025-2026.md` diff --git a/plugins/linkedin-studio/references/linkedin-growth-playbook-2025-2026.md b/plugins/linkedin-studio/references/linkedin-growth-playbook-2025-2026.md index 1ed5857..cbf4cfb 100644 --- a/plugins/linkedin-studio/references/linkedin-growth-playbook-2025-2026.md +++ b/plugins/linkedin-studio/references/linkedin-growth-playbook-2025-2026.md @@ -203,7 +203,7 @@ LinkedIn removed hashtag following, hashtag pages, and "Talks About" sections in **LinkedIn's data:** - 1.4x more engagement than other formats -- Videos inspire 5x more conversations than text +- #2 format but **declining** in reach; quality of engagement is debated — add captions, most watch muted (see `references/algorithm-signals-reference.md`) **Successful creator perspective (Lara Acosta, #1 UK female creator):** - "Video is overrated for growth on LinkedIn" @@ -1002,7 +1002,7 @@ Within 20 months: 240,000 followers across LinkedIn, TikTok, Instagram, YouTube. - Jasmin Alić mantra: "Share everything you know" **4. Engagement quality trumps impression quantity** -- Comments generate 15x more algorithmic boost than likes +- Substantive comments rank above likes in the engagement order (a quality comment ≈ 2x a like in single-vendor data — directional, not a fixed multiplier; see `references/algorithm-signals-reference.md`) - First-hour response rates directly impact distribution **5. Data-driven iteration** diff --git a/plugins/linkedin-studio/scripts/test-runner.sh b/plugins/linkedin-studio/scripts/test-runner.sh index e6ee8af..ebb6f35 100755 --- a/plugins/linkedin-studio/scripts/test-runner.sh +++ b/plugins/linkedin-studio/scripts/test-runner.sh @@ -184,20 +184,37 @@ echo "--- Algorithm-Stat Consistency ---" # The single source of truth for algorithm magnitudes is # references/algorithm-signals-reference.md. After the Phase-0 reconciliation, -# stale/competing magnitudes — and the unpublishable model brand/date — must not -# reappear anywhere else (cite the reference, do not restate). This enforces -# "one value per effect" by forbidding the known competing values from returning. +# stale/competing magnitudes — the retired engagement-coefficient folklore, the +# unpublishable model params/brand, and the deployment date — must not reappear +# anywhere else (cite the reference, do not restate). This enforces "one magnitude +# per algorithm effect" by forbidding EVERY retired-class value from returning, so +# the same grep that defines the Phase-0 Success Criterion fails on any survivor. # -# Pattern + scope must cover every file the criterion's grep covers. The carousel -# rate appears as the substring "6.60%" (which "6\.6%" does NOT match), and the -# retired comment multiplier as "5x more effective / 5x less valuable / 5x more -# reach than" — folklore the canonical reference forbids (it keeps only the -# engagement ORDER). Scope includes agents/ and assets/templates/ because the two -# v4.0.0 BLOCKERs survived there (agents/engagement-coach.md, assets/templates/ -# carousel-templates.md). assets/templates/ — not all of assets/ — keeps the scan -# off gitignored runtime data (assets/analytics/, assets/drafts/). -STALE_STATS='40-50%|25-40%|6\.6%|6\.60%|1\.92%|15x more reach|5x more effective|5x less valuable|5x more reach than|-40-60%|360Brew|January 2026' -STAT_HITS=$(grep -rnE "$STALE_STATS" references/ commands/ skills/ hooks/prompts/ agents/ assets/templates/ CLAUDE.md README.md .claude-plugin/plugin.json 2>/dev/null | grep -v 'algorithm-signals-reference' || true) +# S9 rebuild: the S8 list forbade only the two S7-named strings and went green +# over six more survivors (the coefficient system in analytics-interpreter/ +# content-optimizer/pipeline/glossary, the playbook 15x/5x, the 150B model). This +# list is rebuilt to the FULL criterion. Forbidden classes (each maps to a +# canonical statement in the reference): +# - Carousel-rate folklore: 6.6% / 6.60% / 1.92% → reference: "~7% top format" +# - Link-penalty folklore: 40-50% / 25-40% / -40-60% → reference: one ~38% correlational band +# - Comment-multiplier folklore: "15x more reach/algorithmic", "5x more effective/ +# less valuable/reach than" → reference: order only, comment ≈ 2x a like +# - Video-multiplier folklore: "5x more conversations" → reference: video declining, no multiplier +# - Engagement-coefficient system: 7-9x, 2.5x, 0.2x, (10x), (8x), "10x weight" +# → reference: "never hard coefficients to optimize against" +# - Model params/brand/date: 150B param / 150 billion param / 360Brew / January 2026 +# → reference: "Not publishable as fact" +# Bare "10x"/"15x"/"5x" are deliberately NOT forbidden — they carry legitimate +# uses (collaboration "10x your reach" hyperbole, "5x5x5", posting cadence, pixel +# dims like 1080x1350), so each token targets the retired *phrasing*, not the bare +# number. +# +# Scope covers every dir the criterion's grep covers, including assets/checklists/ +# (the 360Brew survivor lived there, outside the S8 scan) and assets/templates/. +# assets/{templates,checklists}/ — not all of assets/ — keeps the scan off +# gitignored runtime data (assets/analytics/, assets/drafts/, voice-samples/). +STALE_STATS='40-50%|25-40%|6\.6%|6\.60%|1\.92%|15x more reach|15x more algorithmic|5x more effective|5x less valuable|5x more reach than|5x more conversations|7-9x|2\.5x|0\.2x|\(10x\)|\(8x\)|10x weight|-40-60%|150 ?B param|150 billion param|360Brew|January 2026' +STAT_HITS=$(grep -rnE "$STALE_STATS" references/ commands/ skills/ hooks/prompts/ agents/ assets/templates/ assets/checklists/ CLAUDE.md README.md .claude-plugin/plugin.json 2>/dev/null | grep -v 'algorithm-signals-reference' || true) if [ -z "$STAT_HITS" ]; then pass "no stale algorithm magnitudes / model brand outside the canonical reference" else diff --git a/plugins/linkedin-studio/skills/linkedin-voice/SKILL.md b/plugins/linkedin-studio/skills/linkedin-voice/SKILL.md index e6f3959..c69e95a 100644 --- a/plugins/linkedin-studio/skills/linkedin-voice/SKILL.md +++ b/plugins/linkedin-studio/skills/linkedin-voice/SKILL.md @@ -49,7 +49,7 @@ This skill covers voice identity, profile optimization for the topic-relevance r ### The Profile/Topic Relevance Factors -LinkedIn's 150B parameter foundation model evaluates **five criteria** before your post reaches anyone: +The 2026 relevance-ranking model evaluates **five criteria** before your post reaches anyone: | Criteria | What It Checks | Impact if Missing | |----------|----------------|-------------------|