---
name: linkedin:newsletter
description: |
  Long-form orchestrator: produce a newsletter edition (or any long-form piece)
  end-to-end at series quality — research → draft → fact-check → persona-review
  BEFORE lock → delivery → hook-gate. Multi-session with maintained edition-state.
  Use when the user is producing a newsletter, a long-form essay, or a series
  edition — NOT for short-form feed posts (use /linkedin:post, :quick, :react).
  Triggers on: "newsletter", "long-form", "edition", "linkedin newsletter",
  "write the next edition", "produce an essay", "series article", "/linkedin:newsletter".
allowed-tools:
  - Read
  - Glob
  - Grep
  - WebFetch
  - Bash
  - AskUserQuestion
  - Task
  - Write
---

# LinkedIn Newsletter — Long-Form Content Engine

You are the long-form orchestrator for the LTL plugin. You own the entire chain
for a newsletter edition — from research to a locked, delivered POST.html and a
post-lock hook-gate — at the quality the Seres series proved possible.

This command is **fundamentally different** from the short-form commands:

- **Heavier review machinery.** Long-form quality is enforced by *pipeline
  phases* (fact-check sweep + persona sweep + hook-gate), NOT by the short-form
  `PreToolUse` content-gatekeeper/voice-guardian hooks (those stay short-form-only).
- **State lives in the series folder, not the plugin.** Production state for an
  edition lives in the resolved **series root** (Step 0) — by default the
  maskinrommet series folder
  (`${LTL_SERIES_ROOT:-/Users/ktg/repos/maskinrommet/serier}/<slug>/`), per
  decision G, but any explicit path works (Step 0 resolution order). The plugin
  ships the *schema* (`config/edition-state.template.json`) and this command;
  the edition's actual state + drafts live with the series.
- **Multi-session by design.** A single edition spans several sessions. Every
  phase transition rewrites `edition-state.json` + the edition-HANDOVER so the
  next session resumes exactly where this one stopped.

## Architecture principle — all orchestration runs in the FOREGROUND from this command layer

**Every `Task` fan-out — research (Step 2), fact-check (Step 5), persona sweep
(Step 6) — is launched directly from THIS command, in the foreground.** Never
delegate the fan-out to a nested background agent.

> **Agent invocation form (required).** Plugin agents resolve only under their
> namespaced type — `subagent_type: linkedin-thought-leadership:<name>` (e.g.
> `linkedin-thought-leadership:fact-checker`), never the bare `<name>`. A bare
> `subagent_type` does not resolve and the `Task` call fails. Every
> `subagent_type` below is written in the namespaced form for this reason.

> **Why this is non-negotiable (principle 4, plan §3):** an agent spawned in the
> background loses access to the `Task`/Agent tool and silently degrades to
> *guessing* instead of parallelizing. The command layer (this session) is the
> only layer that can reliably spawn parallel sub-agents. So this command issues
> the parallel `Task` calls itself and synthesizes their returns inline.

## Pipeline overview (11 phases)

The phase order is fixed. The persona sweep runs **BEFORE** lock — this is the
single most important correction from the Seres process (plan §0.4, principle 5).

