feat(linkedin): v2.2.0 — harden longform gates from 2nd production run

Implements the 6-change spec from the Seres-serien production
(linkedin-plugin-endringsspec.md). All acceptance criteria met.

1. Avoid-patterns (modell-/navne-katalog, completeness-over-reader-action,
   self-referential overhead openings) → longform-quality-rules.md (rule 1+3)
   + user-profile.template.md.
2. Persona gate now BLOCKING with explicit hard-fail list (primær mistet meg /
   doesn't own action / sjargong-mur / modell-navne-katalog → BLOCK;
   "JA med store forbehold" = NEI) → persona-reviewer.md + personas.template.md.
3. Fact-check declared orthogonal to narrative strength + post-cutoff
   web-search mandate + high-frequency-error checklist → fact-checker.md.
4. NEW agent voice-scrubber.md (Opus) — de-AI scrub + Norwegian-chronicle
   voice-drift; gold standard = approved Norwegian editions, NOT the English
   post corpus. Wired into newsletter.md Step 4.
5. Operator gates = render+annotate rounds (build-html.mjs to file://) as
   primary flow, AskUserQuestion as receipt/fallback → newsletter.md 2.5+3a.
6. Edition state reconciled with STATE.md (ONE-system). edition-HANDOVER
   template deleted; narrative to <serie>/STATE.md, machine data
   (factcheckLog, personaSweep, immutableRules) to edition-state.json.

Agents 14 to 15; commands unchanged (24). Backward-compatible (additive
state-shape only). Docs updated across all three levels + CHANGELOG.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Kjell Tore Guttormsen 2026-05-28 20:50:56 +02:00
commit 4ed9717627
15 changed files with 494 additions and 152 deletions

View file

@ -1,56 +0,0 @@
<!--
TEMPLATE — edition-HANDOVER.md (narrative production state for one edition)
Purpose : the human-readable narrative companion to edition-state.json. Where
edition-state.json is the machine-readable resumption state (currentPhase,
per-article status), this HANDOVER carries the *narrative* state a human
(or the next session) reads to understand where the edition is, what is
immutable, and what happens next. /linkedin:newsletter Step 0 reads it;
every phase appends to §6.
Decision: G — production lives in the series root, NOT the plugin. Copy this to
<serie>/linkedin/edition-HANDOVER.md (or <serie>/HANDOVER.md) and fill in.
DISTINCT from the plugin's own docs/BUILD-HANDOVER.local.md (which governs building
the plugin). Never merge the two.
Section numbering is referenced by name in commands/newsletter.md:
§1 where we are · §3§5 brief/rules/calibration · §4 immutable rules + fact-check
log · §5 persona calibration + conversion verdict · §6 next-session pointer.
-->
# Edition HANDOVER — <series title>, article <NN> "<edition title>"
## §1 — Where we are
- Current phase: <currentPhase, mirrors edition-state.json>
- Article status: <pending | in-progress | locked | scheduled>
- One-line state: <e.g. "draft complete, consistency pass next" or "locked, awaiting hook gate">
## §2 — Premise & angle
- Premise (one clear claim): <…>
- Angle / dramaturgical spine: <…>
- Leader-takeaway (the one thing a busy reader keeps): <…>
## §3 — Brief & scope
- Audience personas (active set; mark primær): <persona A (PRIMÆR), persona B, …>
- Key points (24): <…>
- Tone / voice anchor: <reference to assets/voice-samples + any edition-specific note>
- Out of scope for this edition: <…>
## §4 — Immutable rules + fact-check log
> Rules locked for this edition (do not relitigate mid-pipeline):
- <immutable rule 1 — e.g. "no vendor names in the hook">
- <immutable rule 2>
> Fact-check log (Step 5 — guilty-until-disproven; 🔴 must be empty before lock):
| Claim | Risk | Source / verification | Status |
|-------|------|-----------------------|--------|
| <claim> | 🔴/🟡/🟢 | <primary source> | open / resolved |
## §5 — Persona calibration + verdicts
- Pre-lock resonance sweep (Step 6): primær <name> → <JA / NEI + one-line reason>
- Method/persona calibration notes: <any axis tuning, secondary-NO signals>
- Post-lock conversion sweep (Step 9): primær <name> mode konverter → <JA / NEI>
## §6 — Next session
- Next step: Step <N> — <name> (per the Step 0 resumption table)
- Precise pointer: <e.g. "draft resumes at section 3" or "render POST.html, then hook gate">
- Open questions for the operator: <…, or "none">

