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>
This commit is contained in:
parent
4ed9717627
commit
7ebd28cb0b
9 changed files with 304 additions and 29 deletions
|
|
@ -9,7 +9,7 @@ plugins/
|
||||||
ai-psychosis/ v1.0.0 — Interaction awareness (sycophancy, reinforcement loops)
|
ai-psychosis/ v1.0.0 — Interaction awareness (sycophancy, reinforcement loops)
|
||||||
config-audit/ v3.1.0 — Configuration intelligence (health, opportunities, auto-fix, whats-active)
|
config-audit/ v3.1.0 — Configuration intelligence (health, opportunities, auto-fix, whats-active)
|
||||||
graceful-handoff/ v2.1.0 — Auto-trigger handoff via Stop hook (skill + JSON pipeline + 4-step model-aware context resolution)
|
graceful-handoff/ v2.1.0 — Auto-trigger handoff via Stop hook (skill + JSON pipeline + 4-step model-aware context resolution)
|
||||||
linkedin-thought-leadership/ v2.2.0 — Full-spectrum LinkedIn content engine (short-form feed + long-form newsletter). v2.0.0 consolidated surface (27→24 commands, 16→14 agents) + added `/linkedin:newsletter` orchestrator with fact-check + persona-sweep gates BEFORE lock. v2.1.0 added skeleton-gate BEFORE prose (Step 2.5 + Step 3a) + third `persona-reviewer` mode (`skjelett`); pipeline 11→13 phases. v2.2.0 hardens the longform gates (2nd production run): blocking persona hard-fails (mistet meg / doesn't own action / sjargong-mur / modell-navne-katalog → BLOCK), fact-check post-cutoff web-search mandate + orthogonal-to-narrative rule, new `voice-scrubber` agent (Opus, de-AI + Norwegian-chronicle voice; gold standard = approved Norwegian editions NOT English post corpus), render+annotate operator gates (2.5/3a), and edition-state reconciled with STATE.md (`edition-HANDOVER.md` deleted). Agents 14→15; commands unchanged (24). Render pipeline self-hosted (OFL-1.1 fonts).
|
linkedin-thought-leadership/ v2.3.0 — Full-spectrum LinkedIn content engine (short-form feed + long-form newsletter). v2.0.0 consolidated surface (27→24 commands, 16→14 agents) + added `/linkedin:newsletter` orchestrator with fact-check + persona-sweep gates BEFORE lock. v2.1.0 added skeleton-gate BEFORE prose (Step 2.5 + Step 3a) + third `persona-reviewer` mode (`skjelett`); pipeline 11→13 phases. v2.2.0 hardened the longform gates (2nd production run): blocking persona hard-fails, fact-check post-cutoff web-search mandate + orthogonal-to-narrative rule, new `voice-scrubber` agent (Opus, de-AI + Norwegian-chronicle voice; gold standard = approved Norwegian editions NOT English post corpus), render+annotate operator gates (2.5/3a), edition-state reconciled with STATE.md (`edition-HANDOVER.md` deleted); agents 14→15. v2.3.0 adds **Step 7.5 (visual-assets)** to `/linkedin:newsletter` — cover (+ inline figures) or carousel deck, generated (default mcp-image; external `cover-raw.png` accepted) + operator-gated via `SendUserFile` BEFORE lock so `build-linkedin.mjs` picks up `cover.png` without a post-lock re-render; pipeline 13→14 phases, new `config/image-credit-caption.template.md`, additive `visualAssets` state, doc/orchestration-only (no new code). Commands unchanged (24); agents 15. Render pipeline self-hosted (OFL-1.1 fonts).
|
||||||
llm-security/ v7.7.2 — Security scanning, auditing, threat modeling. HTML report output for all 18 skill commands (render-report CLI + canonical ESM module mirrored bit-identical into the playground). v7.7.2 translated the remaining Norwegian surface text in the playground UI, the canonical renderer, the agent prompts, and the README/CLAUDE.md state sections to English. v7.7.1 stripped the playground to the catalog as the only routable surface.
|
llm-security/ v7.7.2 — Security scanning, auditing, threat modeling. HTML report output for all 18 skill commands (render-report CLI + canonical ESM module mirrored bit-identical into the playground). v7.7.2 translated the remaining Norwegian surface text in the playground UI, the canonical renderer, the agent prompts, and the README/CLAUDE.md state sections to English. v7.7.1 stripped the playground to the catalog as the only routable surface.
|
||||||
ms-ai-architect/ v1.15.0 — Microsoft AI architecture (Cosmo Skyberg persona) + manual KB-refresh slash command + v3 project-view (sidebar med 17 artifacts + main + import-modal overlay, v2-surface fjernet i v1.15.0)
|
ms-ai-architect/ v1.15.0 — Microsoft AI architecture (Cosmo Skyberg persona) + manual KB-refresh slash command + v3 project-view (sidebar med 17 artifacts + main + import-modal overlay, v2-surface fjernet i v1.15.0)
|
||||||
okr/ v1.0.0 — OKR guidance for Norwegian public sector
|
okr/ v1.0.0 — OKR guidance for Norwegian public sector
|
||||||
|
|
|
||||||
|
|
@ -206,19 +206,20 @@ Key commands: `/architect`, `/architect:ros`, `/architect:security`, `/architect
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### [LinkedIn Thought Leadership](plugins/linkedin-thought-leadership/) `v2.2.0`
|
### [LinkedIn Thought Leadership](plugins/linkedin-thought-leadership/) `v2.3.0`
|
||||||
|
|
||||||
Build authentic LinkedIn authority through algorithmic understanding, strategic consistency, and AI-assisted content creation.
|
Build authentic LinkedIn authority through algorithmic understanding, strategic consistency, and AI-assisted content creation.
|
||||||
|
|
||||||
v2.2.0 **hardens the longform gates** with the lessons from the second `/linkedin:newsletter` production run (Seres-serien): the persona gate is now **blocking with an explicit hard-fail list** (primær «mistet meg» / doesn't own the action / sjargong-mur / modell-/navne-katalog → BLOCK; «JA med store forbehold» = NEI); fact-check is **orthogonal to narrative strength** with a **post-cutoff web-search mandate** + high-frequency-error checklist; a new **`voice-scrubber`** agent (Opus) does de-AI scrub + Norwegian-chronicle voice-drift correction (gold standard = approved Norwegian editions, NOT the English post corpus); operator gates become **render+annotate** rounds; and per-edition state is **reconciled with the global STATE.md** continuity system (`edition-HANDOVER.md` removed). Agents 14 → 15; commands unchanged (24). v2.1.0's skeleton-gate-before-prose and v2.0.0's full-spectrum surface are preserved. Updated for the January 2026 360Brew algorithm change, which validates your creator profile before distributing content.
|
v2.3.0 makes **visual assets an explicit pipeline phase** in `/linkedin:newsletter`: new **Step 7.5 (visual-assets)** between annotation (Step 7) and lock (Step 8). The cover (+ optional inline figures) or a carousel deck is generated (default `mcp-image`; external `cover-raw.png` accepted) and operator-gated via `SendUserFile` **before lock**, so `render/build-linkedin.mjs` picks up `cover.png` + the edition-config credit/caption at lock without a post-lock re-render. Pipeline 13 → 14 phases; new `config/image-credit-caption.template.md` + additive `visualAssets` state; doc/orchestration-only (no new code). v2.2.0 **hardened the longform gates** with the lessons from the second production run (Seres-serien): the persona gate is **blocking with an explicit hard-fail list** (primær «mistet meg» / doesn't own the action / sjargong-mur / modell-/navne-katalog → BLOCK); fact-check is **orthogonal to narrative strength** with a **post-cutoff web-search mandate**; a **`voice-scrubber`** agent (Opus) does de-AI scrub + Norwegian-chronicle voice-drift correction; operator gates became **render+annotate** rounds; and per-edition state was **reconciled with the global STATE.md** continuity system (`edition-HANDOVER.md` removed). Commands unchanged (24); 15 agents. v2.1.0's skeleton-gate-before-prose and v2.0.0's full-spectrum surface are preserved. Updated for the January 2026 360Brew algorithm change, which validates your creator profile before distributing content.
|
||||||
|
|
||||||
|
- **Visual-assets gate BEFORE lock (v2.3)** — Step 7.5 decides image needs from the article type (method-heavy → 1–2 inline figures; diagnosis-heavy → cover only), briefs each image, generates via mcp-image or an external `cover-raw.png`, surfaces candidates with `SendUserFile`, and copies the approved one to the fixed `cover.png` name. Explicit `format: "carousel"` branch reusing `build-carousel.mjs`.
|
||||||
- **Blocking persona hard-fails (v2.2)** — `persona-reviewer` + `personas.template.md` make primær «mistet meg», doesn't-own-the-action, sjargong-mur, and modell-/navne-katalog BLOCK-level rewrites, not annotations. The bar is the primær reader's *clean* JA.
|
- **Blocking persona hard-fails (v2.2)** — `persona-reviewer` + `personas.template.md` make primær «mistet meg», doesn't-own-the-action, sjargong-mur, and modell-/navne-katalog BLOCK-level rewrites, not annotations. The bar is the primær reader's *clean* JA.
|
||||||
- **Fact-check orthogonal to polish (v2.2)** — `fact-checker` web-searches every post-cutoff claim (never confirms from memory) and runs an explicit checklist for person titles, org-varying "standards", over-credited studies, source scope, and founding/release years.
|
- **Fact-check orthogonal to polish (v2.2)** — `fact-checker` web-searches every post-cutoff claim (never confirms from memory) and runs an explicit checklist for person titles, org-varying "standards", over-credited studies, source scope, and founding/release years.
|
||||||
- **New `voice-scrubber` agent (v2.2, Opus)** — aggressive de-AI scrub (Pass 1) + Norwegian-chronicle voice-drift correction (Pass 2), calibrated to the **approved Norwegian editions** rather than the English post corpus; wired into Step 4. *New agent — requires a session reload before invokable.*
|
- **New `voice-scrubber` agent (v2.2, Opus)** — aggressive de-AI scrub (Pass 1) + Norwegian-chronicle voice-drift correction (Pass 2), calibrated to the **approved Norwegian editions** rather than the English post corpus; wired into Step 4. *New agent — requires a session reload before invokable.*
|
||||||
- **Render+annotate operator gates (v2.2)** — Steps 2.5/3a surface an annotatable `file://` review page (`build-html.mjs`) as the primary operator-review flow; `AskUserQuestion` becomes a receipt + fallback.
|
- **Render+annotate operator gates (v2.2)** — Steps 2.5/3a surface an annotatable `file://` review page (`build-html.mjs`) as the primary operator-review flow; `AskUserQuestion` becomes a receipt + fallback.
|
||||||
- **STATE.md-reconciled edition state (v2.2)** — narrative state in `<serie>/STATE.md` (auto-injected, overwritten each phase), machine state in `edition-state.json`; no separate `edition-HANDOVER.md`.
|
- **STATE.md-reconciled edition state (v2.2)** — narrative state in `<serie>/STATE.md` (auto-injected, overwritten each phase), machine state in `edition-state.json`; no separate `edition-HANDOVER.md`.
|
||||||
- **Skeleton gate BEFORE prose (v2.1)** — `/linkedin:newsletter` Step 2.5 writes a five-line spine (premiss / problem / anbefaling / gevinst / vei videre) + one-line section pitches to `<serie>/NN-skjelett.md`. Operator-gate AND parallel persona-skjelett-sweep must both return JA before the pipeline can advance. Step 3a follows with spine prose (one paragraph per section) and its own operator-gate. Encodes the Maskinrommet writing-contract §A discipline into the pipeline.
|
- **Skeleton gate BEFORE prose (v2.1)** — `/linkedin:newsletter` Step 2.5 writes a five-line spine (premiss / problem / anbefaling / gevinst / vei videre) + one-line section pitches to `<serie>/NN-skjelett.md`. Operator-gate AND parallel persona-skjelett-sweep must both return JA before the pipeline can advance. Step 3a follows with spine prose (one paragraph per section) and its own operator-gate. Encodes the Maskinrommet writing-contract §A discipline into the pipeline.
|
||||||
- **Long-form `/linkedin:newsletter` orchestrator** — multi-session 13-phase pipeline (research → **skeleton + persona-skjelett-sweep** → **spine prose** → full prose draft → **de-AI/voice scrub** → fact-check sweep → persona sweep → lock → delivery → hook-gate) with maintained edition-state. Newsletter editions, essays, series articles
|
- **Long-form `/linkedin:newsletter` orchestrator** — multi-session 14-phase pipeline (research → **skeleton + persona-skjelett-sweep** → **spine prose** → full prose draft → **de-AI/voice scrub** → fact-check sweep → persona sweep → annotation → **visual assets (cover/figures or carousel, BEFORE lock)** → lock → delivery → hook-gate) with maintained edition-state. Newsletter editions, essays, series articles
|
||||||
- **Three longform-quality gate agent modes** — `fact-checker` (Opus, verifies every claim against primary sources) and `persona-reviewer` (Opus) with three modes: **`skjelett`** (Step 2.5, before prose), **`resonans`** (Step 6, before lock), **`konverter`** (Step 9, after lock)
|
- **Three longform-quality gate agent modes** — `fact-checker` (Opus, verifies every claim against primary sources) and `persona-reviewer` (Opus) with three modes: **`skjelett`** (Step 2.5, before prose), **`resonans`** (Step 6, before lock), **`konverter`** (Step 9, after lock)
|
||||||
- **Render pipeline in-plugin** — `build-html.mjs`, `build-pdf.mjs`, `build-linkedin.mjs`, `build-carousel.mjs` with self-hosted Newsreader/Inter/JetBrains Mono under OFL-1.1
|
- **Render pipeline in-plugin** — `build-html.mjs`, `build-pdf.mjs`, `build-linkedin.mjs`, `build-carousel.mjs` with self-hosted Newsreader/Inter/JetBrains Mono under OFL-1.1
|
||||||
- **Guided onboarding** — `/linkedin:onboarding` walks new users through profile → setup → first post in one flow
|
- **Guided onboarding** — `/linkedin:onboarding` walks new users through profile → setup → first post in one flow
|
||||||
|
|
@ -233,7 +234,7 @@ v2.2.0 **hardens the longform gates** with the lessons from the second `/linkedi
|
||||||
|
|
||||||
Key commands: `/linkedin:onboarding`, `/linkedin:post`, `/linkedin:quick`, `/linkedin:newsletter`, `/linkedin:carousel`, `/linkedin:react`, `/linkedin:report`
|
Key commands: `/linkedin:onboarding`, `/linkedin:post`, `/linkedin:quick`, `/linkedin:newsletter`, `/linkedin:carousel`, `/linkedin:react`, `/linkedin:report`
|
||||||
|
|
||||||
14 specialized agents · 24 commands · 6 skills · 9 hooks · 24 reference docs
|
15 specialized agents · 24 commands · 6 skills · 9 hooks · 24 reference docs
|
||||||
|
|
||||||
→ [Full documentation](plugins/linkedin-thought-leadership/README.md)
|
→ [Full documentation](plugins/linkedin-thought-leadership/README.md)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "linkedin-thought-leadership",
|
"name": "linkedin-thought-leadership",
|
||||||
"version": "2.2.0",
|
"version": "2.3.0",
|
||||||
"description": "Full-spectrum LinkedIn content engine — feed posts, carousels, video scripts, and long-form newsletter editions — with the January 2026 360Brew algorithm baked in. v2.2 hardens the longform gates: blocking persona hard-fails, post-cutoff fact-check mandate, a Norwegian-chronicle de-AI voice-scrubber, render+annotate operator gates, and STATE.md-reconciled edition state.",
|
"description": "Full-spectrum LinkedIn content engine — feed posts, carousels, video scripts, and long-form newsletter editions — with the January 2026 360Brew algorithm baked in. v2.3 adds Step 7.5 (visual assets) to /linkedin:newsletter: cover + inline figures or carousel deck, generated and operator-gated BEFORE lock (pipeline 13→14 phases). v2.2 hardened the longform gates: blocking persona hard-fails, post-cutoff fact-check mandate, a Norwegian-chronicle de-AI voice-scrubber, render+annotate operator gates, and STATE.md-reconciled edition state.",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "Kjell Tore Guttormsen"
|
"name": "Kjell Tore Guttormsen"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,28 @@ 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/),
|
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).
|
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 → 1–2 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 0–7, 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
|
## [2.2.0] - 2026-05-28
|
||||||
|
|
||||||
### Summary
|
### Summary
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
# LinkedIn Thought Leadership Plugin (v2.2.0)
|
# LinkedIn Thought Leadership Plugin (v2.3.0)
|
||||||
|
|
||||||
Full-spectrum LinkedIn content engine — short-form feed posts, carousels, video scripts, and long-form newsletter editions — with the January 2026 360Brew algorithm baked in. v2.0.0 consolidated the surface (27 commands → 24, 16 agents → 14) while adding the long-form `/linkedin:newsletter` orchestrator + two longform-quality gate agents (`fact-checker`, `persona-reviewer`). v2.1.0 added two gates BEFORE prose (Step 2.5 skeleton + Step 3a spine prose) + a third `persona-reviewer` mode (`skjelett`). **v2.2.0** hardens the longform gates with the lessons from the second production run (Seres-serien): the persona gate is now **blocking with an explicit hard-fail list** (primær «mistet meg» / doesn't own the action / sjargong-mur / modell-/navne-katalog → BLOCK; «JA med store forbehold» = NEI); fact-check is declared **orthogonal to narrative strength** with a **post-cutoff web-search mandate** + high-frequency-error checklist; a new **`voice-scrubber`** agent (Opus) does de-AI scrub + Norwegian-chronicle voice-drift correction (gold standard = approved Norwegian editions, NOT the English post corpus) wired into Step 4; operator gates become **render+annotate** rounds (Steps 2.5/3a); and per-edition production state is **reconciled with the global STATE.md** continuity system — `edition-HANDOVER.md` is gone. Agents 14 → 15; commands unchanged (24).
|
Full-spectrum LinkedIn content engine — short-form feed posts, carousels, video scripts, and long-form newsletter editions — with the January 2026 360Brew algorithm baked in. v2.0.0 consolidated the surface (27 commands → 24, 16 agents → 14) while adding the long-form `/linkedin:newsletter` orchestrator + two longform-quality gate agents (`fact-checker`, `persona-reviewer`). v2.1.0 added two gates BEFORE prose (Step 2.5 skeleton + Step 3a spine prose) + a third `persona-reviewer` mode (`skjelett`). v2.2.0 hardened the longform gates with the lessons from the second production run (Seres-serien): blocking persona hard-fails, a post-cutoff fact-check mandate, a `voice-scrubber` agent, render+annotate operator gates, and STATE.md-reconciled edition state. **v2.3.0** makes **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 a carousel deck, generated (default `mcp-image`, external `cover-raw.png` accepted) and **operator-gated BEFORE lock** so `render/build-linkedin.mjs` picks up `cover.png` at lock without a post-lock re-render. Pipeline 13 → 14 phases; new `config/image-credit-caption.template.md`; additive `visualAssets` state. Doc/orchestration-only — no new code. Agents unchanged (15); commands unchanged (24).
|
||||||
|
|
||||||
## Architecture
|
## Architecture
|
||||||
|
|
||||||
|
|
@ -46,7 +46,7 @@ All content commands (post, quick, react, pipeline, first-post, video, multiplat
|
||||||
| `/linkedin:post` | Full post creation (10-15 min) |
|
| `/linkedin:post` | Full post creation (10-15 min) |
|
||||||
| `/linkedin:quick` | 5-minute quick post (3-line formula) + 8 post-type templates |
|
| `/linkedin:quick` | 5-minute quick post (3-line formula) + 8 post-type templates |
|
||||||
| `/linkedin:pipeline` | Full end-to-end content pipeline |
|
| `/linkedin:pipeline` | Full end-to-end content pipeline |
|
||||||
| `/linkedin:newsletter` | Long-form orchestrator: newsletter edition / essay / series article — multi-session 13-phase pipeline with **skeleton + spine-prose gates BEFORE prose (v2.1)** and fact-check + persona-sweep BEFORE lock |
|
| `/linkedin:newsletter` | Long-form orchestrator: newsletter edition / essay / series article — multi-session 14-phase pipeline with **skeleton + spine-prose gates BEFORE prose (v2.1)**, fact-check + persona-sweep BEFORE lock, and **visual-assets gate BEFORE lock (Step 7.5, v2.3)** |
|
||||||
| `/linkedin:batch` | Create a full week of content |
|
| `/linkedin:batch` | Create a full week of content |
|
||||||
| `/linkedin:calendar` | View/manage post scheduling queue + publish action (mark scheduled posts as published) |
|
| `/linkedin:calendar` | View/manage post scheduling queue + publish action (mark scheduled posts as published) |
|
||||||
| `/linkedin:carousel` | Structured multi-slide carousel generator |
|
| `/linkedin:carousel` | Structured multi-slide carousel generator |
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
*AI-generated: all code produced by Claude Code through dialog-driven development. [Full disclosure →](../../README.md#ai-generated-code-disclosure)*
|
*AI-generated: all code produced by Claude Code through dialog-driven development. [Full disclosure →](../../README.md#ai-generated-code-disclosure)*
|
||||||
|
|
||||||

|

|
||||||

|

|
||||||

|

|
||||||

|

|
||||||
|
|
@ -14,12 +14,13 @@
|
||||||

|

|
||||||

|

|
||||||
|
|
||||||
A comprehensive Claude Code plugin that turns LinkedIn from a chore into a full-spectrum content engine — short-form feed posts, carousels, video scripts, and long-form newsletter editions. v2.0.0 consolidated the surface (27 → 24 commands, 16 → 14 agents) and added `/linkedin:newsletter` as a multi-session long-form orchestrator with fact-check + persona-sweep gates BEFORE lock. **v2.1.0** adds two more gates BEFORE prose to `/linkedin:newsletter` — a skeleton gate (Step 2.5) and a spine-prose gate (Step 3a) — encoding the Maskinrommet writing-contract §A discipline (premiss / problem / anbefaling / gevinst / vei videre) into the pipeline itself, so spine errors get caught in minutes at the skeleton stage instead of hours at the resonance stage or a full day post-lock. **v2.2.0** hardens the longform gates with the lessons from the next production run: blocking persona hard-fails (primær «mistet meg» / doesn't own the action / jargon wall / model-name catalog → BLOCK), a post-cutoff fact-check mandate, a new Norwegian-chronicle de-AI voice-scrubber agent, render+annotate operator gates, and edition state reconciled with the global STATE.md continuity system. 24 slash commands, 15 specialized agents, 9 automated hooks, and a 24-document knowledge base grounded in LinkedIn's actual algorithm signals. Updated for the January 2026 **360Brew** algorithm change, where LinkedIn now validates your profile before distributing content.
|
A comprehensive Claude Code plugin that turns LinkedIn from a chore into a full-spectrum content engine — short-form feed posts, carousels, video scripts, and long-form newsletter editions. v2.0.0 consolidated the surface (27 → 24 commands, 16 → 14 agents) and added `/linkedin:newsletter` as a multi-session long-form orchestrator with fact-check + persona-sweep gates BEFORE lock. **v2.1.0** adds two more gates BEFORE prose to `/linkedin:newsletter` — a skeleton gate (Step 2.5) and a spine-prose gate (Step 3a) — encoding the Maskinrommet writing-contract §A discipline (premiss / problem / anbefaling / gevinst / vei videre) into the pipeline itself, so spine errors get caught in minutes at the skeleton stage instead of hours at the resonance stage or a full day post-lock. v2.2.0 hardened the longform gates with the lessons from the next production run: blocking persona hard-fails (primær «mistet meg» / doesn't own the action / jargon wall / model-name catalog → BLOCK), a post-cutoff fact-check mandate, a new Norwegian-chronicle de-AI voice-scrubber agent, render+annotate operator gates, and edition state reconciled with the global STATE.md continuity system. **v2.3.0** makes visual assets an explicit pipeline phase — new **Step 7.5 (visual-assets)** between annotation and lock: the cover (+ optional inline figures) or a carousel deck is generated and operator-gated *before* lock, so the renderer picks up `cover.png` without a post-lock re-render (pipeline 13 → 14 phases). 24 slash commands, 15 specialized agents, 9 automated hooks, and a 24-document knowledge base grounded in LinkedIn's actual algorithm signals. Updated for the January 2026 **360Brew** algorithm change, where LinkedIn now validates your profile before distributing content.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Table of Contents
|
## Table of Contents
|
||||||
|
|
||||||
|
- [What's New in v2.3.0](#whats-new-in-v230)
|
||||||
- [What's New in v2.2.0](#whats-new-in-v220)
|
- [What's New in v2.2.0](#whats-new-in-v220)
|
||||||
- [What's New in v2.1.0](#whats-new-in-v210)
|
- [What's New in v2.1.0](#whats-new-in-v210)
|
||||||
- [What's New in v2.0.0](#whats-new-in-v200)
|
- [What's New in v2.0.0](#whats-new-in-v200)
|
||||||
|
|
@ -41,6 +42,21 @@ A comprehensive Claude Code plugin that turns LinkedIn from a chore into a full-
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## What's New in v2.3.0
|
||||||
|
|
||||||
|
**Visual assets become an explicit pipeline phase.** Until now, images (cover + inline figures) were produced ad-hoc *outside* the `/linkedin:newsletter` pipeline and referenced by hand — even though a cover is mandatory and has to coordinate with the text. v2.3.0 adds **Step 7.5 — Visual assets** between annotation (Step 7) and lock (Step 8), so visuals are resolved *before* the edition locks.
|
||||||
|
|
||||||
|
- **New Step 7.5 — Visual assets** (`commands/newsletter.md`). Decides image needs from the article type (method-heavy → 1–2 inline figures; diagnosis-heavy → cover only), writes a per-image brief, generates, runs an operator-gate, and records credit + caption. It runs **before lock on purpose**: `render/build-linkedin.mjs` picks up `linkedin/NN/cover.png` + the edition-config credit/caption at lock, so generating images after lock would force a re-render and break the lock.
|
||||||
|
- **Two generation routes, no lock-in** — default `mcp__mcp-image__generate_image` (Nano Banana Pro) writing `cover-v<N>-kandidat.png`, or an external route (DALL·E / Midjourney / photographer) via a `cover-raw.png` the operator drops in. The interface is pluggable (path-in / path-out); mcp-image is the default, not a hard dependency.
|
||||||
|
- **Operator-gate = the Step 2.5/3a pattern, for images** — every candidate is surfaced via `SendUserFile` for side-by-side comparison; on approval the chosen candidate is copied to the fixed `cover.png` name that the renderer reads.
|
||||||
|
- **Explicit carousel branch** — `format: "carousel"` editions render a typografisk slide-deck via the existing `render/build-carousel.mjs` instead of cover+inline.
|
||||||
|
- **New `config/image-credit-caption.template.md`** — cover motif + credit + caption table (honest-about-AI credit per the verification duty), modelled on the established series convention. Documents the `cover.png` / `cover-v<N>-kandidat.png` / `cover-raw.png` / `fig<N>.png` naming.
|
||||||
|
- **`visual-assets` phase + additive `visualAssets` state** in `config/edition-state.template.json`. Pipeline grows 13 → 14 phases; resumption stays deterministic (`annotation` now resumes at Step 7.5).
|
||||||
|
|
||||||
|
Doc/orchestration-only — **no new code**. Commands (24) and agents (15) unchanged. Backward-compatible: the only state-shape change is additive.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## What's New in v2.2.0
|
## What's New in v2.2.0
|
||||||
|
|
||||||
**Longform gates hardened** — the second production run (Seres-serien) surfaced six concrete weaknesses; v2.2.0 closes all of them. A chronicle built as a model/name catalog passed review (flags were read as notes, not stop-signs) and nearly shipped; the rewrite was better but introduced fresh factual errors. The gates now make both failure modes blocking.
|
**Longform gates hardened** — the second production run (Seres-serien) surfaced six concrete weaknesses; v2.2.0 closes all of them. A chronicle built as a model/name catalog passed review (flags were read as notes, not stop-signs) and nearly shipped; the rewrite was better but introduced fresh factual errors. The gates now make both failure modes blocking.
|
||||||
|
|
@ -562,6 +578,7 @@ Scheduled posts are tracked in `assets/drafts/queue.json`:
|
||||||
|
|
||||||
| Version | Date | Highlights |
|
| Version | Date | Highlights |
|
||||||
|---------|------|-----------|
|
|---------|------|-----------|
|
||||||
|
| **2.3.0** | 2026-05-28 | Visual assets as an explicit pipeline phase. New **Step 7.5 — Visual assets** in `/linkedin:newsletter` (between annotation and lock): cover (+ optional inline figures) or carousel deck, generated (default `mcp-image`; external `cover-raw.png` accepted) and operator-gated via `SendUserFile` BEFORE lock so `build-linkedin.mjs` picks up `cover.png` without a post-lock re-render. Explicit `format: "carousel"` branch reusing `build-carousel.mjs`. New `config/image-credit-caption.template.md`; additive `visualAssets` state + naming convention (`cover.png` / `cover-v<N>-kandidat.png` / `cover-raw.png` / `fig<N>.png`). Pipeline 13 → 14 phases. Doc/orchestration-only (no new code); commands (24) + agents (15) unchanged. |
|
||||||
| **2.2.0** | 2026-05-28 | Longform gates hardened (2nd production run). Persona gate blocking with explicit hard-fail list (primær mistet meg / doesn't own action / sjargong-mur / modell-navne-katalog → BLOCK; «JA med forbehold» = NEI). Fact-check post-cutoff web-search mandate + high-frequency-error checklist. New `voice-scrubber` agent (Opus): de-AI scrub + Norwegian-chronicle voice-drift, gold standard = approved Norwegian editions (NOT the English post corpus). Render+annotate operator gates (Steps 2.5/3a). Edition state reconciled with STATE.md (ONE-system); `edition-HANDOVER.template.md` deleted. 14 → 15 agents; commands unchanged (24). |
|
| **2.2.0** | 2026-05-28 | Longform gates hardened (2nd production run). Persona gate blocking with explicit hard-fail list (primær mistet meg / doesn't own action / sjargong-mur / modell-navne-katalog → BLOCK; «JA med forbehold» = NEI). Fact-check post-cutoff web-search mandate + high-frequency-error checklist. New `voice-scrubber` agent (Opus): de-AI scrub + Norwegian-chronicle voice-drift, gold standard = approved Norwegian editions (NOT the English post corpus). Render+annotate operator gates (Steps 2.5/3a). Edition state reconciled with STATE.md (ONE-system); `edition-HANDOVER.template.md` deleted. 14 → 15 agents; commands unchanged (24). |
|
||||||
| **2.1.0** | 2026-05-28 | Skeleton gate BEFORE prose in `/linkedin:newsletter`. New Step 2.5 (skeleton + section pitch, operator-gate + persona-skjelett-sweep) and Step 3a (spine prose, operator-gate) split the old Step 3 into pre-prose stages with their own gates. New `persona-reviewer` mode (`skjelett`). Pipeline grows 11 → 13 phases; commands and agents unchanged in count (24, 14). Encodes the Maskinrommet writing-contract §A discipline (premiss / problem / anbefaling / gevinst / vei videre) into the pipeline. Empirically motivated by the Seres-serien Del 3 + Del 4 spine-rework cost. |
|
| **2.1.0** | 2026-05-28 | Skeleton gate BEFORE prose in `/linkedin:newsletter`. New Step 2.5 (skeleton + section pitch, operator-gate + persona-skjelett-sweep) and Step 3a (spine prose, operator-gate) split the old Step 3 into pre-prose stages with their own gates. New `persona-reviewer` mode (`skjelett`). Pipeline grows 11 → 13 phases; commands and agents unchanged in count (24, 14). Encodes the Maskinrommet writing-contract §A discipline (premiss / problem / anbefaling / gevinst / vei videre) into the pipeline. Empirically motivated by the Seres-serien Del 3 + Del 4 spine-rework cost. |
|
||||||
| **2.0.0** | 2026-05-28 | Full-spectrum content engine. `/linkedin:newsletter` long-form orchestrator with multi-session edition-state, fact-check + persona-sweep gates BEFORE lock. New agents: `fact-checker`, `persona-reviewer` (both Opus). Render pipeline migrated in-plugin with self-hosted OFL-1.1 fonts. Net-fewer surface: 27 → 24 commands (5 removed, 2 added), 16 → 14 agents (4 merged, 2 added). Router gating on monetize/outreach (unlocks at ~1K). `/linkedin:import` delegates analysis to `/linkedin:report`. |
|
| **2.0.0** | 2026-05-28 | Full-spectrum content engine. `/linkedin:newsletter` long-form orchestrator with multi-session edition-state, fact-check + persona-sweep gates BEFORE lock. New agents: `fact-checker`, `persona-reviewer` (both Opus). Render pipeline migrated in-plugin with self-hosted OFL-1.1 fonts. Net-fewer surface: 27 → 24 commands (5 removed, 2 added), 16 → 14 agents (4 merged, 2 added). Router gating on monetize/outreach (unlocks at ~1K). `/linkedin:import` delegates analysis to `/linkedin:report`. |
|
||||||
|
|
|
||||||
|
|
@ -71,7 +71,7 @@ delegate the fan-out to a nested background agent.
|
||||||
> only layer that can reliably spawn parallel sub-agents. So this command issues
|
> only layer that can reliably spawn parallel sub-agents. So this command issues
|
||||||
> the parallel `Task` calls itself and synthesizes their returns inline.
|
> the parallel `Task` calls itself and synthesizes their returns inline.
|
||||||
|
|
||||||
## Pipeline overview (13 phases)
|
## Pipeline overview (14 phases)
|
||||||
|
|
||||||
The phase order is fixed. Two gates run **BEFORE prose** (skeleton + spine
|
The phase order is fixed. Two gates run **BEFORE prose** (skeleton + spine
|
||||||
prose), and the persona resonance sweep runs **BEFORE lock** — these are the
|
prose), and the persona resonance sweep runs **BEFORE lock** — these are the
|
||||||
|
|
@ -90,17 +90,19 @@ single most important corrections from the Seres process (plan §0.4, principle
|
||||||
| 5 | **Fact-check sweep** | risk-sorted (🔴/🟡/🟢), guilty-until-disproven, verification log | **`fact-checker` (parallel)** |
|
| 5 | **Fact-check sweep** | risk-sorted (🔴/🟡/🟢), guilty-until-disproven, verification log | **`fact-checker` (parallel)** |
|
||||||
| 6 | **Persona sweep — BEFORE lock** | reader jury, primær wins, convergence to clean YES | **`persona-reviewer`** (resonance mode) |
|
| 6 | **Persona sweep — BEFORE lock** | reader jury, primær wins, convergence to clean YES | **`persona-reviewer`** (resonance mode) |
|
||||||
| 7 | **Annotation (optional)** | render annotatable review HTML for a manual pass | `render/build-html.mjs` |
|
| 7 | **Annotation (optional)** | render annotatable review HTML for a manual pass | `render/build-html.mjs` |
|
||||||
|
| 7.5 | **Visual assets — BEFORE lock** | cover (+ optional inline figures) or carousel deck: behov → per-image brief → generate (mcp-image default / external `cover-raw.png`) → operator-gate (`SendUserFile`) → approve to `cover.png` → credit/caption. Runs before lock so the renderer picks the cover up. | `mcp__mcp-image__generate_image` + `SendUserFile` + (carousel) `render/build-carousel.mjs` |
|
||||||
| 8 | **LOCK → delivery** | POST.html "all in one place" | `render/build-linkedin.mjs` |
|
| 8 | **LOCK → delivery** | POST.html "all in one place" | `render/build-linkedin.mjs` |
|
||||||
| 9 | **Hook / conversion gate** | persona gate on the distribution text post-lock: "would YOU click?" | **`persona-reviewer`** (conversion mode) |
|
| 9 | **Hook / conversion gate** | persona gate on the distribution text post-lock: "would YOU click?" | **`persona-reviewer`** (conversion mode) |
|
||||||
| 10 | **Scheduling** | register the edition in the plugin queue/state for native scheduling | `hooks/scripts/queue-manager.mjs` |
|
| 10 | **Scheduling** | register the edition in the plugin queue/state for native scheduling | `hooks/scripts/queue-manager.mjs` |
|
||||||
|
|
||||||
> **Build status:** all 13 phases (Steps 0–2.5, 3a, 3b, 4–10) are implemented
|
> **Build status:** all 14 phases (Steps 0–2.5, 3a, 3b, 4–7, 7.5, 8–10) are
|
||||||
> below. This command takes an edition end-to-end: load → calibration →
|
> implemented below. This command takes an edition end-to-end: load →
|
||||||
> verified research → **skeleton + section pitch (operator + persona gate
|
> calibration → verified research → **skeleton + section pitch (operator +
|
||||||
> BEFORE prose)** → **spine prose (operator gate BEFORE full expansion)** →
|
> persona gate BEFORE prose)** → **spine prose (operator gate BEFORE full
|
||||||
> full prose draft → consistency/quality → fact-check sweep → pre-lock persona
|
> expansion)** → full prose draft → consistency/quality → fact-check sweep →
|
||||||
> sweep → optional annotation → LOCK/delivery → post-lock hook gate →
|
> pre-lock persona sweep → optional annotation → **visual assets (cover/figures
|
||||||
> scheduling, persisting each phase to `edition-state.json` (machine) and
|
> or carousel, operator-gated BEFORE lock)** → LOCK/delivery → post-lock hook
|
||||||
|
> gate → scheduling, persisting each phase to `edition-state.json` (machine) and
|
||||||
> `<serie>/STATE.md` (narrative) and stopping cleanly between sessions.
|
> `<serie>/STATE.md` (narrative) and stopping cleanly between sessions.
|
||||||
|
|
||||||
> **Why two gates BEFORE prose (v2.1).** Spine errors are the dearest failure
|
> **Why two gates BEFORE prose (v2.1).** Spine errors are the dearest failure
|
||||||
|
|
@ -181,8 +183,9 @@ Look up `edition-state.json` → `articles.<currentArticle>` (and the top-level
|
||||||
| `draft` | Step 4 — Consistency + quality *(see draft-cursor note)* |
|
| `draft` | Step 4 — Consistency + quality *(see draft-cursor note)* |
|
||||||
| `consistency-quality` | Step 5 — Fact-check sweep |
|
| `consistency-quality` | Step 5 — Fact-check sweep |
|
||||||
| `factcheck-sweep` | Step 6 — Persona sweep (pre-lock) |
|
| `factcheck-sweep` | Step 6 — Persona sweep (pre-lock) |
|
||||||
| `persona-sweep-prelock` | Step 7 — Annotation (optional) → Step 8 |
|
| `persona-sweep-prelock` | Step 7 — Annotation (optional) → Step 7.5 |
|
||||||
| `annotation` | Step 8 — LOCK → delivery |
|
| `annotation` | Step 7.5 — Visual assets *(cover/figures or carousel deck, BEFORE lock)* |
|
||||||
|
| `visual-assets` | Step 8 — LOCK → delivery |
|
||||||
| `lock-delivery` | Step 9 — Hook / conversion gate |
|
| `lock-delivery` | Step 9 — Hook / conversion gate |
|
||||||
| `hook-conversion-gate` | Step 10 — Scheduling |
|
| `hook-conversion-gate` | Step 10 — Scheduling |
|
||||||
| `scheduling` | **Edition complete** — nothing to resume (start the next article or edition) |
|
| `scheduling` | **Edition complete** — nothing to resume (start the next article or edition) |
|
||||||
|
|
@ -883,13 +886,164 @@ editor is satisfied with the in-session draft. It does not gate lock.
|
||||||
substantive, re-run the affected sweep (Step 5 or 6) before proceeding.
|
substantive, re-run the affected sweep (Step 5 or 6) before proceeding.
|
||||||
|
|
||||||
4. **Persist.** Set `currentPhase: "annotation"` in `edition-state.json` and
|
4. **Persist.** Set `currentPhase: "annotation"` in `edition-state.json` and
|
||||||
write an "annotation rendered (optional) → next: lock/delivery" line to
|
write an "annotation rendered (optional) → next: visual assets" line to
|
||||||
`<serie>/STATE.md` (overwrite). If skipped, note "annotation skipped" and move on.
|
`<serie>/STATE.md` (overwrite). If skipped, note "annotation skipped" and move on.
|
||||||
|
|
||||||
```
|
```
|
||||||
Annotation (optional).
|
Annotation (optional).
|
||||||
- Rendered: <serie-mappe>/review/NN-utkast.html (or: skipped)
|
- Rendered: <serie-mappe>/review/NN-utkast.html (or: skipped)
|
||||||
- build-html exit: 0 (else: non-zero — review HTML NOT produced, see stderr)
|
- build-html exit: 0 (else: non-zero — review HTML NOT produced, see stderr)
|
||||||
|
Next: Step 7.5 — Visual assets (cover/figures or carousel, BEFORE lock).
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Step 7.5: Visual assets — cover (+ inline figures) or carousel deck, BEFORE lock
|
||||||
|
|
||||||
|
The edition needs at least a **cover** (mandatory per the KTG cover-directive:
|
||||||
|
TLDR on top + at least one figure per article) and, for method-heavy editions,
|
||||||
|
one or two inline figures. This is a real pipeline phase — not an ad-hoc step
|
||||||
|
outside the pipeline — because the cover and its credit/caption coordinate with
|
||||||
|
the text, and because Step 8's renderer **picks them up**.
|
||||||
|
|
||||||
|
> **Why BEFORE lock (enforced).** `render/build-linkedin.mjs` (Step 8) reads
|
||||||
|
> `linkedin/NN/cover.png` (fixed filename) and the edition-config
|
||||||
|
> `coverCredit` + `captions[NN]` when it builds `POST.html`. If images were
|
||||||
|
> generated *after* lock, `POST.html` would have to be re-rendered — so the
|
||||||
|
> edition would no longer be locked in practice. Visual assets are therefore
|
||||||
|
> resolved here, between the pre-lock persona sweep (Step 6) / optional
|
||||||
|
> annotation (Step 7) and the lock (Step 8). `[GATE]`
|
||||||
|
|
||||||
|
> **What the renderer does and does NOT do.** `build-linkedin.mjs` embeds the
|
||||||
|
> **cover** by *filename reference* in the POST.html cover field (`linkedin/NN/cover.png`
|
||||||
|
> + credit + caption) — it does not inline the image bytes, and it does **not**
|
||||||
|
> embed `fig<N>.png` at all. Inline figures are referenced in the draft markdown
|
||||||
|
> (``) for the author's reference and **uploaded
|
||||||
|
> manually** in the LinkedIn editor. So "visual assets" here means: produce and
|
||||||
|
> approve the image *files* + record their credit/caption, not auto-embed them.
|
||||||
|
|
||||||
|
**Format branch (decide first).** An edition is either `standard` (cover +
|
||||||
|
optional inline figures) or `carousel` (a typografisk slide-deck instead of
|
||||||
|
cover+inline). It is `carousel` when its `NN` is in `edition-config.json`
|
||||||
|
→ `carousel` (the list of editions that ship a document post), or when the
|
||||||
|
operator declares carousel format for it. Branch accordingly:
|
||||||
|
|
||||||
|
- **`standard`** → run steps 1–5 below (cover, optional figures, credit/caption).
|
||||||
|
- **`carousel`** → skip cover/figures; jump to **step 6 (carousel branch)** below.
|
||||||
|
|
||||||
|
**Procedure (`standard` format):**
|
||||||
|
|
||||||
|
1. **Decide image needs from the article type.** Use a light heuristic on the
|
||||||
|
article's skeleton (Step 2.5) + writing contract, or just ask the operator:
|
||||||
|
- **Cover** — always mandatory: one hero illustration for the edition.
|
||||||
|
- **Inline figures** — article-dependent. *Method-heavy* articles (a model
|
||||||
|
diagram, a relationship map, before/after) usually want 1–2 figures;
|
||||||
|
*diagnosis-heavy* articles often need only the cover. Propose a count and
|
||||||
|
let the operator confirm or override.
|
||||||
|
|
||||||
|
2. **Write a brief per image.** For each image (cover + any figures): a short
|
||||||
|
text brief — motif, mood, format, aspect ratio (cover target is **1920×1080**,
|
||||||
|
per the renderer's cover block). Generate a first proposal from the skeleton +
|
||||||
|
writing contract; the operator overrides freely. Record each per-image brief
|
||||||
|
in `edition-state.json` → `articles.NN.visualAssets.cover.brief` and
|
||||||
|
`…figures[].brief`.
|
||||||
|
|
||||||
|
3. **Generate — two routes, no lock-in.** The interface is pluggable (path-in /
|
||||||
|
path-out); `mcp-image` is the default, not a hard dependency:
|
||||||
|
- **Default route — `mcp__mcp-image__generate_image`** (Nano Banana Pro /
|
||||||
|
Gemini 3 Pro Image). Write candidates to
|
||||||
|
`linkedin/NN/cover-v<N>-kandidat.png` (and `fig<N>-kandidat.png` for
|
||||||
|
figures). Candidate naming lets several attempts sit side by side without
|
||||||
|
overwriting an approved file. Record route `"mcp-image"`.
|
||||||
|
- **External route** — DALL·E, Midjourney, a photographer, a hand-built SVG.
|
||||||
|
The plugin accepts a `linkedin/NN/cover-raw.png` the operator drops in; no
|
||||||
|
tool is mandated. Record route `"external"`. (The raw file may then be
|
||||||
|
cropped/retouched into a candidate, or approved directly.)
|
||||||
|
|
||||||
|
4. **Operator-gate (render + approve — the Step 2.5/3a pattern, for images).**
|
||||||
|
Surface **every candidate** to the operator with `SendUserFile` (the image
|
||||||
|
equivalent of the render+annotate gate the write deliverables use):
|
||||||
|
1. Collect the candidate paths (`cover-v<N>-kandidat.png`, any external
|
||||||
|
`cover-raw.png`).
|
||||||
|
2. `SendUserFile` them with a one-line caption tying each to its brief, so
|
||||||
|
the operator can compare side by side.
|
||||||
|
3. The operator either **approves one** or **asks for more attempts** (loop
|
||||||
|
back to step 3 — generate the next `cover-v<N+1>-kandidat.png`).
|
||||||
|
4. On approval, **copy the approved candidate to the fixed name** that
|
||||||
|
`build-linkedin.mjs` reads — `cover.png`:
|
||||||
|
```bash
|
||||||
|
cd <serie-mappe> && cp "linkedin/NN/cover-v<N>-kandidat.png" "linkedin/NN/cover.png"
|
||||||
|
```
|
||||||
|
(Same pattern for figures: approved → `linkedin/NN/fig<N>.png`.) Confirm
|
||||||
|
`linkedin/NN/cover.png` exists before advancing.
|
||||||
|
|
||||||
|
Do not advance to Step 8 without an approved `cover.png`. `[OPERATØR]`
|
||||||
|
|
||||||
|
5. **Credit + caption (prep for Step 8).** Read
|
||||||
|
`<serie>/linkedin/image-credit-caption.md` (template:
|
||||||
|
`${CLAUDE_PLUGIN_ROOT}/config/image-credit-caption.template.md` — copy it in
|
||||||
|
for a new series) and add/update the row for this edition: **motif** (one
|
||||||
|
line) + **caption** (one line, encoding the article's signal). The credit
|
||||||
|
must be **honest about AI generation** when the image is AI-made
|
||||||
|
(verification duty). Then fold these into `<serie>/linkedin/edition-config.json`:
|
||||||
|
- `coverCredit` — the global cover credit line (one value for the edition/series).
|
||||||
|
- `captions[NN]` — this article's cover caption / alt text.
|
||||||
|
These are exactly the fields `build-linkedin.mjs` already reads in Step 8.
|
||||||
|
|
||||||
|
**Procedure (`carousel` format — branch):**
|
||||||
|
|
||||||
|
6. **Render the carousel deck instead of cover+inline.** A carousel edition's
|
||||||
|
visual asset is the slide-deck, not a hero cover. Author the slides in
|
||||||
|
`<serie>/linkedin/NN/carousel.md` (the `## SLIDE N — …` grammar
|
||||||
|
`build-carousel.mjs` parses), then render with the plugin-owned renderer
|
||||||
|
(cwd = series folder):
|
||||||
|
```bash
|
||||||
|
cd <serie-mappe> && node "${CLAUDE_PLUGIN_ROOT}/render/build-carousel.mjs" linkedin/NN/carousel.md
|
||||||
|
```
|
||||||
|
`build-carousel.mjs` writes `linkedin/NN/carousel.html` always and
|
||||||
|
`linkedin/NN/carousel.pdf` when `weasyprint` is on PATH (it degrades with an
|
||||||
|
install hint instead of throwing — check the output and surface the hint if
|
||||||
|
the PDF was skipped). Surface the rendered deck (`carousel.pdf`, else
|
||||||
|
`carousel.html`) to the operator via `SendUserFile` for the same approve /
|
||||||
|
regenerate gate as step 4. Record this under
|
||||||
|
`articles.NN.visualAssets.carousel = { source, pdf, status }` and set
|
||||||
|
`format: "carousel"`. No `cover.png` is required for a pure carousel edition
|
||||||
|
(its `POST.html` carousel block references `linkedin/NN/carousel.pdf`); a
|
||||||
|
carousel edition that *also* posts a feed cover runs both branches. `[OPERATØR]`
|
||||||
|
|
||||||
|
**Naming convention (documented — consistent with existing series use):**
|
||||||
|
|
||||||
|
| File | Meaning |
|
||||||
|
|------|---------|
|
||||||
|
| `cover.png` | **Approved, fixed name** — the only cover filename `build-linkedin.mjs` reads. |
|
||||||
|
| `cover-v<N>-kandidat.png` | Generation attempts (mcp-image or post-processed). Several may coexist. |
|
||||||
|
| `cover-raw.png` | Optional external pre-edit source (DALL·E / Midjourney / photographer). |
|
||||||
|
| `fig<N>.png` | Inline figure (`fig1.png`, `fig2.png`, …), referenced from the draft markdown, uploaded manually. |
|
||||||
|
| `carousel.md` / `carousel.pdf` | Carousel deck source + rendered PDF (carousel-format editions). |
|
||||||
|
|
||||||
|
Descriptive variant suffixes (e.g. `cover-foto-kandidat-v2.png`) are fine for
|
||||||
|
parallel exploration as long as the **approved** image always lands at the fixed
|
||||||
|
`cover.png` name.
|
||||||
|
|
||||||
|
**Persist + checkpoint state.** Once the cover is approved (or, for carousel,
|
||||||
|
the deck is approved) and credit/caption are recorded:
|
||||||
|
|
||||||
|
- Set `articles.NN.visualAssets` (format, cover.status `approved`,
|
||||||
|
cover.approved `"cover.png"`, candidates list, figures, carousel) in
|
||||||
|
`edition-state.json`.
|
||||||
|
- Set `currentPhase: "visual-assets"` in `edition-state.json` (the marker that
|
||||||
|
Step 7.5 is complete and the gate has passed).
|
||||||
|
- Write a "visual assets approved (cover/figures or carousel) → next:
|
||||||
|
lock/delivery" line to `<serie>/STATE.md` (overwrite).
|
||||||
|
|
||||||
|
```
|
||||||
|
Visual assets (BEFORE lock).
|
||||||
|
- Format: standard (cover + <N> figures) (or: carousel deck)
|
||||||
|
- Cover: linkedin/NN/cover.png approved (after <N> candidates) (or: N/A — carousel)
|
||||||
|
- Figures: <N> approved → linkedin/NN/figN.png (or: none)
|
||||||
|
- Carousel deck: linkedin/NN/carousel.pdf rendered + approved (or: N/A — standard)
|
||||||
|
- Route: mcp-image | external Credit/caption: recorded in image-credit-caption.md + edition-config.json
|
||||||
|
- Operator gate: approved (candidates surfaced via SendUserFile) [OPERATØR]
|
||||||
Next: Step 8 — LOCK → delivery.
|
Next: Step 8 — LOCK → delivery.
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -912,9 +1066,11 @@ produces the editor's single delivery artifact — `POST.html`, the
|
||||||
**Procedure:**
|
**Procedure:**
|
||||||
|
|
||||||
1. **Confirm lock preconditions.** In `edition-state.json`: the article's
|
1. **Confirm lock preconditions.** In `edition-state.json`: the article's
|
||||||
`factcheckLog` has no open 🔴 and `personaSweep.resonance` recorded a primær
|
`factcheckLog` has no open 🔴, `personaSweep.resonance` recorded a primær JA,
|
||||||
JA. If either is missing, STOP — return to Step 5/6. Do not lock past an
|
and `visualAssets` is gated — for `standard` format the approved
|
||||||
open gate.
|
`linkedin/NN/cover.png` exists (Step 7.5); for `carousel` format the approved
|
||||||
|
`carousel.pdf`/`carousel.html` exists. If any is missing, STOP — return to the
|
||||||
|
relevant step (5/6/7.5). Do not lock past an open gate.
|
||||||
|
|
||||||
2. **Confirm the delivery inputs in the series folder.**
|
2. **Confirm the delivery inputs in the series folder.**
|
||||||
`render/build-linkedin.mjs` reads, relative to cwd (`<serie>/linkedin/`):
|
`render/build-linkedin.mjs` reads, relative to cwd (`<serie>/linkedin/`):
|
||||||
|
|
@ -1062,8 +1218,9 @@ Edition complete. Visible in /linkedin:calendar; mark live via /linkedin:calenda
|
||||||
|
|
||||||
## Reference Files
|
## Reference Files
|
||||||
|
|
||||||
- `${CLAUDE_PLUGIN_ROOT}/config/edition-state.template.json` — edition-state schema (13 phases including v2.1 skeleton + spine-prose gates)
|
- `${CLAUDE_PLUGIN_ROOT}/config/edition-state.template.json` — edition-state schema (14 phases including v2.1 skeleton + spine-prose gates and v2.3 visual-assets)
|
||||||
- `${CLAUDE_PLUGIN_ROOT}/config/edition-config.template.json` — static delivery metadata schema (calendar, freshness, credit, captions) — Step 8
|
- `${CLAUDE_PLUGIN_ROOT}/config/edition-config.template.json` — static delivery metadata schema (calendar, freshness, credit, captions) — Step 8
|
||||||
|
- `${CLAUDE_PLUGIN_ROOT}/config/image-credit-caption.template.md` — cover motif + credit + caption table (honest-about-AI credit) — Step 7.5
|
||||||
- `${CLAUDE_PLUGIN_ROOT}/config/edition-delingstekst.template.md` — distribution-copy grammar (`## Del N —` / `## Samle`) — Steps 8/9
|
- `${CLAUDE_PLUGIN_ROOT}/config/edition-delingstekst.template.md` — distribution-copy grammar (`## Del N —` / `## Samle`) — Steps 8/9
|
||||||
- `${CLAUDE_PLUGIN_ROOT}/config/personas.template.md` — reusable reader personas + "primær trumfer" rule
|
- `${CLAUDE_PLUGIN_ROOT}/config/personas.template.md` — reusable reader personas + "primær trumfer" rule
|
||||||
- `${CLAUDE_PLUGIN_ROOT}/agents/fact-checker.md` — Step 5 fact-check agent (risk-sorted, guilty-until-disproven)
|
- `${CLAUDE_PLUGIN_ROOT}/agents/fact-checker.md` — Step 5 fact-check agent (risk-sorted, guilty-until-disproven)
|
||||||
|
|
@ -1072,5 +1229,6 @@ Edition complete. Visible in /linkedin:calendar; mark live via /linkedin:calenda
|
||||||
- `${CLAUDE_PLUGIN_ROOT}/commands/react.md` — multi-source synthesis discipline (reused in Step 2)
|
- `${CLAUDE_PLUGIN_ROOT}/commands/react.md` — multi-source synthesis discipline (reused in Step 2)
|
||||||
- `${CLAUDE_PLUGIN_ROOT}/assets/voice-samples/authentic-voice-samples.md` — voice matching
|
- `${CLAUDE_PLUGIN_ROOT}/assets/voice-samples/authentic-voice-samples.md` — voice matching
|
||||||
- `${CLAUDE_PLUGIN_ROOT}/references/longform-quality-rules.md` — canonical long-form rules (Steps 2.5, 3a, 3b, 4–9 all reference)
|
- `${CLAUDE_PLUGIN_ROOT}/references/longform-quality-rules.md` — canonical long-form rules (Steps 2.5, 3a, 3b, 4–9 all reference)
|
||||||
- `${CLAUDE_PLUGIN_ROOT}/render/build-linkedin.mjs` — POST.html delivery (Step 8)
|
- `${CLAUDE_PLUGIN_ROOT}/render/build-linkedin.mjs` — POST.html delivery; reads `linkedin/NN/cover.png` + credit/caption (Step 8)
|
||||||
- `${CLAUDE_PLUGIN_ROOT}/render/build-html.mjs` — annotatable review renderer (Step 7)
|
- `${CLAUDE_PLUGIN_ROOT}/render/build-html.mjs` — annotatable review renderer (Step 7)
|
||||||
|
- `${CLAUDE_PLUGIN_ROOT}/render/build-carousel.mjs` — carousel deck renderer (`## SLIDE N —` → PDF via weasyprint) — Step 7.5 carousel branch
|
||||||
|
|
|
||||||
|
|
@ -15,11 +15,13 @@
|
||||||
"factcheck-sweep — risk-sorted, guilty-until-disproven, verification log (Step 5)",
|
"factcheck-sweep — risk-sorted, guilty-until-disproven, verification log (Step 5)",
|
||||||
"persona-sweep-prelock — reader jury, primary wins, convergence to clean YES (Step 6)",
|
"persona-sweep-prelock — reader jury, primary wins, convergence to clean YES (Step 6)",
|
||||||
"annotation — optional annotatable review HTML for a manual pass (Step 7)",
|
"annotation — optional annotatable review HTML for a manual pass (Step 7)",
|
||||||
|
"visual-assets — cover (+ optional inline figures) or carousel deck: brief → generate → operator-gate → approve, BEFORE lock so build-linkedin.mjs picks them up (Step 7.5)",
|
||||||
"lock-delivery — LOCK → POST.html all-in-one-place deliverable (Step 8)",
|
"lock-delivery — LOCK → POST.html all-in-one-place deliverable (Step 8)",
|
||||||
"hook-conversion-gate — persona gate on distribution text post-lock: would YOU click? (Step 9)",
|
"hook-conversion-gate — persona gate on distribution text post-lock: would YOU click? (Step 9)",
|
||||||
"scheduling — register edition in plugin queue/state for native LinkedIn scheduling (Step 10)"
|
"scheduling — register edition in plugin queue/state for native LinkedIn scheduling (Step 10)"
|
||||||
],
|
],
|
||||||
"articleStatusValues": ["pending", "in-progress", "locked", "scheduled"]
|
"articleStatusValues": ["pending", "in-progress", "locked", "scheduled"],
|
||||||
|
"visualAssets": "Per-article visual-asset record written by Step 7.5 (visual-assets phase). Runs BEFORE lock because render/build-linkedin.mjs picks up linkedin/NN/cover.png + the edition-config credit/caption when it builds POST.html — generating images after lock would force a re-render. Shape: { format: \"standard\" | \"carousel\"; cover: { brief, route, candidates[], approved, status }; figures: [ { id, brief, placement, status } ]; carousel: null | { source, pdf, status } }. format \"standard\" = cover + optional inline figures (cover.png is mandatory per the KTG cover-directive); format \"carousel\" = typografisk deck via render/build-carousel.mjs instead of cover+inline (cover/figures stay empty). route: \"mcp-image\" (default, via mcp__mcp-image__generate_image) | \"external\" (DALL·E / Midjourney / photographer → linkedin/NN/cover-raw.png). status ladder: pending → briefed → generated → approved. candidates[] holds the cover-v<N>-kandidat.png attempts; approved is the fixed approved name (\"cover.png\") once the operator-gate passes. figures[].id = \"fig1\"..; placement = section reference in NN-utkast.md (figures are referenced in the draft via  and uploaded manually in the LinkedIn editor — build-linkedin.mjs does NOT embed them). Naming convention: cover.png (approved, fixed — what build-linkedin.mjs reads) | cover-v<N>-kandidat.png (attempts) | cover-raw.png (optional external pre-edit source) | fig<N>.png (inline). credit + caption are recorded in <serie>/linkedin/image-credit-caption.md and flow into edition-config.json coverCredit + captions[NN]."
|
||||||
},
|
},
|
||||||
"schemaVersion": 1,
|
"schemaVersion": 1,
|
||||||
"series": {
|
"series": {
|
||||||
|
|
@ -41,6 +43,18 @@
|
||||||
"resonance": null,
|
"resonance": null,
|
||||||
"conversion": null
|
"conversion": null
|
||||||
},
|
},
|
||||||
|
"visualAssets": {
|
||||||
|
"format": "standard",
|
||||||
|
"cover": {
|
||||||
|
"brief": null,
|
||||||
|
"route": null,
|
||||||
|
"candidates": [],
|
||||||
|
"approved": null,
|
||||||
|
"status": "pending"
|
||||||
|
},
|
||||||
|
"figures": [],
|
||||||
|
"carousel": null
|
||||||
|
},
|
||||||
"locked": false,
|
"locked": false,
|
||||||
"scheduled": null
|
"scheduled": null
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,63 @@
|
||||||
|
# Bilde-credit + caption — cover per edition
|
||||||
|
|
||||||
|
> **TEMPLATE.** Copy this to `<serie>/linkedin/image-credit-caption.md` and fill it
|
||||||
|
> in per series. `/linkedin:newsletter` Step 7.5 (visual-assets phase) reads it and
|
||||||
|
> updates the row for the edition in production; the values flow into
|
||||||
|
> `<serie>/linkedin/edition-config.json` → `coverCredit` + `captions[NN]`, which
|
||||||
|
> `render/build-linkedin.mjs` reads when it builds `POST.html` (Step 8). This file
|
||||||
|
> is the human-readable source of truth for *motif + credit + caption*; the JSON is
|
||||||
|
> the machine copy the renderer consumes.
|
||||||
|
|
||||||
|
LinkedIn-editoren har et **«Add credit and caption»**-felt under hvert bilde. Fyll
|
||||||
|
inn per cover. Caption = én kort linje som koder artikkelens signal (det leseren
|
||||||
|
skal sitte igjen med), ikke en bildebeskrivelse.
|
||||||
|
|
||||||
|
> Format i editoren: ofte ett felt. Lim «Caption — Credit» eller bruk feltene hver
|
||||||
|
> for seg om de finnes.
|
||||||
|
|
||||||
|
## Verifiseringsplikt — credit skal være ærlig
|
||||||
|
|
||||||
|
Er coveret **KI-generert** (Nano Banana Pro / Gemini / DALL·E / Midjourney) →
|
||||||
|
credit MÅ si det. Aldri la et AI-bilde framstå som foto eller egenprodusert
|
||||||
|
illustrasjon. Eksempel-credit for AI-cover:
|
||||||
|
|
||||||
|
**Felles credit (alle editions):** `Illustrasjon generert med <verktøy>` — f.eks.
|
||||||
|
`Illustrasjon generert med Google Gemini (Nano Banana Pro)`.
|
||||||
|
|
||||||
|
Er coveret et ekte foto eller en håndlaget figur → bytt til den ærlige creditten
|
||||||
|
(`Foto: <fotograf>`, `Egenprodusert figur`). Avvik fra felles-creditten føres under.
|
||||||
|
|
||||||
|
**Per-edition credit-avvik:** _(list any edition whose credit differs from the
|
||||||
|
felles-credit, with the reason — e.g. «Del 3: Egenprodusert figur (kodet SVG)».
|
||||||
|
None by default.)_
|
||||||
|
|
||||||
|
## Motiv + caption per edition
|
||||||
|
|
||||||
|
| Del | Cover (motiv) | Caption |
|
||||||
|
|-----|---------------|---------|
|
||||||
|
| 01 | _<one-line motif — what the cover depicts>_ | _<one-line caption — the article's signal>_ |
|
||||||
|
| 02 | _…_ | _…_ |
|
||||||
|
| samle | _<optional samle-post badge/motif>_ | _<optional>_ |
|
||||||
|
|
||||||
|
## Naming-konvensjon (cover-filer)
|
||||||
|
|
||||||
|
- `cover.png` — **godkjent, fast navn**. Det eneste filnavnet `build-linkedin.mjs`
|
||||||
|
leser. Operator-gaten i Step 7.5 kopierer den godkjente kandidaten hit.
|
||||||
|
- `cover-v<N>-kandidat.png` — genererings-forsøk (mcp-image eller etterbehandlet).
|
||||||
|
Flere kan ligge side om side uten å overskrive den godkjente.
|
||||||
|
- `cover-raw.png` — valgfri ekstern pre-edit-kilde (DALL·E / Midjourney / fotograf).
|
||||||
|
- `fig<N>.png` — inline-figur (`fig1.png`, `fig2.png`, …), referert fra utkast-markdown
|
||||||
|
med `` og **lastet opp manuelt** i editoren
|
||||||
|
(`build-linkedin.mjs` embedder ikke figurer).
|
||||||
|
|
||||||
|
## Carousel-utgaver
|
||||||
|
|
||||||
|
Carousel-editions (typografisk deck via `render/build-carousel.mjs`) har som regel
|
||||||
|
**ingen foto-cover** → ingen bilde-credit nødvendig. Slide-kilden er
|
||||||
|
`linkedin/NN/carousel.md`, rendret til `linkedin/NN/carousel.pdf`. En carousel-edition
|
||||||
|
som *også* legger en feed-cover trenger likevel en rad over.
|
||||||
|
|
||||||
|
## Samle-post
|
||||||
|
|
||||||
|
Ev. Maskinrommet-/serie-badge (egen asset) → ingen credit. Lenken til serien ligger i
|
||||||
|
første kommentar, ikke i bildet.
|
||||||
Loading…
Add table
Add a link
Reference in a new issue