| Step | Phase | What | Tools |
|------|-------|------|-------|
| 0 | **Load context** | edition-state/HANDOVER, voice profile, persona library, series brief | `Read` |
| 1 | **Brief + calibration** | angle, voice, audience personas (mark primær), key points, tone, leader-takeaway. ≤3 questions | `AskUserQuestion` |
| 2 | **Research** | parallel scoped mandates → verified notes; triangulation | **`Task` fan-out (foreground)** |
| 3 | **Draft** | dramaturgical order, voice-matched; may span sessions | `content-repurposer` + `Task` |
| 4 | **Consistency + quality** | threads, premise→conclusion arc, leader-takeaway, AI-slop removal, formatting dose | inline + `references/longform-quality-rules.md` |
| 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) |
| 7 | **Annotation (optional)** | render annotatable review HTML for a manual pass | `render/build-html.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) |
| 10 | **Scheduling** | register the edition in the plugin queue/state for native scheduling | `hooks/scripts/queue-manager.mjs` |

> **Build status:** all 11 phases (Steps 0–10) are implemented below. This
> command takes an edition end-to-end: load → calibration → verified research →
> draft → consistency/quality → fact-check sweep → pre-lock persona sweep →
> optional annotation → LOCK/delivery → post-lock hook gate → scheduling,
> persisting each phase to `edition-state.json` and the HANDOVER and stopping
> cleanly between sessions.

---

## Step 0: Load context

Resume state first — this command is multi-session, so always reconstruct where
the edition left off before doing anything.

1. **Locate the series folder.** Resolve a **series root** — the folder that
   holds this edition. Resolution order:
   - If the operator passed an explicit path (e.g. `/linkedin:newsletter
     <path-to-serie>`), use it verbatim. This is how the edition is produced for
     any repo, a throwaway fixture, or a non-default location.
   - Otherwise derive it from the series slug under the **default series base**,
     `${LTL_SERIES_ROOT:-/Users/ktg/repos/maskinrommet/serier}/<slug>/`. The
     `LTL_SERIES_ROOT` env-var overrides the base without editing this command;
     the maskinrommet path is the default, not the only path.
   - If neither a path nor a resolvable slug is available, ask once which series
     (or series-root path) this edition belongs to.
   All later steps treat `<serie>` as this resolved series root; nothing below
   re-hardcodes the maskinrommet path.
2. **Read edition-state** (`<serie>/linkedin/edition-state.json`) if it exists —
   it tells you `currentArticle`, `currentPhase`, and per-article status, so you
   can resume mid-pipeline. The schema is documented in
   `${CLAUDE_PLUGIN_ROOT}/config/edition-state.template.json` (read it if you are
   initializing a new edition). If no state file exists, this is a fresh edition —
   you will create one at the end of Step 2.
3. **Read the edition-HANDOVER** (`<serie>/HANDOVER.md` or
   `<serie>/linkedin/edition-HANDOVER.md`) — the narrative state (§1 where we
   are, §4 immutable rules + fact-check log, §6 next session). The structure is
   defined by `${CLAUDE_PLUGIN_ROOT}/config/edition-HANDOVER.template.md` (copy +
   fill it when starting a new edition). This is the *production* HANDOVER for the
   edition — **distinct** from the plugin's `docs/BUILD-HANDOVER.local.md`, which
   governs building the plugin itself. Do not confuse or merge them.
4. **Read the voice profile** — `assets/voice-samples/authentic-voice-samples.md`
   and anything else under `assets/voice-samples/`. Long-form must match the
   author's voice; this is the reference for every drafting and review phase.
5. **Read the persona library** — `${CLAUDE_PLUGIN_ROOT}/config/personas.local.md`
   if it exists, else `${CLAUDE_PLUGIN_ROOT}/config/personas.template.md`. You
   will select the active personas + mark the primær in Step 1.
6. **Read the series brief** — whatever the series folder defines as its brief /
   premise / freshness rules (e.g. `<serie>/brief.md` or the HANDOVER §3–§5).
   This anchors angle and scope.

### Deterministic resumption — `currentPhase` → resume step

This command is multi-session: a session may be aborted (context budget, `/clear`,
interruption) at any phase. Resumption is **deterministic** — it is driven by the
`currentPhase` written to `edition-state.json`, never by re-asking the operator
where things stopped. Each Step sets `currentPhase` to its own phase **on
completion**, so resumption means *run the step AFTER the recorded phase*.

Look up `edition-state.json` → `articles.<currentArticle>` (and the top-level
`currentPhase`) in this table and **jump straight to the resume step**:

| `currentPhase` (last completed) | Resume at |
|---------------------------------|-----------|
| *(no state file)*               | **NEW edition** → Step 1 (init state at end of Step 2) |
| `load-context`                  | Step 1 — Brief + calibration |
| `brief-calibration`             | Step 2 — Research |
| `research`                      | Step 3 — Draft |
| `draft`                         | Step 4 — Consistency + quality *(see draft-cursor note)* |
| `consistency-quality`           | Step 5 — Fact-check sweep |
| `factcheck-sweep`               | Step 6 — Persona sweep (pre-lock) |
| `persona-sweep-prelock`         | Step 7 — Annotation (optional) → Step 8 |
| `annotation`                    | Step 8 — LOCK → delivery |
| `lock-delivery`                 | Step 9 — Hook / conversion gate |
| `hook-conversion-gate`          | Step 10 — Scheduling |
| `scheduling`                    | **Edition complete** — nothing to resume (start the next article or edition) |

The phase identifiers are the canonical ones defined in
`${CLAUDE_PLUGIN_ROOT}/config/edition-state.template.json` (`_doc.phases`); the
Steps below write exactly these strings. If `currentPhase` is missing or
unrecognized, do NOT guess — read the edition-HANDOVER §6 next-session pointer and
confirm with the operator before proceeding.

**Draft-cursor note (Step 3 only).** `draft` is the one phase that can be
*partial*: if Step 3 stopped mid-draft it records a section-level cursor in
`edition-state.json` and a "draft resumes at section <X>" pointer in HANDOVER §6.
On resume with `currentPhase: "draft"`, check for that cursor first — if present,
re-enter **Step 3** at the cursor and finish the draft before Step 4; only when the
HANDOVER records "draft complete" do you resume at **Step 4**.

> **Resumption is the deterministic test (plan §10, archetype E).** Abort after
> Step 6 → `currentPhase` is `persona-sweep-prelock` → re-run → the table resumes
> at Step 7. No operator question, no re-doing the persona sweep. The same holds
> at every row.

Then display a short status:

```
Edition: <series title> — article <currentArticle> "<title>"
Last completed phase: <currentPhase>   (or: NEW edition)
Resuming at: Step <N> — <step name>   (from the resumption table above)
Voice profile: loaded | MISSING
Persona library: <N> personas loaded (active set chosen in Step 1)
```

If the voice profile or persona library is missing, say so plainly and continue —
do not fabricate either.

## Step 1: Brief + calibration

Establish the edition brief with **at most ~3 calibration questions**. Infer
everything you can from Step 0 (series brief, HANDOVER, prior edition); only ask
what genuinely changes the work.

Settle these dimensions (most should come from context, not questions):

- **Angle** — the one premise this edition argues.
- **Voice** — confirmed from the voice profile (no question needed unless drift).
- **Audience personas** — select the relevant subset from the persona library
  and **mark exactly one as primær**. The primær reader weighs highest in the
  Step 6 sweep; a *secondary* NO from a role/expertise mismatch is a SIGNAL the
  gate works (accept it), but a *primær* NO is never accepted (revise until a
  clean YES). See `config/personas.template.md` → "How the library is used".
- **Key points** — the 2–4 load-bearing claims the edition must make.
- **Tone** — respected-peer vs. teaching-down; calibrated to the primær.
- **Leader-takeaway** — the ONE takeaway + ONE concrete action the reader leaves
  with (plan §8: cut references hard, hands-on credibility beats citation-piles).

Use `AskUserQuestion` only for the genuinely open dimensions (cap ≈3). Good
candidates: which personas are in scope + which is primær; the angle if the
series brief leaves it open; fold-in aggressiveness for later sweeps
(conservative vs. aggressive — plan §8, a per-sweep user choice, not a default).

Record the resolved brief inline (you will persist it to edition-state in Step 2):

```
Edition brief
- Angle: <one sentence>
- Primær persona: <name>   | Secondary: <names>
- Key points: <2–4 bullets>
- Tone: <…>
- Leader-takeaway: <one takeaway + one action>
```

## Step 2: Research — parallel `Task` fan-out (foreground)

> **This is the load-bearing phase.** Quality long-form needs verified, triangulated
> research, and it must be produced by **real parallel `Task` calls issued from this
> command layer** — not sequential guessing, not a background agent. (Principle 4.)

**Procedure:**

1. **Decompose** the edition's key points (Step 1) into 2–5 *scoped, orthogonal*
   research sub-questions. Each sub-question must be answerable independently so
   the calls can run in parallel without overlap. Reuse the multi-source synthesis
   discipline from `commands/react.md` (Comparison Path, Steps 2b–3b): per source,
   extract claims, stance, data points; then look across sources for common ground,
   tension, and blind spots.

2. **Fan out in parallel — issue all sub-question `Task` calls in a SINGLE message**
   (multiple `Task` tool-uses in one turn) so they run concurrently. Each call gets
   a tightly-scoped inline mandate and a fixed return schema. Use `WebSearch`-capable
   research agents (e.g. `general-purpose`, or the voyage docs/community researchers
   when available). Mandate template per call:

   ```
   Research sub-question: <one scoped question>
   Constraints: cite primary/credible sources; distinguish verified fact from
   inference; if you cannot verify a claim, label it UNVERIFIED — never fill the
   gap with a guess (this feeds a later fact-check sweep that assumes
   guilty-until-disproven).
   Return EXACTLY this structure:
     - Findings: 3–5 bullets, each with a source
     - Data points: any statistics/figures with source + date
     - Confidence: high | medium | low, with one-line reasoning
     - Open/unverified: anything that could not be confirmed
   ```

3. **Detect degradation (gate).** When the parallel calls return, confirm each
   came back **structured and populated** (Findings + sources present), not empty,
   refused, or collapsed to a single hedged paragraph. If the fan-out degraded —
   calls ran sequentially, returned no sources, or one silently produced a guess —
   **stop and escalate to the operator** (do NOT paper over it by re-running the
   research sequentially without sign-off). This is the assumption the whole
   long-form pipeline rests on.

4. **Triangulate + synthesize.** Cross-check the returns: where do sources agree,
   where do they conflict, what is everyone missing? Produce a single set of
   **verified research notes** organized by key point, each note tagged with its
   source(s) and a confidence marker. Carry forward the `Open/unverified` items —
   they become 🟡 entries for the Step 5 fact-check sweep.

5. **Persist + checkpoint state.** Write the resolved brief (Step 1) and the
   verified research notes into the edition's `edition-state.json`
   (`currentPhase: "research"`, article status `in-progress`) and append a
   "research complete → next: draft" pointer to the edition-HANDOVER §6. If this
   is a fresh edition, initialize `edition-state.json` from the template schema
   first. Then stop cleanly — drafting (Step 3) begins in the next session.

```
Research phase complete.
- Sub-questions: <N> (ran in parallel)
- Verified notes: <N> by key point
- Carried to fact-check (🟡 unverified): <N>
State written: <serie>/linkedin/edition-state.json (phase: research)
Next session: Step 3 — Draft.
```

---

## Step 3: Draft — dramaturgical order, voice-matched

Turn the verified research notes (Step 2) into a full draft. This is a
*long-form* draft, not a feed post: it has an arc, not a hook-and-three-bullets.

> **This phase may span multiple sessions.** A long edition can exceed a single
> session's context budget. If you approach the budget mid-draft, stop cleanly,
> write the partial draft to the series root as `<serie>/NN-utkast.md` (the
> canonical draft path — see step 4), record `currentPhase: "draft"`
> with a section-level cursor in `edition-state.json`, and append a precise
> "draft resumes at section <X>" pointer to the edition-HANDOVER §6. The next
> session re-reads Step 0, picks up the cursor, and continues. Never start the
> consistency pass (Step 4) on a half-written draft.

**Procedure:**

1. **Re-read the voice profile** (`assets/voice-samples/`) before writing a single
   sentence — this is the existing LTL rule and it is not optional for long-form.
   The draft must read as the author, not as generic LinkedIn prose.

2. **Lay out the dramaturgical order** from the brief (Step 1) and notes (Step 2):
   - **Ingress + first paragraph** establish ONE clear premise (this is the front
     half of the premise→conclusion arc enforced in Step 4).
   - **Body** develops the 2–4 key points in the order that builds tension, each
     anchored to a verified note. Carry each note's source marker inline as a
     comment so the Step 5 fact-check sweep can find it.
   - **Conclusion** grips the premise concretely and twists it forward (direction +
     one concrete grip) — it does not merely summarize.

3. **Draft with the `content-repurposer` muscle.** Reuse `agents/content-repurposer.md`
   (its article→long-form conversion discipline) for the section-to-prose work —
   invoke it via `Task` (`subagent_type: linkedin-thought-leadership:content-repurposer`)
   for individual sections when useful, *from this command layer* (foreground,
   principle 4). The command owns assembly and voice; the
   agent assists with conversion. The draft is voice-matched by THIS session, not
   self-certified for voice — voice-match remains an `[OPERATØR]` / `[GATE:
   voice-trainer]` judgment, never auto-passed (plan §10.0).

4. **Write the draft** to the **series root** as `<serie>/NN-utkast.md` (NN =
   zero-padded edition number — the SAME filename Steps 7 and 8 render from).
   This is the single canonical draft path: `render/build-html.mjs` (Step 7) and
   `render/build-linkedin.mjs` (Step 8) both consume `NN-utkast.md` from cwd, and
   the renderer **silently skips** any draft without an `NN` prefix. Do NOT write
   to `linkedin/<article>.draft.md` — that path is skipped at render. Set
   `currentPhase: "draft"` in `edition-state.json`, and append a "draft complete →
   next: consistency/quality" pointer to the HANDOVER §6.

```
Draft complete (or: partial — resumes at section <X>).
- Premise established: <one line>
- Key points drafted: <N>/<N>
- Voice-match: [OPERATØR]/[GATE: voice-trainer] — NOT self-certified
Draft written: <serie>/NN-utkast.md   (series root, NN-prefixed — Steps 7/8 render this exact file)
Next: Step 4 — Consistency + quality.
```

## Step 4: Consistency + quality

Run the draft through the long-form quality rules. This is a *tightening* pass —
the gap between draft and final is closed by **swapping weaker for sharper and
cutting, not by expansion**; hold the length flat (plan §8).

> **Canonical rules live in one place.** The long-form quality rules are codified
> in **`${CLAUDE_PLUGIN_ROOT}/references/longform-quality-rules.md`** — read it
> now and apply it. There is exactly one source of truth; this Step does not
> restate the rules, it enforces them. (The rules were inlined here through plan
> step S11 and extracted to the reference file at S12, per the original
> forward-reference note — no rule text is duplicated.)

**Calibration first (rule 7 — a per-sweep user choice, not a default):** before
running this pass, confirm fold-in aggressiveness (conservative vs. aggressive),
jargon handling, and — when it matters later — persona weighting on conflict. Ask
once if the Step 1 brief did not already settle it.

Apply the reference rules and report a **pass/flag per rule**. The operative
checklist (full detail + pass/flag criteria in the reference file):

1. **Threads** *(consistency)* — every thread opened in the ingress/body resolves
   by the conclusion; no dropped setups, no orphaned promises.
2. **Leder-takeaway** (rule 1) — ONE takeaway + ONE concrete action; cut references
   hard.
3. **Premiss→konklusjon-bue** (rule 2) — ingress-premise == conclusion-premise; the
   close grips and twists forward, never just summarizes.
4. **AI-slop-fraser** (rule 3) — strip the Norwegian ban-list on sight (report a
   removed-count).
5. **Generell, ikke etat-/person-spesifikk** (rule 4) — opportunities not
   provocations; ≤1 structural anchor.
6. **Formaterings-dose** (rule 5) — minimal; no PowerPoint-printout.
7. **Stramming, ikke utvidelse** (rule 6) — close gaps by tightening; hold the
   length flat.

After the pass, set `currentPhase: "consistency-quality"` in `edition-state.json`
and append a "quality pass complete → next: fact-check sweep" pointer to the
HANDOVER §6.

```
Consistency + quality pass complete.
- Threads resolved: <yes/flags>
- Premise→conclusion arc: <intact/realigned>
- Leader-takeaway: <one line>
- AI-slop phrases removed: <N>
- Formatting dose: <within bounds/trimmed>
- Length delta vs. draft: <flat/±N words>   (target: flat)
Next: Step 5 — Fact-check sweep (guilty-until-disproven, BEFORE lock).
```

## Step 5: Fact-check sweep — guilty-until-disproven (BEFORE lock)

Every factual claim in the consistency-passed draft (Step 4) is now treated as
**guilty until proven** — it is not true until a primary or credible source
confirms it. This is its OWN pipeline phase, separate from the quality pass,
because in the Seres production ~15 factual errors slipped past both the research
notes and a subagent's reasoning and were caught only here (plan §0.5, the
"Altinn error": Altinn was used as an example of "built in-house" when Accenture
was in fact the prime contractor — nearly a counter-example). Never trust a claim
because it "feels" right or because it sits in your own research notes.

> **This sweep runs BEFORE lock.** No edition is locked (Step 8) with an
> unresolved 🔴 claim. Fact-check precedes the persona sweep (Step 6), which in
> turn precedes lock — fixing facts can move text, and the text must settle
> before the reader jury judges resonance.

**Procedure:**

1. **Extract every checkable claim** from the draft: numbers, named examples,
   quotes, dates, who-did-what, causal claims. Skip opinions and predictions —
   they are not claims to verify. Pull the inline source markers the draft
   carried from Step 2/3 so each claim arrives with its provenance, and fold in
   the 🟡 `Open/unverified` items carried forward from Step 2 — they enter this
   sweep automatically.

2. **Group the claims into N orthogonal blocks** (by section or by topic) so each
   block can be verified independently without overlap.

3. **Fan out in parallel — issue all N `fact-checker` calls in a SINGLE message**
   (multiple `Task` tool-uses in one turn, `subagent_type: linkedin-thought-leadership:fact-checker`) so they
   run concurrently, from THIS command layer in the foreground (principle 4, plan
   §3). Each call gets one claim-block and returns the agent's standard
   verification log + risk-sort (🔴/🟡/🟢) + gate decision (PASS/REWORK/BLOCK).
   Do not delegate the fan-out to a nested background agent — it would lose the
   `Task` tool and silently degrade to guessing (the same gate as Step 2).

4. **Detect degradation (gate).** Confirm each return is structured and populated
   — a real verification log with per-claim verdicts and searched sources, not an
   empty or hedged paragraph. If any call degraded (ran without searching,
   returned no sources, or guessed a citation), **stop and escalate to the
   operator** — do not silently re-run sequentially. `[GATE]`

5. **Merge into one verification log + risk sort.** Combine the per-block logs
   into a single edition-level table, risk-sorted 🔴/🟡/🟢, then resolve:
   - 🔴 **High risk** (contradicted, or a precise claim with no usable source) →
     must be fixed before proceeding. Fix by sourcing, softening to a hedged
     opinion, or cutting — **by tightening, never by expanding** (the Step 4 rule
     still holds). Re-run the relevant `fact-checker` call on the fix.
   - 🟡 **Unverified** → either frame it explicitly as opinion/hedge in the draft,
     source it, or cut it. A 🟡 left asserted as bare fact is a REWORK.
   - 🟢 **Verified** → keep; record its source in the verification log.

   The sweep is not done until there are **zero unresolved 🔴** and every 🟡 is
   either resolved or deliberately framed as opinion. This is the fact-gate;
   verdicts are `[GATE]` (objective where a source settles it; operator judgment
   on hedge-vs-cut). Flag any time-sensitive figure (e.g. a valuation) as a
   **freshness flag** to re-verify on publish day — a checklist item, not a text
   weakness.

6. **Persist + checkpoint state.** Write the merged verification log into the
   edition-HANDOVER §4 (immutable rules + fact-check log — the durable record of
   what was checked), set `currentPhase: "factcheck-sweep"` in `edition-state.json`,
   and append a "fact-check complete → next: persona sweep (BEFORE lock)" pointer
   to the HANDOVER §6.

```
Fact-check sweep complete.
- Claims checked: <N> across <N> parallel blocks
- 🔴 High risk: <N> → all resolved (sourced/softened/cut)
- 🟡 Unverified: <N> → <resolved / framed as opinion>
- 🟢 Verified: <N> (sources logged to HANDOVER §4)
- Freshness flags (re-verify on publish day): <N or none>
Gate: [PASS — zero unresolved 🔴]   (else REWORK/BLOCK with the open claims)
Next: Step 6 — Persona sweep (reader jury, BEFORE lock).
```

## Step 6: Persona sweep — reader jury, BEFORE lock

The fact-checked draft now faces the **reader jury**: the personas selected in
Step 1 read it read-only and judge whether it *lands* — not whether it is correct
(that was Step 5), not whether it is original. This is the **single most
important ordering rule in the whole pipeline.** In the Seres production this
sweep was originally run *after* the texts were locked (Step 8), which forced
reopening locked texts — the biggest single process error of the series (plan
§0.4). **It runs here, FØR lås / before lock, without exception.**

> **Order assertion (enforced).** This persona sweep (Step 6) precedes lock
> (Step 8). The pipeline may NOT proceed to Step 8 until the primær persona
> returns a clean JA from this sweep. A wiring that locked first and reviewed
> after would reproduce the exact Seres failure — do not do it. `[GATE]`

**Procedure:**

1. **Load the active personas** chosen in Step 1, with exactly one marked
   **primær**. Each persona's five fields (rolle, avkobler, overbeviser,
   ekspertise, sjargong) come from `config/personas.local.md` (or the template).

2. **Fan out one `persona-reviewer` call per persona, in parallel** — issue them
   in a SINGLE message (multiple `Task` tool-uses, `subagent_type:
   linkedin-thought-leadership:persona-reviewer`), from THIS command layer in the
   foreground (principle 4).
   Pass each call its persona name and **`mode: resonans`** (the before-lock mode
   — all six axes, ≤5 flags as direction). This is NOT conversion mode, which is
   the post-lock hook-gate in Step 9. One persona per run — never mix two.

3. **Collect verdicts and gate.** Each call returns per-axis flags
   (LØST/DELVIS/IKKE), ≤5 direction-only flags, a per-persona verdict (JA/NEI),
   and a gate decision. Aggregate per the agent's rule:
   - **primær JA** + no real (non-ceiling) sekundær IKKE → PASS, ready to lock.
   - **primær NEI**, or a fixable DELVIS/IKKE → REWORK.
   - **primær NEI on Krok or Leder-takeaway** → BLOCK (the reader never starts,
     or leaves with nothing to do) — must be reworked before lock.
   A *sekundær* NEI from a role mismatch or expertise ceiling is a SIGNAL the gate
   works — accept it; do not distort the text to chase it (plan §0.5, "primær
   trumfer"). The jury returns **direction only** — the editor (this session)
   holds the pen; never paste a persona's rewritten copy. `[GATE]`

4. **Convergence loop.** If the gate is REWORK/BLOCK, fold the flags into the
   draft **by tightening** (the Step 4 rule holds — close the gap, hold the length
   flat), then **re-run the same `persona-reviewer` calls** against the revised
   draft. Each round re-judges every prior flag as LØST / DELVIS / IKKE. Loop
   until the primær returns a clean JA (in Seres this took 2 rounds). Re-run only
   the personas whose verdicts are still open.

5. **Persist + checkpoint state.** Record the final per-persona verdicts and the
   resolved flags in the edition-HANDOVER §5 (method / persona calibration), set
   `currentPhase: "persona-sweep-prelock"` in `edition-state.json`, and append a "persona sweep
   PASS (primær JA) → next: lock/delivery" pointer to the HANDOVER §6.

```
Persona sweep complete (BEFORE lock).
- Personas run: <N> (primær: <name>)
- Convergence rounds: <N>
- primær verdict: JA   (else: still NEI — loop open, NOT ready to lock)
- Accepted sekundær ceiling-NOs (signal, not failure): <N or none>
Gate: [PASS — primær JA, ready to lock]   (else REWORK/BLOCK)
Next: Step 7 — Annotation (optional), then Step 8 — LOCK → delivery.
```

---

## Step 7: Annotation — optional annotatable review HTML

Before locking, you may render the draft as a self-contained, annotatable HTML
page for one last manual read in the browser (the same pencil-toggle annotation
surface the render scripts ship). This step is **optional** — skip it if the
editor is satisfied with the in-session draft. It does not gate lock.

**Procedure:**

1. **Confirm the draft path.** The consistency- and persona-passed draft from
   Steps 4–6 is the `NN-utkast.md` (NN = zero-padded edition number) in the
   series folder, with YAML front matter (`title`, etc.).

2. **Render the review HTML.** `render/build-html.mjs` writes to `./review/`
   relative to the *current working directory*, so run it **with cwd = the
   series folder** (not the plugin). Pass the draft file:

   ```bash
   cd <serie-mappe> && node "${CLAUDE_PLUGIN_ROOT}/render/build-html.mjs" NN-utkast.md
   ```

   **Check the exit code (N3 — do not assume success).** As of S14/F7 the exit
   code is authoritative: `build-html.mjs` exits **non-zero when zero HTML files
   were produced** (e.g. a typo'd/missing filename — it prints `Fant ikke:` then
   `Ingen HTML produsert …`), and exits 0 only when at least one file was written.
   Still confirm the expected output file exists — verify
   `<serie-mappe>/review/NN-utkast.html` is present, not just exit 0. Report the
   stderr and do NOT advance the phase if the file is missing.

3. **Hand off the link.** On success the script prints `Skrev <path> (<KB>)`.
   Surface `<serie-mappe>/review/NN-utkast.html` as a `file://` link for the
   editor's manual annotation pass. Fold any resulting edits back **by
   tightening** (the Step 4 rule still holds) — then, if the edit was
   substantive, re-run the affected sweep (Step 5 or 6) before proceeding.

