feat(linkedin): v2.4.0 — editorial-reviewer agent + Step 5.5 craft gate in /linkedin:newsletter

Endring 8 from the change spec (Del 4 production, Maskinrommet). The persona
resonance sweep measures reader-response (does it land?); nothing measured prose
craft or narrative architecture (is it well-made?). In Del 4 every persona
reported PASS, yet the editor found 8 fresh editorial points on first reading —
~6/8 craft/architecture blind spots no agent could see. v2.4.0 adds the missing
editor role.

New Step 5.5 (editorial-review) runs between fact-check (Step 5) and the persona
sweep (Step 6): a new editorial-reviewer agent (Opus) judges two axes —
prosa-handverk (em-dash density, verbatim repetition, postulated numbers,
contradictions, versal-tic) + narrativ-arkitektur (concrete instantiation,
theory-anchored hypotheses, series-title symmetry, equal action per addressee,
un-overloaded conclusion). Returns <=10 flags as direction (never copy), each
BLOCK/REWORK/NICE, operator-gated via SendUserFile. Runs before the persona
sweep so the personas measure resonance instead of stumbling on craft noise.
Mirrors the Maskinrommet writing-contract section C2 (bidirectional mirror rule).

- agents/editorial-reviewer.md (NEW, Opus, orange) + fasit fixture
  (editorial-reviewer-cases.md: Del 4 v5 gold standard, 8 points -> 2 axes +
  severities, 3 BLOCK / 5 REWORK, 6/8 blind spots) + structural lint (7 tests).
- Step 5.5 wired into commands/newsletter.md; pipeline 14 -> 15 phases.
- editorial-review phase + additive editorialReview state in
  config/edition-state.template.json; resumption: factcheck-sweep -> Step 5.5,
  editorial-review -> Step 6 (spec said fact-check; canonical key is
  factcheck-sweep).
- persona-reviewer contract unchanged: editorial-reviewer is supplementary
  (one measures craft, one measures response).
- All doc levels synced (plugin + root README/CLAUDE.md, CHANGELOG, plugin.json
  2.3.0 -> 2.4.0; agents 15 -> 16). 94 tests green.

Acceptance-criterion #8 (live run on Del 4 v5) delivered as fasit fixture:
a live run needs a session reload (new agent not invokable until then) + read
access to the Del 4 v5 draft in Maskinrommet.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Kjell Tore Guttormsen 2026-05-29 06:17:50 +02:00
commit 9df3de795c
11 changed files with 714 additions and 28 deletions

View file

