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
|
||||
|
|
@ -71,13 +71,15 @@ 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 (15 phases)
|
||||
## Pipeline overview (16 phases)
|
||||
|
||||
The phase order is fixed. Two gates run **BEFORE prose** (skeleton + spine
|
||||
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).
|
||||
prose), an **editorial craft gate** runs before the persona sweep, the persona
|
||||
resonance sweep runs **BEFORE lock**, and a **cold adversarial review package**
|
||||
(Step 6.5) runs after the in-session persona sweep and 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; v3.1 / Endring 9 on adversarial independence + framing-bias).
|
||||
|
||||
| Step | Phase | What | Tools |
|
||||
|------|-------|------|-------|
|
||||
|
|
@ -91,22 +93,25 @@ important corrections from the Seres process (plan §0.4, principle 5; v2.1 brie
|
|||
| 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) |
|
||||
| 6.5 | **Headless adversarial review — BEFORE lock** | COLD review package on a frozen draft, no drafting-session context: content-reviewer (argument) + language-reviewer (Norwegian) + fact-reviewer (cold re-verification incl. pivot premises) + persona-reviewer resonance/conversion. Consolidated, operator-gated via `SendUserFile`. The independence layer the in-session gates can't be. | **`content-reviewer` + `language-reviewer` + `fact-reviewer` + `persona-reviewer`** (parallel) + `SendUserFile` |
|
||||
| 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` |
|
||||
| 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 15 phases (Steps 0–2.5, 3a, 3b, 4, 5, 5.5, 6, 7, 7.5,
|
||||
> 8–10) are implemented below. This command takes an edition end-to-end: load →
|
||||
> calibration → verified research → **skeleton + section pitch (operator +
|
||||
> **Build status:** all 16 phases (Steps 0–2.5, 3a, 3b, 4, 5, 5.5, 6, 6.5, 7,
|
||||
> 7.5, 8–10) 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
|
||||
> `<serie>/STATE.md` (narrative) and stopping cleanly between sessions.
|
||||
> pre-lock persona sweep → **headless adversarial review (cold review package,
|
||||
> operator-gated BEFORE lock)** → 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 `<serie>/STATE.md` (narrative) and stopping cleanly between
|
||||
> sessions.
|
||||
|
||||
> **Why two gates BEFORE prose (v2.1).** Spine errors are the dearest failure
|
||||
> mode in long-form: catching one at the skeleton stage costs 5–15 min, at the
|
||||
|
|
@ -157,9 +162,18 @@ the edition left off before doing anything.
|
|||
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.
|
||||
5. **Resolve the active personas (per-artifact).** Personas are configured **per
|
||||
edition**, not from one fixed global file. Resolve the set for
|
||||
`articles.<currentArticle>` in this order (Step 1 finalizes + persists it):
|
||||
1. **`edition-state.json` → `articles.NN.personas`** — if already populated
|
||||
(a resumed edition), use it as-is.
|
||||
2. **`<serie>/linkedin/personas.md`** — a per-series persona file, if present.
|
||||
3. **`${CLAUDE_PLUGIN_ROOT}/config/personas.local.md`** (else
|
||||
`personas.template.md`) — the plugin library; select a subset.
|
||||
4. **None / insufficient** — Step 1 will **define personas interactively**.
|
||||
Exactly one persona is the **primær**. The resolved set feeds BOTH the Step 6
|
||||
resonance sweep AND the Step 6.5 headless package; see
|
||||
`config/personas.template.md` → "Per-artifact personas".
|
||||
6. **Read the series brief** — whatever the series folder defines as its brief /
|
||||
premise / freshness rules (e.g. `<serie>/brief.md`, or the resolved brief
|
||||
recorded in `edition-state.json`). This anchors angle and scope.
|
||||
|
|
@ -187,7 +201,8 @@ Look up `edition-state.json` → `articles.<currentArticle>` (and the top-level
|
|||
| `consistency-quality` | Step 5 — Fact-check sweep |
|
||||
| `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 |
|
||||
| `persona-sweep-prelock` | Step 6.5 — Headless adversarial review *(v3.1 — cold review package, BEFORE lock)* |
|
||||
| `headless-review` | 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 |
|
||||
| `lock-delivery` | Step 9 — Hook / conversion gate |
|
||||
|
|
@ -216,8 +231,8 @@ the gated skeleton — typically minutes, not session-length work).
|
|||
|
||||
> **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.
|
||||
> at Step 6.5 (headless adversarial review). No operator question, no re-doing the
|
||||
> persona sweep. The same holds at every row.
|
||||
|
||||
Then display a short status:
|
||||
|
||||
|
|
@ -242,11 +257,20 @@ 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".
|
||||
- **Audience personas (per-artifact)** — finalize the **one or more personas for
|
||||
THIS edition** from the Step 0 resolution, and **mark exactly one as primær**.
|
||||
If the resolution found a set (edition-state / series file / plugin library),
|
||||
confirm or trim it; if it found none — or the operator wants a reader the
|
||||
library does not cover — **define personas interactively** here (name + the
|
||||
five fields: rolle, avkobler, overbeviser, ekspertise, sjargong). The primær
|
||||
reader weighs highest in the Step 6 sweep AND the Step 6.5 headless package; 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).
|
||||
**Persist** the resolved set to `edition-state.json` →
|
||||
`articles.NN.personas` (each entry: name, tier, the five fields, source) at the
|
||||
Step 2 checkpoint — it is then stable across sessions and is the single source
|
||||
every later sweep reads. See `config/personas.template.md` →
|
||||
"Per-artifact personas".
|
||||
- **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
|
||||
|
|
@ -316,9 +340,11 @@ Edition brief
|
|||
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
|
||||
5. **Persist + checkpoint state.** Write the resolved brief (Step 1), the
|
||||
resolved **per-article personas** (`articles.NN.personas` — the set + primær
|
||||
confirmed/defined in 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: skeleton + section pitch (BEFORE prose)" next-step
|
||||
line to `<serie>/STATE.md` (overwrite). If this is a fresh edition, initialize
|
||||
`edition-state.json` from the template schema first. Stop cleanly here if
|
||||
|
|
@ -951,9 +977,13 @@ reopening locked texts — the biggest single process error of the series (plan
|
|||
|
||||
5. **Persist + checkpoint state.** Record the final per-persona verdicts and the
|
||||
resolved flags in `edition-state.json` → `articles.NN.personaSweep.resonance`
|
||||
(where the old HANDOVER §5 calibration now lives), set
|
||||
`currentPhase: "persona-sweep-prelock"`, and write a "persona sweep
|
||||
PASS (primær JA) → next: lock/delivery" line to `<serie>/STATE.md` (overwrite).
|
||||
(where the old HANDOVER §5 calibration now lives). **Also record the cleared
|
||||
draft's word count** as `articles.NN.personaSweep.resonance.wordCount`
|
||||
(`wc -w <serie>/NN-utkast.md`) — this is the **baseline** the pivot-detection
|
||||
heuristic (Step 8 / `/linkedin:pivot`) compares against to catch a late pivot.
|
||||
Set `currentPhase: "persona-sweep-prelock"`, and write a "persona sweep
|
||||
PASS (primær JA) → next: headless adversarial review (Step 6.5, BEFORE lock)"
|
||||
line to `<serie>/STATE.md` (overwrite).
|
||||
|
||||
```
|
||||
Persona sweep complete (BEFORE lock).
|
||||
|
|
@ -961,8 +991,115 @@ Persona sweep complete (BEFORE lock).
|
|||
- 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>
|
||||
- Cleared word count recorded: <N> (pivot-detection baseline)
|
||||
Gate: [PASS — primær JA, ready to lock] (else REWORK/BLOCK)
|
||||
Next: Step 7 — Annotation (optional), then Step 8 — LOCK → delivery.
|
||||
Next: Step 6.5 — Headless adversarial review (cold review package, BEFORE lock).
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Step 6.5: Headless adversarial review — cold review package, BEFORE lock
|
||||
|
||||
The persona-passed draft now faces a **cold, adversarial review package**: five
|
||||
independent archetypes read the *finished* text with **none of this session's
|
||||
context** — no version history, no "deliberately omitted" list, no pivot
|
||||
narrative, no record of who approved what. They are the independence layer the
|
||||
in-session gates (`fact-checker` Step 5, `editorial-reviewer` Step 5.5,
|
||||
`persona-reviewer` Step 6) structurally cannot be, because those share the
|
||||
drafting session's framing-bias.
|
||||
|
||||
> **Why this step exists (Del 4 diagnosis, Endring 9).** In Del 4 the editor and
|
||||
> the persona sweep ran in the same session as drafting. They shared the
|
||||
> conversation history, so they carried framing-bias and were not adversarial:
|
||||
> the resonance sweep effectively judged an early version, editor-approval was
|
||||
> single-source, and the fact-check was post-hoc relative to a late pivot. This
|
||||
> step answers KTG's question — *how do I start sessions with no context from the
|
||||
> main session, to review both content and language?* — by running the review on
|
||||
> a **frozen** draft through agents that refuse session framing.
|
||||
|
||||
> **Order assertion (enforced).** Step 6.5 runs AFTER the in-session persona
|
||||
> sweep (Step 6) and BEFORE lock (Step 8), on a **frozen snapshot** of the
|
||||
> publish-ready draft. Any flag the operator folds in is re-touched **before**
|
||||
> lock — never reopen a locked text (the cardinal Seres lesson). If a pivot
|
||||
> changes the draft after this gate, `/linkedin:pivot` re-opens the pipeline and
|
||||
> this package re-runs on the pivoted version. `[GATE]`
|
||||
|
||||
**Relationship to the in-session gates (deliberate redundancy).** `fact-reviewer`
|
||||
overlaps `fact-checker` and `language-reviewer` overlaps `editorial-reviewer`'s
|
||||
prose axis **on purpose** — the cold re-read catches what the framing-biased
|
||||
in-session pass hid. `content-reviewer` is genuinely new (argument integrity,
|
||||
which no in-session gate measures). Do NOT collapse the pairs.
|
||||
|
||||
**Procedure** (this is the same package the standalone `/linkedin:headless-review`
|
||||
command runs — see `commands/headless-review.md` for the full cold contract):
|
||||
|
||||
1. **Freeze the draft.** Snapshot the persona-passed `NN-utkast.md` so the
|
||||
reviewers judge a stable artifact and 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 every reviewer.
|
||||
|
||||
2. **Resolve the cold inputs.** The writing contract (`<serie>/../../docs/skrivekontrakt.md`
|
||||
→ plugin mirror → `references/longform-quality-rules.md`) and the active
|
||||
personas (`articles.NN.personas`, primær identified). Nothing else.
|
||||
|
||||
3. **Fan out the five archetypes in parallel** — issue them in a SINGLE message
|
||||
(multiple `Task` tool-uses) from THIS command layer in the foreground
|
||||
(principle 4), `subagent_type` namespaced:
|
||||
- `linkedin-studio:content-reviewer` — argument integrity (C1–C5)
|
||||
- `linkedin-studio:language-reviewer` — Norwegian language (L1–L5)
|
||||
- `linkedin-studio:fact-reviewer` — cold re-verification (F1–F4, 🔴/🟡/🟢, incl. pivot premises)
|
||||
- `linkedin-studio:persona-reviewer` `mode: resonans` — **one call per active persona**
|
||||
- `linkedin-studio:persona-reviewer` `mode: konverter` — **primær only** (hook)
|
||||
|
||||
Each call's prompt carries ONLY the cold-contract inputs (frozen draft path,
|
||||
contract path, persona for the persona modes) + the instruction to ignore any
|
||||
framing about prior versions / cuts / pivots. **Never** paste history or
|
||||
summarize "what we changed" into a reviewer prompt — that is the context
|
||||
pollution the package exists to eliminate.
|
||||
|
||||
> **Maximum-independence path.** The strongest isolation is the operator
|
||||
> running `/linkedin:headless-review --draft <frozen> --article NN` in a
|
||||
> **fresh session** (the parent then has no drafting transcript at all) and
|
||||
> pasting the consolidated report back. The inline fan-out here is the
|
||||
> single-session path; both use the same agents.
|
||||
|
||||
4. **Degradation gate.** Confirm each call returned structured, populated output
|
||||
(real flags / a verification log), not empty or a hedged paragraph. Re-run any
|
||||
degraded archetype — do not proceed with a missing reviewer. `[GATE]`
|
||||
|
||||
5. **Consolidate + surface (`SendUserFile`).** Merge the returns into one report
|
||||
at `<serie>/review/NN-headless-<stamp>.md`, grouped by archetype → severity,
|
||||
with a cross-archetype signal line. **Mark ⚑ converged** any passage two
|
||||
independent cold reviewers flag — independent agreement with no shared session
|
||||
is the package's strongest signal. `SendUserFile` it (else a `file://` link)
|
||||
so KTG decides which flags fold in. You do not resolve flags or pick winners;
|
||||
the operator gates. `[OPERATØR]`
|
||||
|
||||
6. **Fold in by tightening, → v(n+1).** Fold the flags KTG approved into
|
||||
`NN-utkast.md` **by tightening** (rule 6 — close the gap, hold the length flat).
|
||||
The editor (this session) holds the pen; never paste a reviewer's direction as
|
||||
copy. If the fold-in was substantive, re-run the affected archetype on v(n+1).
|
||||
All of this happens **before** lock, so the body is never reopened post-lock.
|
||||
|
||||
7. **Persist + checkpoint state.** Record the run in `edition-state.json` →
|
||||
`articles.NN.headlessReview` (`frozenDraft`, per-reviewer `{reportPath,
|
||||
summary, status}`, `consolidatedReport`, `foldedIn`/`waived`, `status:
|
||||
"folded"`), set `currentPhase: "headless-review"`, and write a "headless review
|
||||
complete (cold, converged flags folded) → next: annotation/lock" line to
|
||||
`<serie>/STATE.md` (overwrite).
|
||||
|
||||
```
|
||||
Headless adversarial review (cold, BEFORE lock) — complete.
|
||||
- Frozen draft: <serie>/review/NN-frozen-<stamp>.md
|
||||
- Archetypes: content · language · fact · persona-resonance (<N> personas) · persona-conversion (primær)
|
||||
- Converged flags (independent agreement): <N>
|
||||
- BLOCK/🔴: <N> → folded/waived REWORK: <N> primær resonance: JA conversion: JA
|
||||
- Surfaced to operator: <serie>/review/NN-headless-<stamp>.md (via SendUserFile) [OPERATØR]
|
||||
- Folded in (by tightening, pre-lock): <N> Waived: <N>
|
||||
Gate: [PASS — operator approved, body re-touched pre-lock]
|
||||
Next: Step 7 — Annotation (optional), then Step 7.5 — Visual assets, then Step 8 — LOCK.
|
||||
```
|
||||
|
||||
---
|
||||
|
|
@ -1175,19 +1312,38 @@ produces the editor's single delivery artifact — `POST.html`, the
|
|||
(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]`
|
||||
> sweep (Step 6) AND the headless adversarial review (Step 6.5), and BEFORE the
|
||||
> hook/conversion gate (Step 9). Reversing lock and the pre-lock sweeps
|
||||
> 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 🔴, `personaSweep.resonance` recorded a primær JA,
|
||||
and `visualAssets` is gated — for `standard` format the approved
|
||||
`linkedin/NN/cover.png` exists (Step 7.5); for `carousel` format the approved
|
||||
`carousel.pdf`/`carousel.html` exists. If any is missing, STOP — return to the
|
||||
relevant step (5/6/7.5). Do not lock past an open gate.
|
||||
`headlessReview.status` is `folded` (or `run` with no open BLOCK/🔴 the
|
||||
operator left unaddressed — Step 6.5), and `visualAssets` is gated — for
|
||||
`standard` format the approved `linkedin/NN/cover.png` exists (Step 7.5); for
|
||||
`carousel` format the approved `carousel.pdf`/`carousel.html` exists. If any is
|
||||
missing, STOP — return to the relevant step (5/6/6.5/7.5). Do not lock past an
|
||||
open gate.
|
||||
|
||||
**Pivot-detection gate (Endring 9c — enforced).** Before locking, compare the
|
||||
current draft against the version that last cleared Step 6:
|
||||
- **word count:** `wc -w <serie>/NN-utkast.md` vs
|
||||
`articles.NN.personaSweep.resonance.wordCount` (the recorded baseline);
|
||||
- **new sections:** top-level headings now present that were absent then
|
||||
(`grep -c '^## '` delta is a fair proxy).
|
||||
|
||||
If the draft has drifted **> 20 % in word count OR gained > 2 sections** since
|
||||
Step 6 cleared, the text pivoted after its gates — **STOP, do not lock.** Run
|
||||
`/linkedin:pivot --article NN --reason "<what changed>"`, which re-opens the
|
||||
pipeline so fact-check (5) → editorial (5.5) → persona (6) → headless (6.5)
|
||||
re-run on the pivoted version. Likewise, if `articles.NN.pivots[]` has an entry
|
||||
whose `gatesToRerun` gates have not since re-passed, STOP — the pivot's
|
||||
re-review is incomplete. (This is exactly the Del 4 v8→v11 case: +42 %, 2 new
|
||||
sections → the gate would have fired and forced the re-sweep.) `[GATE]`
|
||||
|
||||
2. **Confirm the delivery inputs in the series folder.**
|
||||
`render/build-linkedin.mjs` reads, relative to cwd (`<serie>/linkedin/`):
|
||||
|
|
@ -1335,15 +1491,20 @@ Edition complete. Visible in /linkedin:calendar; mark live via /linkedin:calenda
|
|||
|
||||
## Reference Files
|
||||
|
||||
- `${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-state.template.json` — edition-state schema (16 phases including v2.1 skeleton + spine-prose gates, v2.3 visual-assets, v2.4 editorial-review, and v3.1 headless-review + per-article `personas` + `pivots`)
|
||||
- `${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/persona-reviewer.md` — Step 2.5/6/9 reader jury (skeleton + resonance + conversion modes); also resonance + conversion in the Step 6.5 headless package
|
||||
- `${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}/agents/content-reviewer.md` — Step 6.5 cold argument-integrity review (C1–C5; headless, no session context)
|
||||
- `${CLAUDE_PLUGIN_ROOT}/agents/language-reviewer.md` — Step 6.5 cold Norwegian-language review (L1–L5; headless)
|
||||
- `${CLAUDE_PLUGIN_ROOT}/agents/fact-reviewer.md` — Step 6.5 cold re-verification (F1–F4, 🔴/🟡/🟢; catches pivot premises Step 5 missed)
|
||||
- `${CLAUDE_PLUGIN_ROOT}/commands/headless-review.md` — the Step 6.5 cold review package as a standalone command (run in a fresh session for maximum isolation)
|
||||
- `${CLAUDE_PLUGIN_ROOT}/commands/pivot.md` — re-opens the pipeline after a late pivot so Steps 5–6.5 re-run on the changed version before lock
|
||||
- `${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}/references/longform-quality-rules.md` — canonical long-form rules (Steps 2.5, 3a, 3b, 4–9 all reference)
|
||||
|
|
|
|||
161
plugins/linkedin-studio/commands/pivot.md
Normal file
161
plugins/linkedin-studio/commands/pivot.md
Normal file
|
|
@ -0,0 +1,161 @@
|
|||
---
|
||||
name: linkedin:pivot
|
||||
description: |
|
||||
Re-open a long-form edition after a substantive late change (a "pivot") so the
|
||||
already-cleared quality gates re-run on the changed version before lock. Logs
|
||||
the pivot in edition-state, moves currentPhase back to the right earlier step,
|
||||
un-locks if needed, and marks which gates (fact-check, editorial, persona,
|
||||
headless) must re-pass. Includes the pivot-detection heuristic (>20% word-count
|
||||
change or >2 new sections).
|
||||
Use when the user says: "pivot", "I changed the angle", "I added a section",
|
||||
"re-open the edition", "the article changed after it was approved",
|
||||
"re-run the gates", "this needs re-review".
|
||||
Triggers on: "pivot", "linkedin pivot", "re-open edition", "added a section",
|
||||
"changed the angle", "pivot-reopen", "/linkedin:pivot".
|
||||
allowed-tools:
|
||||
- Read
|
||||
- Glob
|
||||
- Grep
|
||||
- Bash
|
||||
- AskUserQuestion
|
||||
- Write
|
||||
---
|
||||
|
||||
# LinkedIn Pivot — re-open a cleared edition for re-review
|
||||
|
||||
A **pivot** is a substantive change to a long-form draft made *after* a gate had
|
||||
already cleared it — a new argument anchor, a new section, a changed thesis. The
|
||||
problem this command solves: the pipeline's gates (fact-check Step 5, editorial
|
||||
Step 5.5, persona Step 6, headless Step 6.5) ran on the *pre-pivot* version, so
|
||||
the changed text was never validated. Without an explicit re-open, a pivoted
|
||||
edition can sail into lock carrying an unverified premise or an unread argument.
|
||||
|
||||
> **Why this exists (Del 4, Endring 9c).** Del 4 was LOCK-ready on an early
|
||||
> version (v8). Then a "Security Champions" pivot added a ~260-word section and a
|
||||
> ~270-word role-description section — roughly +42 % length, two new sections, a
|
||||
> new axis. The pipeline had no pivot-mode, so the whole post-lock chain had to be
|
||||
> re-opened by hand. This command makes the re-open a **named ritual**, not a
|
||||
> manual scramble.
|
||||
|
||||
## Command anatomy
|
||||
|
||||
```
|
||||
/linkedin:pivot
|
||||
--article NN (required; the edition article that changed)
|
||||
--reason "<one line>" (required; e.g. "Security Champions-anker")
|
||||
--to-phase draft | consistency-quality | factcheck-sweep (optional; default from the heuristic)
|
||||
```
|
||||
|
||||
## The pivot-detection heuristic
|
||||
|
||||
Compare the **current** draft against the version that **last cleared Step 6**
|
||||
(persona-sweep-prelock). A pivot-reopen is **suggested/required** when either:
|
||||
|
||||
- **word-count change > 20 %**, or
|
||||
- **> 2 new sections** (top-level headings added since the cleared version).
|
||||
|
||||
This heuristic is also checked as a **lock precondition in Step 8** of
|
||||
`/linkedin:newsletter`: if the draft has drifted past these bounds since Step 6
|
||||
cleared, the lock STOPS and points the operator here. (Length-band drift itself
|
||||
— soft/hard caps — is logged friction F1, not yet a gate.)
|
||||
|
||||
> **Worked example (acceptance test — Del 4 v8 → v11).** At v8 the persona sweep
|
||||
> had cleared a ~1 400-word draft. The Security Champions message pushed it to
|
||||
> ~1 992 words (+42 %, > 20 %) and added 2 sections (a new anchor + a
|
||||
> role-description) — at the boundary of the "> 2 new sections" rule and well past
|
||||
> the 20 % rule. **The heuristic fires:** `/linkedin:pivot --article 04 --reason
|
||||
> "Security Champions-anker"` would log the pivot, move `currentPhase` back to
|
||||
> `draft` (structural change → full re-treatment), and require fact-check +
|
||||
> editorial + persona + headless to re-pass on v11 before lock. That is exactly
|
||||
> the re-sweep the manual Del 4 run had to improvise.
|
||||
|
||||
## Step 1 — Load state + locate the article
|
||||
|
||||
1. Resolve the series root and read `<serie>/linkedin/edition-state.json`. Find
|
||||
`articles.NN` for the `--article` value. If it does not exist, stop and report.
|
||||
2. Read the current draft `<serie>/NN-utkast.md` and note `currentPhase` and
|
||||
`locked`.
|
||||
|
||||
## Step 2 — Measure the pivot scope
|
||||
|
||||
1. **Current word count:** `cd <serie-mappe> && wc -w NN-utkast.md`.
|
||||
2. **Baseline word count:** the word count of the version that last cleared
|
||||
Step 6, recorded by newsletter Step 6 in
|
||||
`articles.NN.personaSweep.resonance.wordCount`. If that field is absent (older
|
||||
state), ask the operator for the cleared-version word count, or treat the
|
||||
pivot as structural by default.
|
||||
3. **Compute** `deltaPct = round((current - baseline) / baseline * 100)` and
|
||||
count `newSections` = top-level headings now present that were not in the
|
||||
cleared version (a `grep -c '^## '` delta is a reasonable proxy; confirm with
|
||||
the operator if ambiguous).
|
||||
4. **Classify scope** (drives the default `--to-phase`):
|
||||
- **Structural** (deltaPct > 20 % OR newSections > 2, or a new axis/thesis) →
|
||||
default `to-phase: draft` (Step 3b). The new material needs full prose
|
||||
expansion → consistency → fact-check → editorial → persona → headless.
|
||||
- **Moderate** (new examples/claims, no new sections, deltaPct ≤ 20 %) →
|
||||
default `to-phase: factcheck-sweep` (Step 5) so the new claims get verified,
|
||||
then editorial + persona + headless re-run.
|
||||
- The operator may override with explicit `--to-phase`.
|
||||
|
||||
## Step 3 — Log the pivot + reset the phase (the ritual)
|
||||
|
||||
1. **Append a pivot entry** to `articles.NN.pivots[]`:
|
||||
```json
|
||||
{
|
||||
"timestamp": "<ISO-8601>",
|
||||
"reason": "<--reason>",
|
||||
"fromPhase": "<currentPhase before this command>",
|
||||
"toPhase": "<resolved to-phase>",
|
||||
"wordCountBefore": <baseline>,
|
||||
"wordCountAfter": <current>,
|
||||
"deltaPct": <deltaPct>,
|
||||
"newSections": <newSections>,
|
||||
"gatesToRerun": ["factcheck-sweep", "editorial-review", "persona-sweep-prelock", "headless-review"]
|
||||
}
|
||||
```
|
||||
`gatesToRerun` always spans every gate from the reset phase through Step 6.5 —
|
||||
a pivot invalidates the fact-check, the editorial craft pass, the persona
|
||||
resonance verdict, AND the headless package, because all of them judged the
|
||||
pre-pivot text.
|
||||
2. **Reset `currentPhase`** to the resolved `toPhase`, and set the article's
|
||||
`phase` to match.
|
||||
3. **Un-lock if needed.** If `articles.NN.locked` was `true`, set `locked: false`
|
||||
and `status: "in-progress"` — a pivot means the edition is no longer locked.
|
||||
Surface this plainly (the prior `POST.html` is now stale and will be re-rendered
|
||||
at the next lock).
|
||||
4. **Invalidate the downstream verdicts** so they cannot be mistaken for current:
|
||||
set `personaSweep.resonance`, `editorialReview`, and `headlessReview.status`
|
||||
back to a re-run state (e.g. `headlessReview.status: "pending"`), and note in
|
||||
each that they were invalidated by pivot `<timestamp>`. Leave the `pivots[]`
|
||||
log and `factcheckLog` history intact (history is durable).
|
||||
5. **Update `updatedAt`** and write `edition-state.json`.
|
||||
|
||||
## Step 4 — Point the next step
|
||||
|
||||
Write a precise next-step line to `<serie>/STATE.md` (overwrite, ONE-system):
|
||||
|
||||
```
|
||||
PIVOT logged (<reason>, +<deltaPct>%, <newSections> new sections) →
|
||||
currentPhase reset to <toPhase>. Resume /linkedin:newsletter; it will re-run
|
||||
fact-check (5) → editorial (5.5) → persona (6) → headless (6.5) on the pivoted
|
||||
version BEFORE lock. Headless package: /linkedin:headless-review --article NN.
|
||||
```
|
||||
|
||||
Do not run the gates yourself — `/linkedin:newsletter` owns the pipeline and will
|
||||
resume deterministically from the reset `currentPhase` and re-run every gate in
|
||||
`gatesToRerun` before it permits lock.
|
||||
|
||||
```
|
||||
Pivot logged.
|
||||
- Article NN: <reason>
|
||||
- Scope: <structural|moderate> (Δ <deltaPct>%, <newSections> new sections)
|
||||
- currentPhase: <fromPhase> → <toPhase> locked: <true→false | unchanged>
|
||||
- Gates to re-run before lock: fact-check (5) · editorial (5.5) · persona (6) · headless (6.5)
|
||||
Next: resume /linkedin:newsletter (re-runs the gates) → then lock.
|
||||
```
|
||||
|
||||
## Reference Files
|
||||
|
||||
- `${CLAUDE_PLUGIN_ROOT}/commands/newsletter.md` — the pipeline this re-opens; Step 8 lock-precondition runs the same heuristic; the resumption table replays from the reset `currentPhase`
|
||||
- `${CLAUDE_PLUGIN_ROOT}/commands/headless-review.md` — the cold review package that must re-pass on the pivoted version
|
||||
- `${CLAUDE_PLUGIN_ROOT}/config/edition-state.template.json` — `articles.NN.pivots` schema + the heuristic notes
|
||||
Loading…
Add table
Add a link
Reference in a new issue