4. **Persist.** Set `currentPhase: "annotation"` in `edition-state.json` and
   append an "annotation rendered (optional) → next: lock/delivery" pointer to
   the HANDOVER §6. If skipped, note "annotation skipped" and move on.

```
Annotation (optional).
- Rendered: <serie-mappe>/review/NN-utkast.html   (or: skipped)
- build-html exit: 0   (else: non-zero — review HTML NOT produced, see stderr)
Next: Step 8 — LOCK → delivery.
```

---

## Step 8: LOCK → delivery — POST.html "all in one place"

This is the **lock**. Only enter it once the Step 6 persona sweep returned a
clean primær JA and the Step 5 fact-check has no unresolved 🔴. Locking
produces the editor's single delivery artifact — `POST.html`, the
"all-in-one-place" page that carries the edition text plus its distribution
(delingstekst) copy, ready to paste into LinkedIn.

> **Order assertion (enforced).** Lock (Step 8) runs AFTER the pre-lock persona
> sweep (Step 6) and BEFORE the hook/conversion gate (Step 9). Reversing lock
> and the pre-lock sweep reproduces the exact Seres failure (reopening locked
> texts) — see Step 6. The post-lock hook-gate (Step 9) judges only the
> distribution hook and never reopens the locked body. `[GATE]`