@ -71,12 +71,13 @@ delegate the fan-out to a nested background agent.
> 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 (14 phases)
## Pipeline overview (15 phases)
The phase order is fixed. Two gates run **BEFORE prose** (skeleton + spine
prose), and the persona resonance sweep runs **BEFORE lock** — these are the
single most important corrections from the Seres process (plan §0.4, principle
5; v2.1 brief §1 on spine-error cost).
prose), an **editorial craft gate** runs before the persona sweep, and the
persona resonance sweep runs **BEFORE lock** — these are the single most
important corrections from the Seres process (plan §0.4, principle 5; v2.1 brief
§1 on spine-error cost; v2.4 on the editor/persona role split).
| Step | Phase | What | Tools |
|------|-------|------|-------|
@ -88,6 +89,7 @@ single most important corrections from the Seres process (plan §0.4, principle
| 3b | **Full prose expansion** | expand each section with argument, examples, anchors from research; may span sessions | `content-repurposer` + `Task` |
| 4 | **Consistency + quality** | threads, premise→conclusion arc, leader-takeaway, AI-slop removal, de-AI/voice scrub, formatting dose | inline + `references/longform-quality-rules.md` + **`voice-scrubber`** |
| 5 | **Fact-check sweep** | risk-sorted (🔴/🟡/🟢), guilty-until-disproven, verification log | **`fact-checker` (parallel)** |
| 5.5 | **Editorial review — BEFORE persona sweep** | editor's craft gate: prose-craft (em-dash density, verbatim repetition, postulated numbers, contradictions, versal-tic) + narrative-architecture (concrete instantiation, theory-anchored hypotheses, series-title symmetry, equal action per addressee, un-overloaded conclusion). ≤10 flags, BLOCK/REWORK/NICE. Operator-gated via `SendUserFile`. | **`editorial-reviewer`** + `SendUserFile` |
| 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.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` |
@ -95,11 +97,12 @@ single most important corrections from the Seres process (plan §0.4, principle
| 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 14 phases (Steps 02.5, 3a, 3b, 47, 7.5, 810) are
> implemented below. This command takes an edition end-to-end: load →
> **Build status:** all 15 phases (Steps 02.5, 3a, 3b, 4, 5, 5.5, 6, 7, 7.5,
> 810) are implemented below. This command takes an edition end-to-end: load →
> calibration → verified research → **skeleton + section pitch (operator +
> persona gate BEFORE prose)** → **spine prose (operator gate BEFORE full
> expansion)** → full prose draft → consistency/quality → fact-check sweep →
> **editorial review (craft gate, operator-gated BEFORE the persona sweep)**
> pre-lock persona sweep → optional annotation → **visual assets (cover/figures
> or carousel, operator-gated BEFORE lock)** → LOCK/delivery → post-lock hook
> gate → scheduling, persisting each phase to `edition-state.json` (machine) and
@ -182,7 +185,8 @@ Look up `edition-state.json` → `articles.<currentArticle>` (and the top-level
| `spine-prose` | Step 3b — Full prose expansion |
| `draft` | Step 4 — Consistency + quality *(see draft-cursor note)* |
| `consistency-quality` | Step 5 — Fact-check sweep |
| `factcheck-sweep` | Step 6 — Persona sweep (pre-lock) |
| `factcheck-sweep` | Step 5.5 — Editorial review *(v2.4 — craft gate BEFORE the persona sweep)* |
| `editorial-review` | Step 6 — Persona sweep (pre-lock) |
| `persona-sweep-prelock` | Step 7 — Annotation (optional) → Step 7.5 |
| `annotation` | Step 7.5 — Visual assets *(cover/figures or carousel deck, BEFORE lock)* |
| `visual-assets` | Step 8 — LOCK → delivery |
@ -720,9 +724,10 @@ was in fact the prime contractor — nearly a counter-example). Never trust a cl
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.
> unresolved 🔴 claim. Fact-check precedes the editorial review (Step 5.5) and the
> persona sweep (Step 6), which in turn precede lock — fixing facts can move text,
> and the text must settle before the editor judges craft and the reader jury
> judges resonance.
**Procedure:**
@ -781,14 +786,126 @@ Fact-check sweep complete.
- 🟢 Verified: <N> (sources logged to edition-state.json factcheckLog)
- Freshness flags (re-verify on publish day): <N or none>
Gate: [PASS — zero unresolved 🔴] (else REWORK/BLOCK with the open claims)
Next: Step 5.5 — Editorial review (craft gate, BEFORE the persona sweep).
```
## Step 5.5: Editorial review — craft gate, BEFORE the persona sweep
The fact-checked draft now faces the **editor**: a single `editorial-reviewer`
pass that judges **craft**, not reader-response. It asks *is this well-made?*
clean prose and sound narrative architecture — where Step 6 asks *does this land
for this reader?* Two different roles; both necessary, neither sufficient alone.
> **Why this step exists (Del 4 diagnosis, v2.4).** In the Del 4 production the
> persona resonance sweep returned 15 flags across three personas and **every
> persona reported PASS / ready-to-publish**. The editor then found **eight fresh
> editorial points on first reading**, and only ~25 % overlapped anything the
> personas had touched. The other six — a missing theory anchor, a broken
> series-title link, a stranded small-business addressee, verbatim repetitions,
> em-dash over-density, an internal contradiction — were **craft and architecture
> blind spots** no agent measured. A persona PASS was mis-reporting "ready for the
> editor's reading"; it only ever meant "lands for the reader." That gap cost an
> extra editorial round per article. Step 5.5 closes it.
> **Why BEFORE the persona sweep, not after (enforced).** The personas measure
> *response*. If the prose is locally messy — em-dash thickets, postulated
> numbers, a repeated phrase — the persona flags become **noise**: the reader
> stumbles on the craft defect instead of judging mobilization. Clean the craft
> first (here), and the Step 6 sweep measures exactly what it was built to
> measure. So editorial review runs after fact-check (Step 5) and before the
> persona sweep (Step 6). `[GATE]`
**Relationship to `persona-reviewer` (unchanged).** This step does **not** alter
the persona sweep. `editorial-reviewer` is *supplementary*: one agent measures
craft (this step), one measures reader-response (Step 6). The role boundary is
sharp — `editorial-reviewer` never flags "this won't resonate" (that is Step 6)
and `persona-reviewer` never flags em-dash density (that is this step).
> **Truth source — §C2.** The agent's two-axis checklist is the operationalized
> mirror of the **Maskinrommet skrivekontrakt §C2** (the craft half of the
> contract; §A — skeleton-before-prose — is mirrored by `longform-quality-rules.md`
> rule 8). §C2 is the source of truth: a change on either side must be mirrored to
> the other so the two never drift.
**Procedure:**
1. **Take the fact-checked draft** (Step 5 output → `currentPhase: "factcheck-sweep"`).
The body must already be fact-clean (no open 🔴) — editorial review runs on the
settled text, not on a draft still moving under fact fixes.
2. **Run `editorial-reviewer` (single foreground `Task` call).** Invoke
`subagent_type: linkedin-thought-leadership:editorial-reviewer` from THIS
command layer in the foreground (principle 4), passing the draft path
(`<serie>/NN-utkast.md`) and — when the edition is part of a series — the
series title (for the A3 series-title-symmetry check). The agent returns a
craft report: **≤10 flags** across two axes — **prosa-håndverk** (P1 em-dash
density · P2 verbatim repetition · P3 postulated numbers without source/hedge ·
P4 internal contradiction · P5 versal-tic) and **narrativ-arkitektur** (A1
concrete instantiation · A2 theory-anchored hypotheses · A3 series-title
symmetry · A4 equally-usable action per addressee · A5 un-overloaded
conclusion) — each flag carrying a **quote/line reference**, a **direction**
(never rewritten copy), and a **severity: BLOCK / REWORK / NICE**.
This is one foreground call (not a parallel fan-out): one editor reads the
whole draft. The agent has `Read` + `Grep` — the mechanical prose checks are
grep-able, the architecture checks need a read.
3. **Surface the report to the operator (`SendUserFile` — the Endring-5 pattern).**
The flags are surfaced to KTG as a **markdown report**, the same operator-gate
shape the visual-assets step (7.5) uses for candidates and Steps 2.5/3a use for
annotation:
1. Write the agent's report to `<serie>/NN-editorial-review.md` (NN = the same
zero-padded edition number; new suffix, a first-class artifact alongside
`NN-skjelett.md`).
2. `SendUserFile` it (else a markdown `file://` link) so KTG can read the flags
sorted BLOCK → REWORK → NICE and **decide which fold in**.
3. **KTG gates.** The agent's severity ranking is a *recommendation*; the
operator holds the gate. A BLOCK is the agent's strongest "must fix before
Step 6", not an automatic pipeline halt. `[OPERATØR]`
4. **Fold in the approved flags by tightening, → v(n+1).** Fold the flags KTG
approved into `<serie>/NN-utkast.md` **by tightening** (rule 6 of
`references/longform-quality-rules.md` — close the gap, hold the length flat;
never expand to paper over a craft defect). The result is the next draft
iteration. The editor (this session) holds the pen — never paste the agent's
direction as copy.
5. **Optionally re-run `editorial-reviewer` on the cleaned version.** If the
fold-in was substantive (especially any BLOCK), re-run the agent on v(n+1) to
confirm the flags cleared and no new craft defect was introduced by the edit.
This loop is cheap and is the point of the gate — every craft round saved here
is a KTG round saved at first reading.
6. **Persist + checkpoint state.** Once the editorial pass is folded in (and any
re-run confirms clean):
- Record the editorial report + which flags were folded vs. waived in
`edition-state.json``articles.NN.editorialReview` (the durable,
machine-readable record — what was flagged, severity, fold-in decision).
- Set `currentPhase: "editorial-review"` in `edition-state.json` (the marker
that Step 5.5 is complete and the operator-gate has passed).
- Write an "editorial review complete (craft clean) → next: persona sweep
(BEFORE lock)" line to `<serie>/STATE.md` (overwrite).
```
Editorial review (craft gate, BEFORE persona sweep) — complete.
- Flags: <N> (≤10) prosa-håndverk: <N> narrativ-arkitektur: <N>
- Severity: <N> BLOCK · <N> REWORK · <N> NICE
- Surfaced to operator: <serie>/NN-editorial-review.md (via SendUserFile) [OPERATØR]
- Folded in (by tightening): <N> Waived (operator): <N>
- Re-run on v(n+1): clean (or: skipped — fold-in non-substantive)
- Length delta vs. fact-checked draft: <flat/±N words> (target: flat — rule 6)
Gate: [PASS — craft clean, operator approved] (else: BLOCK flags open — loop)
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
The editorially-cleaned, 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 well-made (that was
Step 5.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
@ -1218,12 +1335,13 @@ Edition complete. Visible in /linkedin:calendar; mark live via /linkedin:calenda
## Reference Files
- `${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-state.template.json` — edition-state schema (15 phases including v2.1 skeleton + spine-prose gates, v2.3 visual-assets, and v2.4 editorial-review)
- `${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/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/editorial-reviewer.md` — Step 5.5 editor's craft gate (prose-craft + narrative-architecture, BLOCK/REWORK/NICE; mirrors Maskinrommet §C2)
- `${CLAUDE_PLUGIN_ROOT}/agents/persona-reviewer.md` — Step 2.5/6/9 reader jury (skeleton + resonance + conversion modes)
- `${CLAUDE_PLUGIN_ROOT}/agents/voice-scrubber.md` — Step 4 de-AI / Norwegian-chronicle voice scrub (gold standard = approved Norwegian editions, NOT the English post corpus)
- `${CLAUDE_PLUGIN_ROOT}/commands/react.md` — multi-source synthesis discipline (reused in Step 2)