Step 21 (remediation Wave 4 / S6, SOLO): finalize the audit-remediation as
v4.0.0. Version 3.1.0 -> 4.0.0 across all current-version declarations; counts
reconciled to the ls-derived source of truth (19 agents / 27 commands / 6 skills
/ 9 hooks / 25 reference docs / 16 newsletter phases — Step 20 confirmed NO
TRIM); three-doc sync (plugin README + plugin CLAUDE.md + root README) clears the
[skip-docs] debt accumulated across Wave 1-4; CHANGELOG v4.0.0 entry summarizing
Steps 1-20.
Scope additions beyond the plan's literal Files list, all version-sync or
[skip-docs]-debt in nature (flagged, not feature creep):
- ../../CLAUDE.md (root marketplace): linkedin-studio entry v3.1.0 -> v4.0.0
(the version-sync invariant mandates updating every version reference; leaving
it stale is a real inconsistency).
- scripts/test-runner.sh: added the version-consistency grep the file's own
Step-1 comment promised ("added in Step 21") — plugin.json version must match
the README badge, the CLAUDE.md header, and the CHANGELOG top entry — and
folded the Wave 2 lint gap (plugin.json now covered by the stat-consistency
scan). 66/0/0, exit 0.
- plugin README: added the missing /linkedin:firsthour command row (Step 16
[skip-docs] debt) and the 25th reference doc (longform-quality-rules.md) to
the knowledge-base table; fixed body counts ("All 26 commands" -> 27, "24
reference documents" -> 25); badges + intro 26 -> 27 commands, 24 -> 25 refs.
- root README + marketplace.json: dropped the unpublishable model brand/date
("360Brew" / "January 2026") the algorithm-signal reconciliation already
removed everywhere inside the plugin.
Surviving "3.1.0" strings are intentional history, not stale declarations: the
README version-history table row, the "vX added Y" attributions in
plugin.json/CLAUDE.md, and the headless-review reload caveat are all
changelog-genre. Every current-version declaration (plugin.json version, README
badge, CLAUDE.md header, root README marker, marketplace.json) reads 4.0.0.
The major bump reflects the remediation's scope plus the reinstall/reload the
newly-wired agents need to register, and consolidates — does not repeat — the
v3.0.0 identity break; it is not a fresh breaking API change (locked operator
decision).
Pre-existing and out of scope (flagged, untouched): a duplicate /linkedin:setup
row in the README command tables.
Verify: bash scripts/test-runner.sh exit 0 (66/0/0); plugin.json + marketplace.json
parse; counts consistent README == CLAUDE.md == root README; stale-count sweep
clean. NO push — /trekreview (S7) is the release gate.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Steg 20 (remediation Wave 4 / S5, SOLO): measure whether the 7-agent long-form
review stack carries redundant gates. Method: cross-reference each agent's check
taxonomy against its in-repo fasit fixture; four fixtures (editorial, content,
language, fact-reviewer) target the SAME Del 4 edition, enabling a real
cross-gate overlap comparison on one piece (not a live run — fixtures' own
live-run notes require a reload + cross-repo Maskinrommet access, out of scope).
Finding: every gate has >=1 unique catch on Del 4. The four genuine overlaps
(verbatim repetition, the Vi/Vi-i-Nav quote, the postulated number, the
small-orgs thread) are each justified — a cold re-take (Endring 9's reason to
exist), the same symptom via a different operation (flag-absence vs web-verify),
or two distinct defects sharing a surface topic — with no subsumption either way.
The fact-checker <-> fact-reviewer overlap is load-bearing (the pivot premise
arrived after Step 5, so only the cold re-run caught it).
Decision: NO TRIM. voice-scrubber has no fixture -> inconclusive; redundancy
retained (Step 20 On-failure = skip). Counts unchanged 19 agents / 27 commands;
count contract (EXPECT_AGENTS=19) untouched. test-runner 62/62 green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Step 19 (Wave 4 S4). Adds an expectation banner at the very top of
/linkedin:newsletter: a multi-session, multi-gate process (16 phases),
~4-8+ hours across several sessions, state maintained between sessions, with
a pointer to the short-form commands for feed posts. Sets realistic
expectations before the user starts. Pure docs/content.
Verify: grep -niE 'multi-session|multi-gate|16 phases|~[0-9].*hour' near top
→ matches; test-runner.sh exit 0.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Step 17 (Wave 4 S4) of the audit remediation. Applies research/03 §D5 + the
two S2 residual fixes folded in. No new commands/agents (counts stay 27/19).
Newsletter (commands/newsletter.md): new "Distribution channel" section after
Step 10 teaching the HONEST native-newsletter mechanics — bypasses organic feed
ranking via ONE deduplicated notification per subscriber per edition (NOT a
three-touchpoint blast); the mass invite fires once → ~1-2K follower floor
(wait until you can spend it); realistic cold-start 0-100 subs months 1-3;
discloses non-export / no-canonical / no-read-analytics / per-subscriber decay;
explicit below-vs-above-floor decision rule. Sourced to research/03 D5.
Profile (commands/profile.md): new "Profile SEO" section — headline as the
highest-weight search field + a per-section keyword-target table
(headline/about/experience/skills/featured), consistency-over-stuffing rule.
Outreach (commands/outreach.md): Step 8c persists the pipeline board to tracked
state via the new recordOutreachContact mutation (mirrors Step 16's
recordFirstHourPlan): additive last_outreach_date/outreach_active scalars + a
non-R-initial ## Outreach Pipeline section in state-updater.mjs +
config/state-file.template.md + --record-outreach CLI branch. +7 tests
(state-updater 26→33, full hook suite 83→90).
Residual 1 (growth-playbook:216): 9:16 "distribution boost" → 4:5/1:1 guidance
(9:16 mobile-only opt-in; "immersive distribution" = uncorroborated heuristic).
Residual 2 (video-strategy-guide:300): "3-second test determines 70% retention"
→ "front-load value for muted autoplay" (three-second hook is folklore, not a
LinkedIn signal).
Verify: grep checks 1-5 pass; test-runner.sh exit 0 (stat-consistency green);
state-updater 33/33. [skip-docs] — tre-doc + version bump deferred to Step 21.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
post-feedback-monitor was the lone non-Opus/Sonnet agent (model: haiku). It
does human-facing, real-time post-publish coaching — exactly the workload the
standing Opus-default targets — so the Haiku setting both contradicts the
default and underpowers the surface the new /linkedin:firsthour command hands
off to (remediation Step 18).
- agents/post-feedback-monitor.md: model haiku -> opus.
- CLAUDE.md: agent-table model column Haiku -> Opus. No Haiku agent remains.
Verify: grep 'model: haiku' agents/ -> none; CLAUDE.md row shows Opus;
structure lint -> 62/62, counts unchanged.
Plan Step 18 (Wave 4 S3). Counts unchanged.
[skip-docs]: tre-doc + version bump deferred to Step 21 per remediation plan.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Wire orphan agent #11 (engagement-coach) by giving it a command surface, and
add the tracked first-hour state the plan calls for (remediation Step 16).
- commands/firsthour.md (new, 27th command): post-publish first-hour /
reply-loop sprint. Delegates plan construction to engagement-coach via
Task (subagent_type: linkedin-studio:engagement-coach) — returns a grouped
target list (whales/inner-circle/ICPs/new connections), 2-3 seed
self-comments + 3-5 CEA replies in the user's voice, and a minute-by-minute
timeline anchored to publish time. Presents timeline/targets/drafts +
velocity checkpoints, auto-copies the drafts to clipboard, persists the
plan, then hands off to post-feedback-monitor for the 48h window.
- hooks/scripts/state-updater.mjs: new pure mutation recordFirstHourPlan()
mirroring updatePostTracking — additive by contract (inserts
last_firsthour_date after last_post_date when absent, creates the
## First-Hour Plans section when absent, never touches existing fields).
Section name is deliberately non-R-initial so it stays outside
pruneContentHistory's "## Recent Posts ... (?=\n## [^R])" capture window.
+ a --record-firsthour CLI branch for parity with the other mutations.
- config/state-file.template.md: additive scalars (last_firsthour_date,
firsthour_active) + the ## First-Hour Plans section.
- hooks/scripts/__tests__/state-updater.test.mjs: extend (existing file) with
7 recordFirstHourPlan tests — section creation, field insertion vs in-place
update (no duplication), round-trip non-interference, graceful empty
defaults, changes array.
- CLAUDE.md: register the command (## Commands 26 -> 27, table row).
- scripts/test-runner.sh: EXPECT_COMMANDS 26 -> 27 (registration guard).
Verify: grep 'subagent_type: linkedin-studio:engagement-coach' commands/ ->
firsthour.md; node --test state-updater -> 26/26; full hook suite -> 83/83;
bash scripts/test-runner.sh -> exit 0 (62 passed, commands 27/27).
Plan Step 16 (Wave 4 S3).
[skip-docs]: tre-doc + version bump deferred to Step 21 per remediation plan.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Resolve the video-advice contradiction on the two surfaces this step owns
(research/03 D1-D3):
- commands/video.md: rename the "3-Second Test" to a "Muted-Autoplay Test"
(front-load value for ~85% muted viewing; the "three-second hook" is
cross-platform folklore, not a LinkedIn signal); make captions the
enforceable spec (SRT or native auto-captions, indexed for search);
aspect ratio as guidance (4:5/1:1 preferred for broad distribution,
9:16 opt-in for the vertical video tab, crops to 1:1 on desktop); add
MP4-default + warn-only MOV/AVI + official upload limits to the
pre-recording reminder.
- references/linkedin-formats.md: reframe the 9:16 "distribution boost"
as uncorroborated heuristic; 4:5 "deprioritized" -> 4:5/1:1 preferred;
drop the "3-second hook"; MP4 safe-default + warn-only MOV/AVI; replace
"good video rewarded more than ever" with the honest "per-video reach
declining; documents/carousels out-engage video."
algorithm-signals-reference.md (the canonical magnitude source) was
already reconciled in Phase 0 (line 56 reads "declining ... 4:5/1:1
preferred, captions are the enforceable spec") and carries no boost copy,
so it is intentionally untouched here — and it is not in this step's
manifest.
Verify: grep 'must be 9:16|9:16 (1080|3-second hook' video.md
linkedin-formats.md -> none; 'captions' video.md -> 5; 'deprioritized'
linkedin-formats.md -> none; structure lint 61/61.
KNOWN RESIDUAL (flagged for follow-up, NOT in this step's scope):
- references/linkedin-growth-playbook-2025-2026.md:216 still carries a
"9:16 ... distribution boost" line. That file is owned by Step 17, but
Step 17's changes are newsletter-distribution — the video line would
fall through. Fold this into Step 17 or the review gate.
- references/video-strategy-guide.md:300 still says "the 3-second test
determines 70% of retention." That file is owned by NO plan step
(orphaned) — needs a home. Surface at session end for an operator
decision.
Plan Step 15 (Wave 4 S2). Counts unchanged.
[skip-docs]: tre-doc + version bump deferred to Step 21 per remediation plan.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Wire the orphan differentiation-checker (#10) into the five short-form
creation commands (post/quick/react/carousel/video) as a De-AI /
Differentiation Gate at each command's quality-check step: confirm the
LinkedIn-named substance signals (personal substance, original thinking,
concrete specifics, genuine voice) + a soft engagement-bait check, and
delegate an originality pass to linkedin-studio:differentiation-checker
when the angle risks commodity content. Add Task to allowed-tools in
quick/react/carousel (post/video already had it from Step 13).
Extend (not duplicate) hooks/prompts/voice-guardian.md's AI-pattern
section with the same named signals from research/01 D8 + research/03 D4.
Runtime-loaded prompt — no compile-hooks.py, no hooks.json change
(verified: compile-hooks --check reports no drift).
Test: new hooks/scripts/__tests__/linkedin-content-filter.test.mjs pins
the content/non-content boundary the gate is scoped by (14 tests).
Full hook suite 76/76, structure lint 61/61.
Plan Step 14 (Wave 4 S2). Counts unchanged (26 commands / 19 agents).
[skip-docs]: tre-doc + version bump deferred to Step 21 per remediation plan.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Resolves the orphan-agent audit finding by the locked default: wire all, no deletions, so the agent count stays 19. Per agent, added Task to the target command's allowed-tools and a coherent 'subagent_type: linkedin-studio:<name>' delegation at a real point in the command's flow (not a token grep-match).
Wired (agents 1-9 of 11): video-scripter -> video.md (Step 4); content-optimizer -> post.md (Step 7 refinement) + ab-test.md (2a.4 optimized challenger); analytics-interpreter -> report.md (Step 7, report mode) + analyze.md (Step 2, interpret mode); content-planner -> batch.md (Step 2) + pipeline.md (Step 1); trend-spotter -> batch.md (Step 1) + pipeline.md (Step 1); network-builder -> outreach.md (Step 3a); strategy-advisor -> strategy.md (Step 3); voice-trainer -> setup.md (Step 3a); post-feedback-monitor -> calendar.md (publish action, 48h monitor).
Deferred to their dedicated steps: #10 differentiation-checker -> Step 14 (short-form de-AI gate), #11 engagement-coach -> Step 16 (first-hour command). Namespaced subagent_type form requires a session reload before the wired agents are invokable.
Verify: each of the 9 has >=1 invocation in commands/; structural lint 61/61 (counts 19/26/25/6 intact); agent-fixtures 35/35; hook tests 62/62. Three-doc + version reconciliation deferred to Step 21 per the locked plan [skip-docs].
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Removed the legacy root local-continuity files (NEXT-SESSION-PROMPT.local.md,
REMEMBER.md, ROADMAP.md, TODO.md) per the single STATE.md continuity system.
Their durable content was already covered by CLAUDE.md + GOVERNANCE.md; the
rest was stale. Updated the Sesjonsfiler and Arbeidsflyt sections so CLAUDE.md
no longer mandates the deleted files. Per-plugin legacy files remain and are
flagged for the same cleanup, one plugin at a time.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
README went from 327 to 191 lines (50.6KB -> 12.6KB). Each plugin now
follows one template: tagline + 3-5 capability bullets + key commands +
stats/docs line. All per-version changelog prose removed (lives in each
plugin's CHANGELOG). Fixed stale LinkedIn Studio stats (now 19 agents /
26 commands / 6 skills, verified against plugin files).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Wave 3 / Step 12 of the remediation plan (Phase 1 — usable by a non-author).
Fix the discoverability defects the audit flagged:
- skills/linkedin-studio/SKILL.md (the auto-activating router): self-naming 'the
LinkedIn thought leadership plugin' -> 'LinkedIn Studio' (v3.0 rename leftover);
the 'All Agents' table corrected from 14 to the real 19 (added editorial-reviewer,
voice-scrubber, content-reviewer, language-reviewer, fact-reviewer); the 'All
Commands' table completed to 26 (added headless-review, pivot, carousel) so it
routes to newsletter/headless-review/pivot/react.
- commands/onboarding.md: '25 commands' -> '26 commands' (x2); pillar count '3-5
expertise areas' -> '5 expertise areas' (reconciles onboarding's 3-5 with
setup.md's '5 core topics' and the CLAUDE.md '5 core expertise areas' rule).
- commands/linkedin.md: router table + numbered option list gain headless-review
and pivot.
Scope note (operator decision, this session): the plan's Verify grep
'grep -rni "thought leadership" skills/ -> no matches' is broader than the brief's
actual criterion (which targets only the router skill's self-naming). 'thought
leadership' is a plugin-wide DOMAIN term — the '8 Thought Leadership Angles'
framework lives in references/thought-leadership-angles.md and is referenced by ~40
files (glossary, agents, post/video/batch commands). Renaming it only inside skills/
would create cross-file inconsistency; renaming it plugin-wide is a separate
vocabulary migration outside Step 12's discoverability scope. Per operator choice,
the router SELF-NAMING is fixed (brief criterion met) and the 4 remaining skills/
hits (linkedin-content-creation headings + one linkedin-strategy phase cell) are
legitimate domain usage kept consistent with the rest of the tree.
setup.md needed no edit: its pillar number was already '5'; reconciliation was a
one-sided fix in onboarding.
Verify: router no longer says 'thought leadership plugin'; grep -nc 'headless-review'
commands/linkedin.md -> 2; onboarding pillar count '5' matches setup.md; SKILL.md
agent table 19 rows, command table 26 rows; structural lint exit 0 (61 passed).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Wave 3 / Step 11 of the remediation plan (Phase 1 — usable by a non-author).
The README claimed 'the version that ships is the version that's actually been
independently reviewed' — an overclaim the operator correction flagged: the cold
review is a CAPABILITY the pipeline can run, not a guarantee every shipped edition
received. Reframe to the honest capability:
- Long-Form Engine bullet: the draft CAN be frozen and re-read by reviewers with no
drafting-session context (argument, language, facts, reader-fit) before lock, so a
cold pass catches what the framing-biased in-session gates miss; run inline (Step
6.5) or via /linkedin:headless-review for maximum independence. No claim about what
every shipped edition went through.
- Pipeline diagram annotation '(independent re-read)' -> '(cold, headless)'.
Verify: grep -ni 'independently reviewed' README.md -> no matches.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Wave 3 / Step 10 of the remediation plan (Phase 1 — usable by a non-author), per
research/02.
State what the plugin can and cannot do for a PERSONAL profile, dated 'as of
2026-05', without replacing one false claim with another:
- README.md: new 'Boundaries (as of 2026-05)' section. (a) post-level analytics API
EXISTS but is partner-gated (vetted Community Management app + verified org +
Page) — not self-serve; CSV is the practical floor; saves visible natively
(count-only, ~Sept 2025) with no self-serve API pull. (b) auto-publish is
technically POSSIBLE self-serve (w_member_social) but deliberately NOT built — a
design + ToS choice, explicitly 'not an impossibility' (avoids the new false
'cannot auto-publish' claim). (c) dwell internal-only for organic, not exportable.
- commands/calendar.md: the publish action now states plainly it marks a post YOU
posted manually as published — the tool does not post on your behalf.
- commands/report.md: dated the saves/no-self-serve-API note ('as of 2026-05').
- commands/import.md: dated 'Why CSV' note — API is partner-gated, CSV is the floor.
Verify: grep -niE 'cannot auto-publish|only way to (get|access)' README.md
commands/*.md -> no matches; grep -ni 'as of 2026' README.md -> 2.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Wave 3 / Step 9 of the remediation plan (Phase 1 — usable by a non-author).
The long-form review layer shipped Norwegian-locked: language-reviewer graded
unconditional Norwegian, voice-scrubber's gold standard was 'approved Norwegian
editions', and the editorial/content craft gates pointed at a 'skrivekontrakt §C2'
that does not ship. A non-Norwegian adopter would get English prose graded against
Norwegian idiom and a gate that depends on an unshipped contract.
- config/edition-state.template.json: add additive 'language' field (top-level,
default 'en') + a _doc entry. Threads into the language-dependent agents.
- agents/language-reviewer.md: new 'Language parameter' section — Norwegian-specific
checks (anglicism->Norwegian idiom, kanselli-stil) apply only when language=='no';
any other value grades that language's equivalents and never flags idiomatic
English as an anglicism. Default 'en'.
- agents/voice-scrubber.md: gold standard reframed to 'approved editions in the
configured language'; the Norwegian-chronicle calibration is the language=='no'
instantiation.
- agents/editorial-reviewer.md + agents/content-reviewer.md: the in-tree checklist
is now the operative, self-contained source of truth; Maskinrommet §C2 is an
optional upstream contract that does NOT ship (available only on the author's
runs). The gates work for an adopter without it.
- commands/newsletter.md: thread 'language' through the Step 6.5 cold-inputs and the
per-reviewer call inputs; the writing contract is now 'if it ships'.
Norwegian remains fully working when language: no (the author's case).
fact-reviewer.md was in the plan's file list but needed no change on inspection:
its F1-F4 checks (claims/quotes/numbers/sources) are language-agnostic; its
'Norwegian' mentions are boundary notes vs language-reviewer, which stay correct.
[skip-docs]: three-doc + version reconciliation is Step 21 (pre-review-gate); these
intermediate Wave commits are not pushed before the /trekreview gate.
Verify: edition-state JSON parses + has top-level language 'en'; language-reviewer
has 'language ==' references and no unconditional-Norwegian assertion; editorial
§C2 reframed to in-tree fallback ('operative source', 'does not ship'); agent
fixtures 35/35 pass; structural lint exit 0 (61 passed).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Wave 3 / Step 8 of the remediation plan (Phase 1 — usable by a non-author).
The flagship long-form engine shipped bespoke-as-general: a maintainer-private
absolute series path and a hardcoded 'Maskinrommet' brand in the renderers. Both
are now generalized so a non-author can run the pipeline without inheriting the
author's filesystem or publication identity.
- commands/newsletter.md + config/edition-state.template.json: series-root default
/Users/ktg/repos/maskinrommet/serier -> $HOME/linkedin-series; reconciled the
prose that called the maskinrommet folder 'the default' so it no longer
contradicts the new neutral default. LTL_SERIES_ROOT override contract preserved.
- render/build-linkedin.mjs + render/build-carousel.mjs: brand is now an LTL_BRAND
env-var (empty default -> generic). Samle-post title, carousel footer brand-span,
and cover-eyebrow fallback are de-branded; empty brand renders clean chrome. The
operator sets LTL_BRAND=Maskinrommet in their own env to re-brand (same pattern
as LTL_SERIES_ROOT).
- config/image-credit-caption.template.md: 'Maskinrommet-/serie-badge' -> generic
'serie-badge'.
Out of scope (Step 9): the residual 'Maskinrommet skrivekontrakt §C2/§A'
references in newsletter.md are the writing-contract generalization, handled next.
[skip-docs]: three-doc + version reconciliation is Step 21 (pre-review-gate, per
plan: 'LAST so it captures everything'). These intermediate Wave commits are NOT
pushed before the /trekreview gate, so the three-doc obligation (which governs
pushed changes) is satisfied at Step 21, not per local checkpoint commit.
Verify: grep -rIn '/Users/ktg' config/ commands/ render/ (excl .local) -> no
matches; grep -rn 'Maskinrommet' render/ -> no matches (de-branded); node --check
on both render scripts -> OK; LTL_SERIES_ROOT still present in newsletter.md.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Wave 2 / Step 7 of the remediation plan (research/02 D2/D4).
The tool parses the LinkedIn analytics CSV export, which has no saves/dwell — and
there is no self-serve API to pull them. Stop implying it tracks them:
- report.md: replace "Saves (10x weight) and expert comments (7-9x) are the
highest-impact signals" with honest wording — saves rank highest in the
engagement ORDER (cite references/algorithm-signals-reference.md, not a restated
coefficient), but are visible only in native LinkedIn post analytics (count-only,
~Sept 2025+) with no self-serve API, so this tool does not auto-track them; dwell
is internal to LinkedIn for organic posts.
- types.ts: document why PostMetrics intentionally omits saves/dwell (no ingest
source) so a future contributor does not "add the missing fields".
- strategy.md: reconcile two saves references (signature-content criterion +
authority scorecard) to say the count is read from native LinkedIn analytics, not
captured by this tool.
No new metric field, no manual-entry feature (operator Q3).
Verify: report.md has no "Saves (10x"/"highest-impact signals" and does say "no
self-serve API"; tsc --noEmit clean; analytics suite 106/106; structural lint
0 failed (report.md cites the reference, so stat-consistency stays green).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Wave 2 / Step 6 of the remediation plan.
Organic personal-post A/B tests gather a handful of posts per variant — far below
the volume a significance test needs — so the tool must not imply statistical
significance:
- Rename the results-table "Significant?" column to "Directional?" and define it
as "clears the ~20% minimum-meaningful-difference AND points the same way across
most posts" — a direction to test further, not a significant result.
- Reword the "20% significance rule" to a minimum-meaningful-difference effect-size
heuristic (explicitly NOT statistical significance).
- Replace the "3 = Medium, 5+ = High" confidence ladder with a directional-only
confidence section: treat every result as directional (not significant) given
realistic volume is well under ~50 conversions/variant; name a direction, not a
winner.
The 20% minimum-meaningful-difference threshold itself stays — it is a legitimate
effect-size heuristic; only the significance framing was the false claim.
Verify: no "Significant?"/"20% significance" remain; "directional" present;
structural lint 0 failed.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Wave 2 / Step 5 of the remediation plan (coupled criticals: voice-leak +
placeholder-detection).
Voice profile (the adopter-default leak):
- Ship a PII-free placeholder at authentic-voice-samples.md carrying a
<!-- VOICE_PLACEHOLDER --> sentinel + neutral default voice principles.
- Migrate the author's real profile to gitignored authentic-voice-samples.local.md
(already matched by *.local.md; added an explicit, commented .gitignore entry so
the intent is unmissable). NO git-history rewrite — the historical file is
attributed authorship, not a secret (per the plan threat model).
- Add authentic-voice-samples.template.md — a clean fill-in template for adopters.
- personalization-score.mjs: detect the sentinel (deterministic) instead of the
unreliable `[Your Name]` heuristic, so the placeholder scores 0 voice points and
a populated profile (sentinel removed) earns the 25.
- Both voice writers replace-not-append on the placeholder: setup.md (merge ->
replace-if-placeholder) and onboarding.md (append -> replace-if-placeholder), so
populating removes the sentinel; updated setup.md's stale heuristic table.
Operator decisions (deviations from plan-literal, approved this session):
- KEEP the plugin.json author name. The plan said scrub author -> neutral/org, but
that contradicts its own LICENSE reasoning (intentional MIT attribution) and all
5 sibling plugins keep author = the author; scrubbing only this one would create
inconsistency for zero security gain (the name is public-by-design). The voice
placeholder fully fixes the adopter-inheritance bug.
- Scrub the stale "January 2026 360Brew" brand from the plugin.json description and
the "360brew" keyword (locked decision: no publishable model name/date). This is
a Wave-1 propagation miss surfaced here because plugin.json was in Step 5's
touch-scope.
Flagged for follow-up (NOT done here — out of Session 2 scope):
- The lint's stat-consistency grep (scripts/test-runner.sh) scans references/,
commands/, skills/, hooks/prompts/, CLAUDE.md, README.md — but NOT
.claude-plugin/plugin.json, which is why the 360Brew brand slipped Wave 1.
Needs a Session-1-scoped lint extension to add plugin.json to the scan set.
- Readers (user-prompt-context.mjs, voice-guardian.md, state-update-reminder.md)
read the tracked .md (placeholder), per the plan. The operator's real voice now
lives in the gitignored .local.md, which nothing reads. To use it, readers + the
voice score should prefer .local.md (matching the user-profile.local.md
precedent). Deferred as a coherence follow-up for operator review.
Test-first: hooks/scripts/__tests__/personalization-score.test.mjs (red on the
placeholder scoring 25 under the old heuristic, green after the sentinel fix). Hook
suite 62/62, structural lint 0 failed.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Wave 2 / Step 4 of the remediation plan (docs/remediation/plan.md).
PRIMARY (the real fresh-clone failure):
- scripts/analytics/node_modules is gitignored, so a fresh clone has neither
tsx nor csv-parse. Surface an idempotent `npm install --silent` prerequisite
at point-of-use in report.md (Step 1b) and import.md (Step 4).
DEVIATION FROM PLAN (correction-in-scope, to satisfy the plan's own Verify gate):
- The plan assumed prepending `npm install` was sufficient. Verified it is NOT:
the commands invoke the CLI with an absolute src/cli.ts path but from the
user's arbitrary CWD, and `node --import tsx` resolves the `tsx` specifier
relative to CWD, not the script. There is no global tsx, so the call still
fails with ERR_MODULE_NOT_FOUND from any CWD other than scripts/analytics.
- Complete fix: invoke the locally-installed tsx by its absolute
node_modules/.bin/tsx path in all CLI calls (report.md x10, import.md x3), so
they resolve from any working directory once the install above has run.
Verified: 0 ERR_MODULE_NOT_FOUND running `report` from /tmp.
SECONDARY (latent correctness / hardening):
- Add findPluginRoot(): walks up to the dir holding .claude-plugin/plugin.json
and anchors getAnalyticsRoot() on it, falling back to the legacy 4-up count.
MEASURED that ../../../../ already resolved to the plugin root from BOTH
src/utils and build/utils (both 4 levels deep), so the plan's "src-vs-build
depth miscalibration" premise was false — this is correct-by-construction
hardening (survives a future source move), not a live-bug fix.
- Reconcile cli.ts usage/help text: `node build/cli.js` -> `node --import tsx
src/cli.ts` (the real runtime).
- Fix report.md troubleshooting: "Verify tsx is available" -> the actual
install command on ERR_MODULE_NOT_FOUND.
Test-first: scripts/analytics/tests/storage-root.test.ts (red on missing
findPluginRoot export, green after). Full suite 106/106, tsc --noEmit clean,
structural lint 0 failed.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Remove REMEMBER.md / TODO.md / ROADMAP.md / NEXT-SESSION-PROMPT.local.md
mechanisms per ~/.claude/CLAUDE.md (2026-05-26 rule): the three layers
are STATE.md + auto-memory + plugin-CLAUDE.md; local mechanisms are
abolished and consolidated into the layers + git + docs/-reference.
- .gitignore: drop REMEMBER.md / TODO.md / ROADMAP.md entries; add STATE.md
(gitignored — overskrives ved sesjonsslutt, lokal state-of-play).
- CLAUDE.md: cross-cutting-invariant note now points to STATE.md instead
of ROADMAP.md (linter update, same theme).
Forward-looking version specs (v5.2 TDD, v5.3 sesjons-konsolidering,
v5.4 brief-format public contract, v5.5 brief framing alignment) lived
in the gitignored ROADMAP.md and are NOT recoverable from git. If
reactivated they go through the brief→plan pipeline (proper Voyage
discipline anyway).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Set expectations up front: the plugin systematizes the work, it doesn't
substitute for the human effort. Letting AI drive lands you in the
forgettable middle; the judgment, genuine engagement, and effort that
make content worth reading stay with the user. Reinforces the README's
no-hype tone and filters for serious adopters over a viral shortcut.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The standalone "What's New in v3.1.0" section opened with internal
development narrative (Endring 9, Del 4 run, framing-bias) that means
nothing to a prospective adopter — still leading with a changelog, the
exact pattern the rewrite set out to fix. Follow llm-security: motivation
first, version detail only in the Version History table at the bottom.
- Remove the "## What's New in v3.1.0" section and its TOC entry
- README now flows intro → Why LinkedIn Studio Exists → What Is This?
→ Two Engines → Quick Start
- Nothing lost: v3.1.0 remains in the Version History table
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The README led with a ~600-word version-by-version wall and stacked
seven "What's New in vX.Y.Z" sections before reaching "What Is This?",
reading as a changelog rather than motivating adoption.
Restructure to the sibling-plugin pattern (config-audit / llm-security
/ voyage):
- Concise motivating intro paragraph (no version enumeration)
- Keep only the latest "What's New" (v3.1.0); full history stays in the
Version History table at the bottom
- New "Why LinkedIn Studio Exists" section: the real pain (blank-page
paralysis, inconsistency, algorithm opacity, generic AI slop, the
360Brew shift) mapped to what the plugin does about each
- New "Two Engines" section framing feed vs. long-form, with the
pre-lock quality gauntlet as the long-form differentiator
- Refresh "What Is This?" to the LinkedIn Studio / content-engine framing
- Swap the unverified "40+ ideas / Content Matrix" claim for the
documented "8 universal angles" (verification duty)
All accurate tables (26 commands, 19 agents, 9 hooks, 24 reference
docs) and the full Version History preserved verbatim.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Cold, adversarial review package for the long-form pipeline + configurable
per-edition personas. Motivated by Del 4 (Security Champions pivot): the
in-session editor + persona sweep shared the drafting session's framing-bias,
so the shipped version was never independently re-reviewed.
Headless package (9a/9b):
- New Step 6.5 (headless-review) in /linkedin:newsletter, after the persona
sweep, before lock — the independence layer the in-session gates can't be.
- New standalone /linkedin:headless-review command (run in a fresh session for
maximum isolation; reconstructs frozen draft + contract + personas from disk).
- 3 new Opus archetypes, each with a cardinal context-isolation block that
refuses drafting-session framing as "context pollution":
- content-reviewer (argument integrity C1–C5, ≤8 flags)
- language-reviewer (Norwegian language L1–L5, ≤10 flags)
- fact-reviewer (cold re-verification F1–F4, risk-sort + pivot-risk, WebSearch)
- Deliberate redundancy with fact-checker / editorial-reviewer documented so
the pairs are never de-duplicated.
Pivot-reopen (9c):
- New /linkedin:pivot command: logs articles.NN.pivots[], resets currentPhase,
un-locks, marks gates to re-run.
- Pivot-detection gate in Step 8 lock precondition (>20% word-count change or
>2 new sections re-opens cleared gates). Del 4 v8→v11 worked example.
Per-artifact personas (new requirement):
- articles.NN.personas with resolution order (edition-state → series file →
plugin library → interactive). One or more readers configurable per edition.
Schema/docs:
- edition-state.template.json: additive personas[], pivots[], headlessReview,
headless-review phase (16 phases); personaSweep.resonance.wordCount baseline.
- 3 fasit fixtures + 3 structural lint tests (Del 4 worked cases).
- Counts: 24→26 commands, 16→19 agents, 15→16 newsletter phases.
- README + CLAUDE.md (plugin + root) + CHANGELOG synced.
Verification: 35 agent-fixture + 59 hook + 20 render tests green. Backward-
compatible (additive state); reload required before the 3 new agents resolve.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
BREAKING CHANGE: the marketplace slug, the agent namespace
(linkedin-studio:<agent>), and the runtime state-file path
(~/.claude/linkedin-studio.local.md) all change. Reinstall required;
existing state migrated in place (post metrics, streak, history preserved).
The /linkedin:* commands are unchanged — the command namespace is set
per-command in frontmatter and was always independent of the plugin slug.
Functionality is byte-identical to v2.4.0; this release is pure identity.
- dir + manifests: plugins/linkedin-studio + plugin.json + root marketplace.json
- agent namespace updated in commands/newsletter.md (only functional invoker)
- state path updated in 4 hook scripts + topic-rotation prompt + state template
- catch-all skill dir renamed skills/linkedin-studio (5 functional skills unchanged)
- docs + version bump to 3.0.0 across README badge, CHANGELOG, root README/CLAUDE.md
- historical records (CHANGELOG past entries, docs/ build artifacts,
config-audit v5.0.0 snapshots) intentionally retain the old slug
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Endring 8 from the change spec (Del 4 production, Maskinrommet). The persona
resonance sweep measures reader-response (does it land?); nothing measured prose
craft or narrative architecture (is it well-made?). In Del 4 every persona
reported PASS, yet the editor found 8 fresh editorial points on first reading —
~6/8 craft/architecture blind spots no agent could see. v2.4.0 adds the missing
editor role.
New Step 5.5 (editorial-review) runs between fact-check (Step 5) and the persona
sweep (Step 6): a new editorial-reviewer agent (Opus) judges two axes —
prosa-handverk (em-dash density, verbatim repetition, postulated numbers,
contradictions, versal-tic) + narrativ-arkitektur (concrete instantiation,
theory-anchored hypotheses, series-title symmetry, equal action per addressee,
un-overloaded conclusion). Returns <=10 flags as direction (never copy), each
BLOCK/REWORK/NICE, operator-gated via SendUserFile. Runs before the persona
sweep so the personas measure resonance instead of stumbling on craft noise.
Mirrors the Maskinrommet writing-contract section C2 (bidirectional mirror rule).
- agents/editorial-reviewer.md (NEW, Opus, orange) + fasit fixture
(editorial-reviewer-cases.md: Del 4 v5 gold standard, 8 points -> 2 axes +
severities, 3 BLOCK / 5 REWORK, 6/8 blind spots) + structural lint (7 tests).
- Step 5.5 wired into commands/newsletter.md; pipeline 14 -> 15 phases.
- editorial-review phase + additive editorialReview state in
config/edition-state.template.json; resumption: factcheck-sweep -> Step 5.5,
editorial-review -> Step 6 (spec said fact-check; canonical key is
factcheck-sweep).
- persona-reviewer contract unchanged: editorial-reviewer is supplementary
(one measures craft, one measures response).
- All doc levels synced (plugin + root README/CLAUDE.md, CHANGELOG, plugin.json
2.3.0 -> 2.4.0; agents 15 -> 16). 94 tests green.
Acceptance-criterion #8 (live run on Del 4 v5) delivered as fasit fixture:
a live run needs a session reload (new agent not invokable until then) + read
access to the Del 4 v5 draft in Maskinrommet.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Implements the 6-change spec from the Seres-serien production
(linkedin-plugin-endringsspec.md). All acceptance criteria met.
1. Avoid-patterns (modell-/navne-katalog, completeness-over-reader-action,
self-referential overhead openings) → longform-quality-rules.md (rule 1+3)
+ user-profile.template.md.
2. Persona gate now BLOCKING with explicit hard-fail list (primær mistet meg /
doesn't own action / sjargong-mur / modell-navne-katalog → BLOCK;
"JA med store forbehold" = NEI) → persona-reviewer.md + personas.template.md.
3. Fact-check declared orthogonal to narrative strength + post-cutoff
web-search mandate + high-frequency-error checklist → fact-checker.md.
4. NEW agent voice-scrubber.md (Opus) — de-AI scrub + Norwegian-chronicle
voice-drift; gold standard = approved Norwegian editions, NOT the English
post corpus. Wired into newsletter.md Step 4.
5. Operator gates = render+annotate rounds (build-html.mjs to file://) as
primary flow, AskUserQuestion as receipt/fallback → newsletter.md 2.5+3a.
6. Edition state reconciled with STATE.md (ONE-system). edition-HANDOVER
template deleted; narrative to <serie>/STATE.md, machine data
(factcheckLog, personaSweep, immutableRules) to edition-state.json.
Agents 14 to 15; commands unchanged (24). Backward-compatible (additive
state-shape only). Docs updated across all three levels + CHANGELOG.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Two new pipeline phases gate the spine before any prose is written:
- Step 2.5 — Skeleton + section pitch: writes <serie>/NN-skjelett.md with
the five-line spine (premiss / problem / anbefaling / gevinst / vei
videre) + one-line section pitches. Operator-gate (JA / REVIDER / NEI)
AND parallel persona-skjelett-sweep must both return JA before the
pipeline can advance.
- Step 3a — Spine prose: one paragraph per section against the gated
skeleton, ~20-30% of final edition length. Operator-gate on whether the
axis lands now that there is prose on it. Old Step 3 (Draft) split into
3a (spine) and 3b (full expansion); 3b owns the multi-session
draft-cursor logic.
Third persona-reviewer mode added: skjelett (alongside resonans + konverter).
Five spine axes scored HOLDER / TVILER / MANGLER, max 3 direction-only flags,
per-pitch section-pay-in check. Reads the skeleton + pitches only.
Pipeline grows from 11 to 13 phases; commands (24) and agents (14) counts
unchanged. Encodes the Maskinrommet writing-contract section A discipline
(premiss / problem / anbefaling / gevinst / vei videre) into the pipeline.
Empirically motivated by the Seres-serien Del 3 + Del 4 production:
a spine error caught at the skeleton stage costs 5-15 min, the same
error caught at Step 6 (resonance) costs 4-12 h, post-lock it costs a
day of cascading rework (delingstekst, hooks, carousel, doc refs).
Backward-compatible: existing editions stop at currentPhase: "research"
and now resume at Step 2.5 instead of Step 3 — an intended deterministic
improvement, never a contract break. Steps 1, 2, 4-10 bit-for-bit
unchanged. Renderers (build-html.mjs, build-linkedin.mjs) untouched.
New phase strings in edition-state.template.json _doc.phases:
- skeleton-pitch (between research and draft)
- spine-prose (between skeleton-pitch and draft)
Files changed (10):
- plugin.json: 2.0.0 -> 2.1.0
- CHANGELOG.md: new [2.1.0] entry
- CLAUDE.md (plugin + marketplace): pipeline 11->13 phases noted
- README.md (plugin + marketplace): What's New v2.1.0 + version row
- agents/persona-reviewer.md: third mode skjelett added; resonans + konverter unchanged
- commands/newsletter.md: Step 2.5 + 3a + 3b sections, resumption + pipeline tables
- config/edition-state.template.json: 11 -> 13 phases in _doc.phases
- references/longform-quality-rules.md: Rule 8 (Skjelett foer prosa)
Verification: 9/9 criteria PASS pre-commit. Phase strings consistent
across template + command + resumption table. Renderer files git-untouched.
All 11 original step headings preserved (Step 0/1/2/4-10).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Step 17 of voyage-build (S16 in plan). publish.md absorbed into calendar.md
as an inline action (Mark as Published flow: queue update, state update,
first-hour battle plan) reusing the same queue-manager.mjs + state-updater.mjs
primitives that publish.md called. calendar.md frontmatter triggers extended
with the publish trigger words; quick-routing block jumps straight to the
publish action when the user prompt names it.
All 21 route-refs reconciled across the 9 expected files, with the 9
hook-script refs (5 in session-start.mjs, 2 in posting-reminder.mjs, 1 in
user-prompt-context.mjs, 1 in hooks/prompts/state-update-reminder.md)
rewritten to call /linkedin:calendar so the runtime guidance no longer
points at a dead command. compile-hooks.py --check reports clean (no
type: prompt hook changes touched hooks.json).
Verify (intent: zero stray refs, file gone): exit 0. Literal Verify in
plan.md:727 logged exit 1 (same exit-inverted && pattern as S15 plan.md:635
— logged for plan-pass at Step 21).
Manifest audit: PASS (expected_paths=calendar.md present; must_contain
[Pp]ublish: 17 matches in calendar.md).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>