**Procedure:**

1. **Confirm lock preconditions.** In `edition-state.json`: the article's
   `factcheckLog` has no open 🔴 and `personaSweep.resonance` recorded a primær
   JA. If either is missing, STOP — return to Step 5/6. Do not lock past an
   open gate.

2. **Confirm the delivery inputs in the series folder.**
   `render/build-linkedin.mjs` reads, relative to cwd (`<serie>/linkedin/`):
   - `linkedin/edition-config.json` — calendar, freshness, cover credit/caption.
     Template + schema: `${CLAUDE_PLUGIN_ROOT}/config/edition-config.template.json`.
   - `linkedin/edition-delingstekst.md` — the per-edition distribution text (and
     the `samle` post). Template: `${CLAUDE_PLUGIN_ROOT}/config/edition-delingstekst.template.md`,
     whose header documents the exact `## Del N —` / `## Samle` section grammar the
     renderer parses.

   Both inputs are **optional and graceful** (renderer degrades, does not throw):
   a missing or malformed `edition-config.json` falls back to empty defaults, and a
   missing `edition-delingstekst.md` yields no distribution copy while the article
   `POST.html` still builds. Provide both for a complete delivery — the
   distribution hook is what Step 9 gates.

3. **Render POST.html.** Run with **cwd = the series folder** (the script
   resolves `linkedin/` from cwd and writes `linkedin/NN/POST.html`). The draft
   filename MUST keep its two-digit `NN` prefix or the script skips it
   (`↷ hopper over … (ikke NN-prefiks)`):

   ```bash
   cd <serie-mappe> && node "${CLAUDE_PLUGIN_ROOT}/render/build-linkedin.mjs" NN-utkast.md
   ```

   **Check the exit code (N3 — do not assume success).** Confirm exit 0 AND
   that `linkedin/NN/POST.html` now exists (the `✓ linkedin/NN/POST.html`
   line). A skip-warning with exit 0 but no NN file means the prefix was wrong —
   treat as failure, rename, re-run. Surface any throw (missing config /
   delingstekst) with its stderr.

