ktg-plugin-marketplace/plugins/linkedin-thought-leadership/CHANGELOG.md
Kjell Tore Guttormsen 7ebd28cb0b feat(linkedin): v2.3.0 — Step 7.5 visual-assets phase in /linkedin:newsletter
Endring 7 from the change-spec: make visual assets an explicit pipeline
phase. New Step 7.5 (visual-assets) between annotation (Step 7) and lock
(Step 8): cover (+ optional inline figures) or carousel deck, generated and
operator-gated BEFORE lock so build-linkedin.mjs picks up cover.png at lock
without a post-lock re-render. Pipeline 13 → 14 phases.

- commands/newsletter.md: Step 7.5 section, pipeline overview + build-status,
  resumption table (annotation → 7.5; new visual-assets → 8), Step 8
  precondition, reference-file list.
- config/edition-state.template.json: visual-assets phase + additive
  articles.NN.visualAssets schema (format / cover / figures / carousel).
- config/image-credit-caption.template.md (new): motif + credit + caption
  table, honest-about-AI credit, naming convention.
- Two generation routes, no lock-in: default mcp-image (cover-v<N>-kandidat.png)
  or external cover-raw.png. Operator-gate via SendUserFile → cp to cover.png.
  Carousel branch reuses render/build-carousel.mjs.
- Doc/orchestration-only — no new code. Commands (24) + agents (15) unchanged.
- Version sync 2.2.0 → 2.3.0 across plugin.json, CHANGELOG, README, CLAUDE.md,
  root README + root CLAUDE.md.

Correction: spec claimed build-linkedin.mjs handles fig1-4; verified it does
NOT — it embeds only cover.png by fixed name; figures are referenced in the
draft markdown and uploaded manually. Step 7.5 documents actual behavior.

All 8 acceptance criteria met. JSON valid (14 phases); 20/20 render tests pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-28 22:49:19 +02:00

