feat(linkedin-studio): v3.1.0 — Endring 9 adversarial review-pakke + per-artefakt personas
Cold, adversarial review package for the long-form pipeline + configurable per-edition personas. Motivated by Del 4 (Security Champions pivot): the in-session editor + persona sweep shared the drafting session's framing-bias, so the shipped version was never independently re-reviewed. Headless package (9a/9b): - New Step 6.5 (headless-review) in /linkedin:newsletter, after the persona sweep, before lock — the independence layer the in-session gates can't be. - New standalone /linkedin:headless-review command (run in a fresh session for maximum isolation; reconstructs frozen draft + contract + personas from disk). - 3 new Opus archetypes, each with a cardinal context-isolation block that refuses drafting-session framing as "context pollution": - content-reviewer (argument integrity C1–C5, ≤8 flags) - language-reviewer (Norwegian language L1–L5, ≤10 flags) - fact-reviewer (cold re-verification F1–F4, risk-sort + pivot-risk, WebSearch) - Deliberate redundancy with fact-checker / editorial-reviewer documented so the pairs are never de-duplicated. Pivot-reopen (9c): - New /linkedin:pivot command: logs articles.NN.pivots[], resets currentPhase, un-locks, marks gates to re-run. - Pivot-detection gate in Step 8 lock precondition (>20% word-count change or >2 new sections re-opens cleared gates). Del 4 v8→v11 worked example. Per-artifact personas (new requirement): - articles.NN.personas with resolution order (edition-state → series file → plugin library → interactive). One or more readers configurable per edition. Schema/docs: - edition-state.template.json: additive personas[], pivots[], headlessReview, headless-review phase (16 phases); personaSweep.resonance.wordCount baseline. - 3 fasit fixtures + 3 structural lint tests (Del 4 worked cases). - Counts: 24→26 commands, 16→19 agents, 15→16 newsletter phases. - README + CLAUDE.md (plugin + root) + CHANGELOG synced. Verification: 35 agent-fixture + 59 hook + 20 render tests green. Backward- compatible (additive state); reload required before the 3 new agents resolve. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
e162cdce38
commit
e69ea1f4c9
20 changed files with 2520 additions and 59 deletions
248
plugins/linkedin-studio/commands/headless-review.md
Normal file
248
plugins/linkedin-studio/commands/headless-review.md
Normal file
|
|
@ -0,0 +1,248 @@
|
|||
---
|
||||
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).** For the publish-ready gate, 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). This is the recommended
|
||||
path and the one KTG asked for.
|
||||
2. **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)
|
||||
```
|
||||
|
||||
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).
|
||||
|
||||
## 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)
|
||||
- Persisted: edition-state.json articles.NN.headlessReview (or: standalone, not persisted)
|
||||
Operator gates the fold-in. For maximum independence, run this command in a FRESH session.
|
||||
```
|
||||
|
||||
## 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 **strongest** isolation is the operator running
|
||||
`/linkedin:headless-review` in a fresh session and pasting the consolidated
|
||||
report back. 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
|
||||
Loading…
Add table
Add a link
Reference in a new issue