4. **Lock the article.** Set `locked: true`, `status: "locked"`, and
   `currentPhase: "lock-delivery"` in `edition-state.json`. Surface
   `<serie-mappe>/linkedin/NN/POST.html` as a `file://` link. From here the
   body is frozen — Step 9 may only adjust the distribution hook, never the
   locked edition text.

```
LOCK → delivery.
- Preconditions: factcheck 🔴 = none, persona resonans primær = JA   (else: STOP)
- Delivered: <serie-mappe>/linkedin/NN/POST.html
- build-linkedin exit: 0 + POST.html present   (else: non-zero / skip — NOT locked)
- Article status: locked
Next: Step 9 — Hook / conversion gate (post-lock).
```

---

## Step 9: Hook / conversion gate — "would YOU click?" (AFTER lock)

The locked edition still has to earn the click. The **distribution text** — the
delingstekst hook that fronts the post in the feed — now faces a final binary
gate: would the primær persona, scrolling, actually stop and open this? This is
the post-lock conversion sweep, distinct from the pre-lock resonance sweep
(Step 6): it judges the **hook only**, binary, never the body.

> **Order assertion (enforced).** This hook-gate (Step 9) runs AFTER lock
> (Step 8) — it operates on the distribution text of an already-locked edition
> and must never reopen the locked body. If the hook fails, you revise the
> *delingstekst* (and re-render via Step 8's `build-linkedin.mjs`), not the
> edition text. This ordering is the inverse of the resonance sweep, which runs
> BEFORE lock — keeping the two apart is what fixed the Seres process. `[GATE]`

**Procedure:**

1. **Take the distribution hook** — the first two lines of the edition's entry
   in `linkedin/edition-delingstekst.md` (and the `samle` hook, if shipping the
   collected post). This is what the reader sees before "…see more".

2. **Run `persona-reviewer` in conversion mode** (`subagent_type:
   linkedin-thought-leadership:persona-reviewer`) for the **primær** persona
   only, from THIS command layer in the foreground. Pass
   **`mode: konverter`** (the after-lock, hook-only mode — NOT resonans). The
   agent returns a single binary verdict, **JA / NEI**, on «would YOU click?» —
   no axis scoring, no flags, no rewritten copy (principle: the jury judges,
   the editor writes).

3. **Gate on the binary.**
   - **JA** → the hook converts. Proceed to Step 10.
   - **NEI** → the hook does not earn the click. Revise the **delingstekst hook
     only** (sharpen the krok by tightening — close the gap, hold the body
     frozen), **re-render POST.html** via Step 8's `build-linkedin.mjs` (the
     body stays locked; only the distribution copy changed), and re-run the
     conversion check. Loop until JA. `[GATE]`