217 lines
22 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [2.3.0] - 2026-05-28
### Summary
Visual assets become an explicit pipeline phase in `/linkedin:newsletter`. Until now images (cover + inline figures) were produced ad-hoc *outside* the 13-phase pipeline and referenced manually from `edition-config.json` + `linkedin/NN/cover.png` — even though a cover is mandatory (KTG cover-directive 27.05: «TLDR on top + at least one figure per article») and must coordinate with the text. v2.3.0 adds **Step 7.5 — Visual assets**, between annotation (Step 7) and lock (Step 8), so the cover is generated, operator-gated, and approved *before* lock — `render/build-linkedin.mjs` picks up `linkedin/NN/cover.png` at lock, so generating images after lock would force a re-render and break the lock. Pipeline 13 → 14 phases. Doc/orchestration-only — no new code; mcp-image is the default generation route but the interface stays pluggable (external `cover-raw.png` accepted), and the carousel branch reuses the existing `render/build-carousel.mjs`. Backward-compatible: the only state-shape change is additive (`visualAssets`), and existing editions resume by `currentPhase` (`annotation` now resumes at Step 7.5 instead of Step 8 — an intended deterministic improvement).
### Added
- **Step 7.5 — Visual assets** in `commands/newsletter.md` (between Step 7 annotation and Step 8 lock). Decides image needs from the article type (method-heavy → 12 inline figures, diagnosis-heavy → cover only), writes a per-image brief, generates via two routes (default `mcp__mcp-image__generate_image``cover-v<N>-kandidat.png`; external → `cover-raw.png`), runs the operator-gate (candidates surfaced via `SendUserFile`, approval copied to the fixed `cover.png` name — the same render+annotate pattern as Steps 2.5/3a), and records credit + caption. Explicit **carousel branch** (`format: "carousel"`): render the deck via `render/build-carousel.mjs` instead of cover+inline.
- **`visual-assets` phase string** + **`articles.NN.visualAssets` schema** in `config/edition-state.template.json` `_doc.phases` (13 → 14 phases) — `{ format, cover: { brief, route, candidates[], approved, status }, figures: [ { id, brief, placement, status } ], carousel }`. Additive; default `format: "standard"`.
- **`config/image-credit-caption.template.md`** (new) — cover motif + credit + caption table, modelled on the established Seres-serien `image-credit-caption.md`. Honest-about-AI credit per the verification duty; documents the cover/figure naming convention.
- **Naming convention documented** in `commands/newsletter.md` Step 7.5 — `cover.png` (approved, fixed) / `cover-v<N>-kandidat.png` (attempts) / `cover-raw.png` (external pre-edit source) / `fig<N>.png` (inline). Consistent with existing series use.
### Changed
- **`/linkedin:newsletter` pipeline overview** — 13 → 14 phases; the overview table, build-status note, and reference-file list reflect Step 7.5.
- **Resumption table** in `commands/newsletter.md``currentPhase: "annotation"` now resumes at **Step 7.5** (was Step 8); new `visual-assets` row resumes at Step 8. `persona-sweep-prelock` flows Step 7 → Step 7.5.
- **Step 8 lock preconditions** now require the gated `visualAssets` (approved `cover.png` for `standard` format, or approved `carousel.pdf` for `carousel` format) alongside the existing fact-check (no open 🔴) and primær-JA gates.
- README, CLAUDE.md, root README, root CLAUDE.md, plugin.json version + descriptions.
### Not changed (explicit non-deltas)
- **Steps 07, 8 body, 9, 10** in `/linkedin:newsletter` — contract unchanged apart from the Step 7→7.5→8 wiring and the additive Step 8 precondition.
- **Renderers** — `render/build-linkedin.mjs`, `build-html.mjs`, `build-carousel.mjs` untouched (Step 7.5 *calls* them; no code change). `build-linkedin.mjs` still reads `cover.png` by fixed name and does not embed `fig<N>.png` (figures are referenced in the draft and uploaded manually) — Step 7.5 documents this actual behavior rather than overstating it.
- **Hooks, scripts, command count (24), agent count (15)** — all unchanged.
## [2.2.0] - 2026-05-28
### Summary
Longform gates hardened with the lessons from the second `/linkedin:newsletter` production run (Seres-serien). A chronicle built as a model/name catalog passed persona review (its flags were read as notes, not stop-signs) and nearly shipped; the rewrite was stronger but introduced fresh factual errors. v2.2.0 closes six concrete weaknesses: the persona gate becomes blocking with an explicit hard-fail list, fact-check is made orthogonal to narrative strength (more polish → more verification) with a post-cutoff web-search mandate, a new Norwegian-chronicle de-AI voice-scrubber is added and wired into Step 4, operator gates become render+annotate rounds, and per-edition production state is reconciled with the global STATE.md continuity system (no more `edition-HANDOVER.md`). 14 → 15 agents; commands unchanged (24). Backward-compatible — the only state-shape change is additive.
### Added
- **`agents/voice-scrubber.md`** (new, Opus) — aggressive de-AI scrubber + voice-drift corrector for long-form **Norwegian chronicle** drafts. Pass 1 strips objective AI-tells («la meg være ærlig», reflex rule-of-three, em-dash-spam, self-referential overhead, modell-/navne-katalog); Pass 2 corrects drift toward the chronicle voice; Pass 3 appends to a chronicle-voice-drift-log so it sharpens over editions. **Calibration rule (cardinal):** gold standard = the approved Norwegian editions, NEVER `assets/voice-samples/authentic-voice-samples.md` (English short-form, forbids the em-dash). *New agent — requires a session reload before it is invokable.*
- **De-AI / voice scrub sub-pass** in `commands/newsletter.md` Step 4 — fans out `voice-scrubber` (foreground, namespaced) with the draft + approved-Norwegian-edition paths as the gold standard.
- **Blocking hard-fail list** in `agents/persona-reviewer.md` + `config/personas.template.md` — primær «mistet meg» / doesn't own the action / sjargong-mur / modell-/navne-katalog → BLOCK regardless of other axes. «JA med store forbehold» = NEI.
- **Post-cutoff fact-check mandate + high-frequency-error checklist** in `agents/fact-checker.md` — claims dated after the model's knowledge cutoff MUST be web-searched; explicit checks for person titles, org-varying "standards", over-credited studies, source scope, and founding/release years. Fact-check declared orthogonal to narrative strength.
- **Render+annotate operator gates** in `commands/newsletter.md` Steps 2.5 + 3a — HTML annotation via `render/build-html.mjs``file://` link is the primary operator-review flow; `AskUserQuestion` becomes a receipt + fallback.
- **Avoid-patterns** — modell-/navne-katalog, completeness-over-reader-action, self-referential overhead openings added to `references/longform-quality-rules.md` (rules 1 + 3) and `config/user-profile.template.md`.
- **`personaSweep.skeleton` + `immutableRules`** fields in `config/edition-state.template.json` (additive).
### Changed
- **Edition production state reconciled with STATE.md (ONE-system).** `commands/newsletter.md` Step 0 now reads `<serie>/STATE.md` (auto-injected by the session-start hook); every phase writes narrative status to `<serie>/STATE.md` (overwrite) and machine state (fact-check log, persona verdicts, immutable rules) to `edition-state.json`. All `HANDOVER §4/§5/§6` references replaced.
- `agents/fact-checker.md` principle 3 strengthened to make web search mandatory for post-cutoff claims.
- README, CLAUDE.md, root README, plugin.json version + descriptions.
### Removed
- **`config/edition-HANDOVER.template.md`** — deleted. The plugin no longer ships or requires a separate handover mechanism; `<serie>/STATE.md` + `edition-state.json` carry its content per the global continuity rule.
## [2.1.0] - 2026-05-28
### Summary
Skeleton gate before prose in `/linkedin:newsletter`. Two new pipeline phases (Step 2.5 — Skeleton + section pitch; Step 3a — Spine prose) split the old Step 3 into pre-prose / spine-prose / full-prose stages, each with an operator-gate. Adds a third `persona-reviewer` mode (`skjelett`) that judges the five-line skeleton + section pitches BEFORE prose is written. Empirically motivated by the Seres-serien Del 3 + Del 4 production: spine errors caught post-prose cost ~1 day; the same error caught at the skeleton stage costs 515 minutes. 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.
### Added
- **Step 2.5 — Skeleton + section pitch** in `commands/newsletter.md`. 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 advances. Encodes the Maskinrommet writing-contract §A discipline into the pipeline itself.
- **Step 3a — Spine prose** in `commands/newsletter.md`. One paragraph per section against the gated skeleton, ~2030 % of final edition length. Operator-gate on whether the axis lands now that there is prose on it. Cheap second checkpoint before full expansion.
- **Step 3b — Full prose expansion** in `commands/newsletter.md`. Splits the old Step 3 (Draft) into spine prose (3a) and full prose expansion (3b). 3b owns the existing draft-cursor logic for multi-session expansion; 3a is short enough to restart on interruption.
- **`persona-reviewer` skjelett-mode** (third mode alongside `resonans` and `konverter`). Five spine axes (Premiss / Problem / Anbefaling / Gevinst / Vei videre) scored HOLDER / TVILER / MANGLER, ≤3 direction-only flags, per-pitch section-pay-in check, gate ladder PASS / REWORK / BLOCK. Caller passes `mode: skjelett`.
- **`skeleton-pitch` + `spine-prose` phase strings** in `config/edition-state.template.json` `_doc.phases` — 11 → 13 phases. Resumption table in `commands/newsletter.md` extended with deterministic rows for both new phases.
- **Rule 8 — Skjelett før prosa** in `references/longform-quality-rules.md`. Documents the skeleton-before-prose pre-condition that all other rules implicitly rely on, with the same five-slot format the pipeline enforces.
### Changed
- **`/linkedin:newsletter` pipeline overview** — 11 → 13 phases; pipeline tables in `commands/newsletter.md` and `CLAUDE.md` reflect the new ordering (0, 1, 2, **2.5**, **3a**, **3b**, 410).
- **Resumption table** in `commands/newsletter.md``currentPhase: "research"` now resumes at Step 2.5 (was Step 3). Two new rows added for `skeleton-pitch` (→ Step 3a) and `spine-prose` (→ Step 3b). Draft-cursor note clarifies that the cursor applies only to Step 3b.
- **`agents/persona-reviewer.md` description, principles, and anti-patterns** — extended to cover the third mode (skjelett). Existing resonans + konverter modes unchanged in contract.
- **`CLAUDE.md` header + persona-reviewer row + newsletter command row** — reflect v2.1.0 surface.
### Not changed (explicit non-deltas)
- **Step 1, Step 2, Steps 410** in `/linkedin:newsletter` — bit-for-bit unchanged in contract.
- **Renderers** — `render/build-html.mjs` and `render/build-linkedin.mjs` untouched; both still consume `NN-utkast.md` (3a writes the spine state, 3b overwrites with the full state, but only `currentPhase: "draft"` triggers rendering).
- **Hooks, scripts, command count (24), agent count (14)** — all unchanged.
## [2.0.0] - 2026-05-28
### Summary
Full-spectrum LinkedIn content engine — short-form feed posts AND long-form newsletter editions in one cohesive surface, with net-fewer commands and net-stronger pipeline. Built across 21 Voyage sessions (S1..S20+S1a) with 1 step = 1 session discipline. Locked decisions AH in `docs/voyage-build-brief.md` §3.
### Added
- **`/linkedin:newsletter`** — long-form orchestrator command. Multi-session pipeline: load → calibrate → research fan-out → draft → consistency/quality → fact-check sweep → persona sweep → annotate → lock → delivery → hook-gate → schedule. Maintained `edition-state.json` across sessions. Supports newsletter editions, essays, and series articles
- **`/linkedin:outreach`** — outreach orchestrator (absorbed `/linkedin:collab` and `/linkedin:speaking`). Covers collaborations, partner pitches, and CFPs/speaking opportunities in one surface
- **`agents/fact-checker.md`** (Opus, brown) — verifies every factual claim in long-form drafts against primary sources. Outputs 🟢/🔴/🟡 verdicts per claim. Runs BEFORE lock
- **`agents/persona-reviewer.md`** (Opus, olive) — evaluates reader-persona resonance + hook-conversion gate. Two modes: per-persona deep review, multi-persona scoreboard. Runs BEFORE lock
- **`render/` pipeline migrated in-plugin** — `build-html.mjs`, `build-pdf.mjs`, `build-linkedin.mjs`, `build-carousel.mjs`. Self-hosted fonts (Newsreader, Inter, JetBrains Mono) under OFL-1.1 with `render/OFL.txt`. WeasyPrint degradation: missing binary → skip-signal, not throw
- **`config/personas.template.md`** — reader persona library. Knowledge level, time-pressure, resonance criteria per persona. Consumed by `persona-reviewer`
- **`config/edition-state.template.json`** — schema for long-form edition state across sessions
- **`references/longform-quality-rules.md`** — quality bar specific to long-form (different from short-form rules)
- **Router gating** — `/linkedin:monetize` and `/linkedin:outreach` surface "unlocks at ~1K followers" guidance and point sub-1K users at `/linkedin:strategy` first
- **`docs/agents-capability-matrix.md`** — single source of truth for which agent owns which capability. Pipeline diagram + intent table + model tier table
### Changed
- **Agent merges (16 → 14):**
- `performance-reporter``analytics-interpreter` (interpret + report modes, same data sources, mode-selector by trigger phrase)
- `comment-strategist``engagement-coach` (5x5x5 + first-hour + CEA method + target scoring + daily routine + comment quality scorecard; upgraded haiku → sonnet since the agent now handles deeper work)
- `content-tracker` → absorbed by `state-updater.mjs` + `analytics-interpreter`
- `personalization-scorer` → absorbed by `personalization-score.mjs` (deterministic, no AI)
- **Command merges (27 → 24):**
- `/linkedin:templates` → mode in `/linkedin:quick` (8 post-type templates)
- `/linkedin:publish` → action in `/linkedin:calendar` (mark scheduled posts as published)
- `/linkedin:authority` → absorbed into `/linkedin:strategy` (canon for authority building, trajectory dedup)
- `/linkedin:collab` + `/linkedin:speaking``/linkedin:outreach`
- **`/linkedin:import` Step 6 analysis** — delegated to `/linkedin:report` (both consume the same `trends` CLI; no more duplicated analysis pipeline)
- **`commands/linkedin.md` router** — newsletter row added, removed-command rows pruned, gating-rule paragraph for monetize/outreach
- **All 6 skill catalogs** reconciled — `linkedin-content-creation`, `linkedin-analytics`, `linkedin-strategy`, `linkedin-networking`, `linkedin-thought-leadership`, `linkedin-voice` all reflect the v2.0.0 command/agent set
### Removed
- `commands/templates.md` (absorbed into `commands/quick.md`)
- `commands/publish.md` (absorbed into `commands/calendar.md`)
- `commands/authority.md` (absorbed into `commands/strategy.md`)
- `commands/collab.md` (absorbed into `commands/outreach.md`)
- `commands/speaking.md` (absorbed into `commands/outreach.md`)
- `agents/content-tracker.md`
- `agents/personalization-scorer.md`
- `agents/performance-reporter.md`
- `agents/comment-strategist.md`
### Fixed
- `references/glossary.md` "Authority Score" entry — corrected stale ref to `commands/authority.md` (removed) → `commands/strategy.md` (canon)
- `scripts/test-runner.sh` `EXPECTED_AGENTS` list — reconciled to 14 agents
### Migration notes
- Plugin remains fully backward-compatible from a user-perspective: removed commands now route to their absorbing command via `commands/linkedin.md`
- v1.x users who had `commands/templates.md` etc. in muscle memory will be auto-redirected by the router
## [1.2.0] - 2026-04-11
### Summary
Friction reduction release. Fewer interactive steps, auto-clipboard, deterministic state management, and progressive onboarding.
### Added
- **`clipboard-helper.mjs`** — cross-platform clipboard utility (macOS `pbcopy`, Linux `xclip`/`xsel`, WSL `clip.exe`). All 8 content commands auto-copy output to clipboard
- **`state-updater.mjs`** — deterministic state mutations: `updatePostTracking`, `pruneContentHistory`, `updateFollowerCount`. Pure functions with 19 tests. No AI involvement in state updates
- **`ical-generator.mjs`** — RFC 5545 calendar file generation for batch scheduling. VALARM reminders, VTIMEZONE support, line folding, special character escaping. 16 tests
- **MCP image carousel pipeline** — `/linkedin:carousel` generates professional slide images via mcp-image (1080x1350, 3:4 ratio) with text overlays. Mermaid Chart and text-based fallbacks
- **Progressive onboarding** — personalization score hidden until 3+ posts; voice guardian suppressed until 5+ voice samples; reasonable defaults in state template
- **iCal integration in batch** — `/linkedin:batch` generates `.ics` file importable into macOS Calendar, Google Calendar, and Outlook
- **Auto-prune content history** — session-start dynamically imports `pruneContentHistory` to remove entries older than 90 days
### Changed
- **Reduced interactive steps** — angle, format, and post type inferred from context. Max 2 questions per post (down from 4-6) in `post`, `quick`, `react`, `pipeline`
- **State management** — Stop hook and 8 commands now reference `state-updater.mjs` for deterministic writes instead of AI-driven YAML editing
- **State file template** — default expertise area changed from domain-specific to `"general"` for better new-user experience
## [1.1.0] - 2026-04-08
### Summary
Q2 2026 feature release. 9 improvements across onboarding, content quality, and analytics pipeline.
### Added
- **`/linkedin:onboarding`** — multi-step onboarding wizard: profile → setup → first-post as one guided flow
- **`/linkedin:carousel`** — structured multi-slide carousel generator with 5 templates and design specs
- **Voice drift scoring** — 6-dimension rubric (sentence structure, word choice, openings, storytelling, tone, formatting) with AUTHENTIC/CAUTION/ALERT/REWRITE verdicts in voice-guardian hook
- **Industry angle variants** — 48 concrete variants (6 industries × 8 angles) in thought-leadership-angles reference
- **Multi-URL comparison** — `/linkedin:react` now supports 2-3 URL synthesis with contrarian and pattern analysis angles
- **Day-of-week heatmap** — `heatmap` CLI command and `HeatmapReport` type in analytics pipeline
- **Month-over-month reports** — `report --month YYYY-MM` CLI command with MoM deltas, weekly breakdown, top performers
- **Automated week-rollover** — session-start hook now writes `posts_this_week: 0` and updates `current_week` on ISO week change
- **Collected Post Samples** — Stop hook passively accumulates published posts in voice-samples file for drift scoring
### Changed
- **README Quick Start** — replaced 4-step manual flow with single `/linkedin:onboarding` entry point
- **`/linkedin:report`** — Step 2 now offers report type choice (weekly/monthly/heatmap)
- **`/linkedin:post`** — Step 2 shows industry-specific angles when user-profile has industry set; Step 3 redirects to carousel when appropriate
- **`/linkedin` router** — added onboarding and carousel to menus and direct routing
- **Command count** — 25 → 27 (onboarding, carousel)
## [1.0.0] - 2026-04-07
### Summary
Public release for open-source marketplace. All runtime bugs fixed, documentation aligned, agent model tiering implemented.
### Fixed
- **Agent model assignments** — all 16 agents corrected from opus to proper tiering (12 Sonnet, 4 Haiku)
- **Queue manager references** — 10 stale `queue-manager.sh` references replaced with `queue-manager.mjs` Node.js invocations
- **Quick-import references** — 2 stale `quick-import.sh` references updated to `.mjs`
- **Personalization score import bug** — standalone execution block now guarded to prevent stdout contamination on import
- **Regex anchor** — invalid `\Z` JavaScript regex replaced with `$` in user-prompt-context.mjs
- **Agent color mismatches** — 8 agent frontmatter colors unified with CLAUDE.md documentation
- **Version inconsistency** — unified from 3 conflicting versions (0.6.0/1.7.0/2.0.1) to 1.0.0
### Added
- **plugin.json** — added `license`, `repository`, `keywords` fields for marketplace compliance
- **README** — attribution note, "What This Plugin Does Not Cover" section, Node.js 18+ prerequisite, hooks badge
- **CONTRIBUTING.md** — replaced GitHub PR template with solo-project boilerplate
- **Quality scorecard** — added "Voice Authenticity" criterion (total now /81)
- **Commands** — `/linkedin:react` and `/linkedin:first-post` added to README command tables
- **agents/README.md** — updated from 14 to 16 agents, added personalization-scorer and post-feedback-monitor
- **SKILL.md** — added 5 missing commands to router command table
### Changed
- **CLAUDE.md** — compacted from 237 to 90 lines, removed duplicated content
- **All hooks** — 100% Node.js (.mjs), no bash dependencies (cross-platform: macOS/Linux/Windows)
- **Error handling** — added JSON.parse guards in queue-manager.mjs and analytics storage.ts
### Removed
- **Skill version fields** — removed non-standard `version:` from all 6 SKILL.md frontmatter
- **Development artifacts** — removed internal evaluation note from collab.md
- **Orphaned files** — deleted outdated docs/commands-reference.md
- **BACKLOG.md and DEVELOPMENT-LOG.md** — gitignored (internal development files)
## [0.6.0] - 2026-02-07
### Note
First formal version. Previously unversioned.
### What exists today
- 20 commands covering full content lifecycle
- 15 specialized agents
- 8 hooks for workflow automation
- Analytics system with CSV import
- 360Brew profile optimization (January 2026 algorithm update)
- Content matrix system (40+ post ideas from single topic)
- Personalization engine
- 20 reference documents for LinkedIn best practices
- Full content pipeline from ideation to post-publish monitoring