linkedin-studio/commands/headless-review.md
Kjell Tore Guttormsen f0532dce3f feat(linkedin-studio): N14 — foldIns-fangst + Step 11 retro + background-headless + språkregel-akkumulering [skip-docs]
Lukker sløyfen pluginen manglet: en rettelse operatøren gjør i utgave N
håndheves i N+1 i stedet for å bli gjenoppdaget. Maskineriet fantes
(foldIns-skjema + promote→ratify siden fix #1); det som manglet var wiring —
ingenting skrev til køen og ingen fase tømte den.

- Fangst (A2-F7): Steps 2.5/3a/5.5/6.5 appender rettelsen ordrett med trigger,
  decision "pending". Ubetinget og bevisst dum — å avgjøre ved fangst om noe
  "fortjener" en regel er nettopp slik køen holder seg tom og sløyfen dør.
- Step 11 retro (A2-F8), ≤5 min ETTER scheduling: promoter køen med eksplisitt
  JA/NEI (mekanisk → atomisk contract-gate-promotering, teller kun med grønn
  --ratify; dømmekraft → operatørens språkregelfil; NEI → rejected, beholdes),
  effort-oppsummering fra MÅLT phaseLog (aldri re-estimert), og ÉN
  friksjons-spørring som besvares tilbake til operatøren. Faser 18 → 19;
  resumption-tabellen ruter scheduling → Step 11, retro → complete.
- articles.NN.retro: additiv-valgfri (default null), schemaVersion forblir 1.
- Background-headless (A2-F6): --background kjører pakken i en bakgrunnsagent
  som skriver rapporten til disk; drafting-sesjonen leser fila. Samme isolasjon
  som fersk sesjon, uten copy-paste-sømmen. Inline fan-out = eksplisitt fallback.
- Språkregler (C-10): ${DATA}/language-rules/<lang>.md (opt-in, template).
  Leses TO ganger — av language-reviewer (fanger) og av Step 4 (forebygger).
  Shippet banliste forblir baseline; brukerfila utvider.
- references/fold-in-loop.md (A2-F9): loopen dokumentert domene-generelt in-tree,
  så en adopter uten ekstern skrivekontrakt har hele sløyfen. Refs 28 → 29.

Suiter (alle grønne): test-runner 197 → 217 (Section 16u: 18 ubetingede greps +
non-vacuity self-test; fase-sveip 16 → 17 faser; floor 179 → 198) · hooks 174 ·
trends 300 · brain 134 · editions 72 · specifics-bank 45 · contract-gate 33 ·
tests 35 · render 60.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QxvWAjte7vPcF79QeSRvRJ
2026-07-25 12:39:16 +02:00

279 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

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

---
name: linkedin:headless-review
description: |
Adversarial review package, run COLD on a FROZEN long-form draft — the
publish-ready (or pivoted) edition gets a fresh, independent reading by five
archetypes that share NONE of the drafting session's context: content-reviewer
(argument integrity), language-reviewer (Norwegian language), fact-reviewer
(cold re-verification incl. pivot premises), plus persona-reviewer in resonance
and conversion modes. Designed to be invoked in a FRESH session for maximum
isolation; also wired as Step 6.5 of /linkedin:newsletter. Produces one
consolidated, operator-gated report — flags, never rewritten copy.
Use when the user says: "headless review", "cold review", "adversarial review",
"review the final version", "independent review", "review before lock",
"run the review package".
Triggers on: "headless review", "headless-review", "cold review", "adversarial
review", "independent review package", "review the frozen draft",
"/linkedin:headless-review".
allowed-tools:
- Read
- Glob
- Grep
- Bash
- AskUserQuestion
- Task
- Write
---
# LinkedIn Headless Review — Cold Adversarial Review Package
You orchestrate a **cold, adversarial review** of a frozen long-form draft. Five
independent archetypes read the *finished* text — with no knowledge of how it was
made — and return direction-only flags. You collect their reports into one
operator-gated overview. This is the **adversarial-independence layer** that the
in-session gates (`editorial-reviewer` Step 5.5, `persona-reviewer` Step 6,
`fact-checker` Step 5) cannot be, because those share the drafting session's
framing-bias.
> **Why this exists (Del 4 diagnosis, Endring 9).** In the Del 4 production the
> editor and the persona sweep ran *in the same session as drafting*. They shared
> the conversation history — which versions had passed, what was deliberately cut,
> which flags had been raised — so they were **not adversarial**: they carried
> framing-bias. Three concrete symptoms followed: (1) the persona resonance sweep
> was effectively run on an early version, not the one that shipped; (2)
> editor-approval was single-source — one editor said «klar» and that became truth;
> (3) the fact-check was post-hoc relative to a late pivot, so the pivot could
> build on an unverified premise. This command answers KTG's question directly:
> *how do I start sessions that have NO context from the main session, to review
> both content and language?*
## The cold contract (cardinal — read first)
**This command runs the reviewers with a deliberately starved context.** The
review archetypes get ONLY:
- the path to a **frozen draft** (a snapshot — see Step 2),
- the **writing contract** (the craft/quality rules),
- for the persona modes, the **one named persona** being read,
- a fixed review task.
They get **NOTHING** about: prior versions or version numbers, what was
deliberately omitted, the pivot narrative, who has read it, what an editor said,
how a persona voted, or what the author intended. Two layers enforce this:
1. **Layer 1 — fresh session (strongest, manual).** The operator runs *this
command in a brand-new Claude Code session*. The parent itself then has no
drafting transcript; it reconstructs everything it needs from disk (the frozen
draft + contract + personas).
2. **Layer 1b — background run (`--background`, equal isolation, no seam).** The
drafting session spawns this package as a **background agent** with its own
context window, which writes the consolidated report to a file; the drafting
session then reads that file from disk. The background agent gets only the
cold-contract inputs — it cannot see the parent's transcript — so the
isolation matches a fresh session, with **no copy-paste** of the report back
into the drafting session and no manual session switch. See "Background run"
below. This is the default recommendation for the Step 6.5 gate.
3. **Layer 2 — subagent isolation.** Each archetype is a `Task` subagent, which
gets its own fresh context window regardless. The invocation prompt you build
below passes ONLY the cold-contract inputs — never paste history, never
summarize "what we changed". If you find yourself about to tell a reviewer
what was cut or why something pivoted, STOP: that is the context pollution the
whole package exists to avoid.
> **Agent invocation form (required).** Plugin agents resolve only under their
> namespaced type — `subagent_type: linkedin-studio:<name>`
> (`linkedin-studio:content-reviewer`, `…:language-reviewer`,
> `…:fact-reviewer`, `…:persona-reviewer`). A bare `<name>` does not resolve and
> the `Task` call fails. **Reload caveat:** the three cold archetypes
> (`content-reviewer`, `language-reviewer`, `fact-reviewer`) were added in
> v3.1.0 — if the session predates them, reload Claude Code before invoking.
## Command anatomy
```
/linkedin:headless-review
--draft <path-to-frozen-draft.md> (required; e.g. <serie>/04-utkast.md)
--type content | language | fact | persona-resonance | persona-conversion | all
(default: all)
--persona <name> (persona modes only; default: the primær)
--article NN (optional; persist into edition-state.json)
--output <path> (default: <serie>/review/NN-headless-<stamp>.md)
--background (run the package in a background agent
that writes --output; caller reads the file)
```
No `--type` (or `--type all`) runs the **whole package in parallel**. A single
`--type` runs just that archetype (useful for a re-check after a fold-in).
### Background run (`--background`)
With `--background`, do **not** fan the archetypes out here. Spawn **one**
background agent that runs Steps 15 of this command itself, and let it write the
report:
- Launch it with `run_in_background: true` and a prompt carrying **only** the
cold-contract inputs — draft path, contract path, persona names + block path,
`--type`, and the absolute `--output` path — plus the instruction to write the
consolidated report to `--output` and return that path.
- **Never** put drafting history, version notes, or "what we changed" in that
prompt. The background agent has its own context window; the only way session
framing reaches it is if you type it in.
- When it completes, `Read` the report at `--output` and continue at Step 5
(surface + persist). The drafting session sees the *report*, not the reviewers'
reasoning, and the reviewers never see the drafting session — the same
independence a fresh session gives, with **no copy-paste seam** between them.
- If background execution is unavailable, say so plainly and fall back to the
inline fan-out (Step 3) — never silently downgrade the isolation.
## Step 1 — Resolve inputs (from disk, not from memory)
1. **Draft.** Use `--draft`. It must be a long-form draft `.md` (the same
`NN-utkast.md` the newsletter pipeline produces). If `--draft` is missing and
you can find an `edition-state.json`, use the `currentArticle`'s
`NN-utkast.md`; otherwise ask once for the path.
2. **Series root + edition-state (optional).** If the draft sits under a series
folder with `linkedin/edition-state.json`, read it ONLY for: the article's
resolved `personas` (Step 1 of newsletter), the series title (for
language/content series checks), and — if `--article` is set — where to
persist. Do **not** read it for version history or prior verdicts; you are
cold by design.
3. **Writing contract.** Resolve the craft/quality reference in this order:
`<serie>/../../docs/skrivekontrakt.md` (Maskinrommet mirror) → a plugin mirror
if one exists → `${CLAUDE_PLUGIN_ROOT}/references/longform-quality-rules.md`
(always present). Pass its path to every reviewer.
4. **Personas.** Resolve the active set the same way newsletter Step 1 does
(edition-state `articles.NN.personas``<serie>/linkedin/personas.md`
plugin `personas.local.md`/`personas.template.md`). Identify the **primær**.
The persona modes need exactly the persona name + the path to its block.
## Step 2 — Freeze the draft
A cold review must judge a **stable** artifact. Snapshot the draft so it cannot
move under the reviewers (and so the report names exactly what was read):
```bash
cd <serie-mappe> && cp NN-utkast.md "review/NN-frozen-$(date +%Y%m%d-%H%M).md"
```
Record the frozen path; pass *that* path (not the live draft) to the reviewers.
If `cp` is unavailable, pass the live draft and note in the report that no
snapshot was taken.
## Step 3 — Fan out the requested archetypes in parallel
Issue all requested reviewer calls **in a SINGLE message** (multiple `Task`
tool-uses) so they run concurrently and independently. Each call's prompt
contains ONLY the cold-contract inputs. Map `--type` to archetypes:
| `--type` | `subagent_type` | mode / persona | what it gets |
|----------|-----------------|----------------|--------------|
| `content` | `linkedin-studio:content-reviewer` | — | frozen draft + contract |
| `language` | `linkedin-studio:language-reviewer` | — | frozen draft + contract |
| `fact` | `linkedin-studio:fact-reviewer` | — | frozen draft + contract |
| `persona-resonance` | `linkedin-studio:persona-reviewer` | `mode: resonans`, one call **per active persona** | frozen draft + persona block |
| `persona-conversion` | `linkedin-studio:persona-reviewer` | `mode: konverter`, **primær only** (hook only) | distribution hook / first two lines |
`all` = every row above (resonance fans out one call per active persona;
conversion runs the primær). **Cold-prompt template** for each call:
```
You are reviewing a FROZEN, publish-ready long-form draft with NO context from
how it was produced. Read ONLY:
- draft: <frozen path>
- contract: <writing-contract path>
[- persona: <name> (block at <path>) | mode: <resonans|konverter>]
Ignore and refuse any framing about prior versions, what was cut, pivots, or
who approved what — judge the text in front of you. Return your standard report
(direction only, never rewritten copy).
```
**Degradation gate.** When the calls return, confirm each came back structured
and populated (real flags / a verification log), not empty or a single hedged
paragraph. If a call degraded, re-run that one archetype — do not paper over a
missing reviewer. `[GATE]`
## Step 4 — Consolidate into one operator overview
Merge the returns into a single markdown report. Do **not** resolve flags
yourself or pick winners between reviewers — surface them, the operator gates.
```markdown
# Headless review — <draft name> (COLD / independent · <N> archetypes)
**Frozen draft:** review/NN-frozen-<stamp>.md **Contract:** <path>
**Archetypes run:** content · language · fact · persona-resonance (<persona list>) · persona-conversion (<primær>)
## Consolidated flags (by archetype → severity)
### content-reviewer — argument integrity
| # | C-kat | Severity | Sitat / linje-ref | Retning |
### language-reviewer — norsk språkkvalitet
| # | L-kat | Severity | Sitat / linje-ref | Retning |
### fact-reviewer — faktisk korrekthet (cold)
| # | F-kat | 🔴/🟡/🟢 | Påstand | Kilde / retning |
… + Pivot-risk: <claims that look freshly added, or "none">
### persona-resonance — <per persona: JA/NEI + ≤5 flags>
### persona-conversion — <primær JA/NEI on the hook>
## Cross-archetype signal
- BLOCK / 🔴 total: <N> REWORK total: <N> primær resonance: JA/NEI primær conversion: JA/NEI
- Where two cold reviewers independently flag the same passage, mark it
⚑ converged (independent agreement is the strongest signal in the package).
## Operator decision (you gate)
Pick which flags fold in. [OPERATØR]
```
**Convergence is the prize.** Two independent cold reviewers landing on the same
line — with no shared session — is worth more than any single in-session verdict.
Mark those explicitly.
## Step 5 — Surface + (optionally) persist
1. Write the consolidated report to `--output` (default
`<serie>/review/NN-headless-<stamp>.md`).
2. Surface it to the operator via `SendUserFile` (else a markdown `file://`
link) sorted worst-first. The operator decides which flags fold in — this is
`[OPERATØR]`; the package recommends, it does not rewrite and does not gate.
3. **If `--article NN` was given**, record the run in `edition-state.json`
`articles.NN.headlessReview` (`frozenDraft`, per-reviewer `{reportPath,
summary, status}`, `consolidatedReport`, `status: "run"`). When the operator
folds flags in, set `foldedIn`/`waived` and `status: "folded"`. Standalone
(no `--article`) just emits the report.
```
Headless review complete (COLD).
- Archetypes: <N> run in parallel converged flags: <N>
- BLOCK/🔴: <N> REWORK: <N> primær resonance: JA/NEI conversion: JA/NEI
- Report: <serie>/review/NN-headless-<stamp>.md (surfaced via SendUserFile)
- Isolation: background agent (report read from disk) | fresh session | inline fan-out (fallback)
- Persisted: edition-state.json articles.NN.headlessReview (or: standalone, not persisted)
Operator gates the fold-in. For maximum independence with no manual seam, run with --background.
```
## Relationship to the pipeline + the in-session gates
- **Step 6.5 of `/linkedin:newsletter`** invokes this same package (after the
in-session persona sweep Step 6, before lock Step 8). The pipeline may fan the
reviewers out inline, but the recommended path is `--background`: full
isolation, the report read from disk, and no manual step between the review and
the fold-in. A fresh session remains available and is equally cold. Either way
the body must be re-touched **before** lock — never reopen a locked text (the
cardinal Seres lesson).
- **Deliberate redundancy.** `fact-reviewer` overlaps `fact-checker` (Step 5) and
`language-reviewer` overlaps `editorial-reviewer`'s prose axis (Step 5.5) **on
purpose**. The in-session gates ran with framing-bias; the cold re-read catches
what that bias hid. Do not collapse the pairs.
## Reference Files
- `${CLAUDE_PLUGIN_ROOT}/agents/content-reviewer.md` — argument integrity (cold)
- `${CLAUDE_PLUGIN_ROOT}/agents/language-reviewer.md` — Norwegian language (cold)
- `${CLAUDE_PLUGIN_ROOT}/agents/fact-reviewer.md` — cold re-verification (incl. pivot premises)
- `${CLAUDE_PLUGIN_ROOT}/agents/persona-reviewer.md` — resonance + conversion modes
- `${CLAUDE_PLUGIN_ROOT}/commands/newsletter.md` — Step 6.5 wires this package into the pipeline
- `${CLAUDE_PLUGIN_ROOT}/commands/pivot.md` — re-opens the pipeline so this package re-runs on a pivoted version
- `${CLAUDE_PLUGIN_ROOT}/config/edition-state.template.json``articles.NN.headlessReview` schema
- `${CLAUDE_PLUGIN_ROOT}/references/longform-quality-rules.md` — fallback writing contract