View file

@ -2,10 +2,10 @@
"_doc": {
"purpose": "Schema for edition-state.json — deterministic resumption state for a newsletter edition in production. Holds the current phase + per-article status so /linkedin:newsletter (Step 0) can resume exactly where a prior session stopped.",
"decision": "G — production state lives in the serie-mappe (e.g. /Users/ktg/repos/maskinrommet/serier/<slug>/linkedin/edition-state.json), NOT in the plugin. This file is the schema-defining TEMPLATE only; copy + fill it in the serie-mappe when producing an edition.",
"complements": "edition-config.json (static: calendar, freshness, captions) and the human-readable edition-HANDOVER.md (narrative state, fasit §5.2). edition-state.json is the lightweight machine-readable companion for deterministic resumption.",
"complements": "edition-config.json (static: calendar, freshness, captions) and <serie>/STATE.md (human-readable narrative state, overwritten each phase per the ONE-system continuity rule — there is no edition-HANDOVER.md). edition-state.json is the machine-readable companion: deterministic resumption + the durable fact-check log, immutable rules, and persona verdicts that the old HANDOVER §4/§5 used to carry.",
"lifecycle": "/linkedin:newsletter reads this in Step 0 and rewrites it at every phase transition. Article keys mirror edition-config.json (zero-padded strings: \"01\", \"02\", ..., or \"samle\").",
"phases": [
"load-context — read state/HANDOVER, voice profile, persona library, series brief (Step 0)",
"load-context — read <serie>/STATE.md, voice profile, persona library, series brief (Step 0)",
"brief-calibration — angle, voice, audience personas, key points, leader-takeaway (Step 1)",
"research — parallel scoped mandates → verified notes, triangulation (Step 2)",
"skeleton-pitch — five-line skeleton (premise/problem/recommendation/payoff/forward) + section pitches, operator gate + persona-skjelett-sweep BEFORE prose (Step 2.5)",
@ -34,8 +34,10 @@
"title": "<Article 1 title>",
"phase": "load-context",
"status": "pending",
"immutableRules": null,
"factcheckLog": null,
"personaSweep": {
"skeleton": null,
"resonance": null,
"conversion": null
},

View file

@ -32,6 +32,27 @@ trim, or extend them per series.
BEFORE lock — «does the point land for this reader?») and conversion mode
(Step 9, after lock — binary «would YOU click?» on the hook only).
### The click-gate is blocking (bar = primær ekte JA)
The persona sweep is not advisory — it returns a **blocking verdict**
(PASS / REWORK / BLOCK), and the bar is the **primær reader's genuine, unqualified
JA**. The three Seres seed personas are the canonical set: **A = IT-divisjonsdirektør**
(sekundær), **B = KI-seksjonsleder** (sekundær), **C = Linjeleder** (PRIMÆR — trumfer).
- **Bar = C ekte JA.** A clean, unqualified yes from the primær. **«JA med store
forbehold» = NEI.**
- ⛔ **Hard fail (= omskriv, ikke annotér):** the verdict is BLOCK, regardless of
the other axes, when the primær —
- «mistet meg» (disengaged before the takeaway), or
- does not own the action (the takeaway is someone else's job), or
- hits a **sjargong-mur** (a wall of technical vocabulary their `sjargong`
rejects), or
- hits a **modell-/navne-katalog** (product/model/benchmark names listed for
completeness).
- These are **rewrite triggers**, not annotations the editor can wave through. A
*sekundær* NO from a role/expertise ceiling stays a SIGNAL the gate works —
never distort the text to chase it.
Each persona documents five fields. Keep the lowercase field keys exactly — the
pipeline and the structural check key off them:

View file

@ -92,6 +92,16 @@ cp config/user-profile.template.md config/user-profile.local.md
- [What you never do]
- [Another thing to avoid]
**Universal anti-patterns (keep these — they hold for every author):**
- **Modell-/navne-katalog.** Do not reel off product names, model names, or
benchmarks for completeness. Pick ONE concrete, verifiable (preferably local)
case over a list — a name-dump is a jargon wall to a non-technical reader.
- **Fullstendighet over leser-handling.** Serve what the primary reader can DO
from their chair, not everything the author knows. Completeness is not a virtue.
- **Selvrefererende overhead-åpning.** No meta-commentary about what the text will
or will not do, no warm-ups. Start on the reader's problem.
- **«ikke bare X, men Y», reflex rule-of-three, tacked-on summaries, hedging.**
**Language:** [English / Norwegian / Other]
---