4. **Persist.** Record the conversion verdict in
   `edition-state.json` → `articles.NN.personaSweep.conversion` (and HANDOVER §5),
   and set `currentPhase: "hook-conversion-gate"`.

```
Hook / conversion gate (post-lock).
- Primær persona: <name>   mode: konverter (hook only)
- Verdict: JA — hook converts   (else: NEI — revise delingstekst hook, re-render, re-check)
- Body: untouched (locked in Step 8)
Gate: [PASS — JA]   (else loop on the distribution hook)
Next: Step 10 — Scheduling.
```

---

## Step 10: Scheduling — register the edition in the plugin queue

The locked, conversion-passed edition is ready to ship. Register it in the
plugin's native scheduling queue so it shows up in `/linkedin:calendar`
(both for viewing and for the publish action) and the posting-time
reminders — the same queue the short-form pipeline uses. The edition is
now a first-class scheduled post.

**Procedure:**

1. **Pick the slot.** Confirm the target `scheduled_date` (YYYY-MM-DD) and
   `scheduled_time` from the edition-config calendar / the series brief. If the
   slot is unset, ask once (Step 1's calibration may already have settled it).

2. **Register via `queue-manager.mjs`.** Add one queue entry for the edition,
   reusing the short-form queue contract — `queueAdd(id, draftPath, schedDate,
   schedTime, pillar, format, hookPreview, charCount)`:
   - `id` — stable edition id (e.g. `<series-slug>-NN`)
   - `draftPath` — the locked `linkedin/NN/POST.html`
   - `format` — `newsletter` (so calendar/reporting can distinguish long-form)
   - `hookPreview` — the conversion-passed distribution hook (first ~80 chars)

   ```bash
   node -e 'import("'"${CLAUDE_PLUGIN_ROOT}"'/hooks/scripts/queue-manager.mjs").then(q => q.queueAdd("<series-slug>-NN","<serie-mappe>/linkedin/NN/POST.html","YYYY-MM-DD","HH:MM","<pillar>","newsletter","<hook ~80c>",<charCount>))'
   ```

   The function appends to `assets/drafts/queue.json` with `status:
   "scheduled"` and returns the new entry.

3. **Persist + close the edition.** Set the article's `status: "scheduled"`,
   `scheduled: "<YYYY-MM-DD HH:MM>"`, and `currentPhase: "scheduling"` in
   `edition-state.json`. Append a closing "edition scheduled → mark live via
   `/linkedin:calendar` on <date>" pointer to the HANDOVER §6. The pipeline is
   complete.

```
Scheduling.
- Queue entry: <series-slug>-NN → assets/drafts/queue.json (status: scheduled)
- Slot: YYYY-MM-DD HH:MM   format: newsletter
- Article status: scheduled
Edition complete. Visible in /linkedin:calendar; mark live via /linkedin:calendar (publish action).
```

---

## Reference Files

- `${CLAUDE_PLUGIN_ROOT}/config/edition-state.template.json` — edition-state schema (11 phases)
- `${CLAUDE_PLUGIN_ROOT}/config/edition-config.template.json` — static delivery metadata schema (calendar, freshness, credit, captions) — Step 8
- `${CLAUDE_PLUGIN_ROOT}/config/edition-delingstekst.template.md` — distribution-copy grammar (`## Del N —` / `## Samle`) — Steps 8/9
- `${CLAUDE_PLUGIN_ROOT}/config/edition-HANDOVER.template.md` — narrative production-state structure (§1–§6) — Step 0
- `${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/persona-reviewer.md` — Step 6/9 reader jury (resonance + conversion modes)
- `${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}/render/build-linkedin.mjs` — POST.html delivery (Step 8)
- `${CLAUDE_PLUGIN_ROOT}/render/build-html.mjs` — annotatable review renderer (Step 7)
