fix(linkedin-studio): S11 harden pivot — moderate to-phase reset skipped fact-check (off-by-one)

S11 hardening of /linkedin:pivot. Class: pure state-mutation/orchestration
command, NOT post-emitting, invokes no agents — re-opens a long-form edition so
cleared gates re-run before lock. Feed-post predicates N/A.

BUG (P4, cross-file off-by-one): newsletter's resumption table treats
currentPhase as the LAST COMPLETED phase and resumes at the NEXT step
(draft->Step 4, consistency-quality->Step 5, factcheck-sweep->Step 5.5). The
moderate pivot default set to-phase: factcheck-sweep "so the new claims get
verified" — which resumes at Step 5.5 and SKIPS the Step 5 fact-check the
moderate pivot exists to run, violating the command's own invariant that a pivot
always invalidates the fact-check.

FIX (4 surgical edits, commands/pivot.md only):
1. moderate default factcheck-sweep -> consistency-quality (resumes Step 5)
2. structural label "(Step 3b)" -> "Step 4 (consistency)"
3. off-by-one guard callout explaining to-phase = currentPhase (last-completed)
4. anatomy enum drops factcheck-sweep -> draft | consistency-quality
gatesToRerun list + worked example unchanged (both correct).

4-axis verdict: a/b/c/d all PASS post-fix, 0 deferrals -> FIXED.
Gate: test-runner 81/0/0 · counts 29/19/26 unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016qgzo6rxthw7KuxHjn5vyE
This commit is contained in:
Kjell Tore Guttormsen 2026-06-18 18:34:37 +02:00
commit 4fad655a66
2 changed files with 97 additions and 5 deletions

View file

@ -43,7 +43,7 @@ edition can sail into lock carrying an unverified premise or an unread argument.
/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)
--to-phase draft | consistency-quality (optional; default from scope — see Step 2)
```
## The pivot-detection heuristic
@ -90,13 +90,23 @@ cleared, the lock STOPS and points the operator here. (Length-band drift itself
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.
default `to-phase: draft` so newsletter resumes at **Step 4
(consistency-quality)** — the changed prose is re-threaded, then fact-check
(5) → editorial (5.5) → persona (6) → headless (6.5) re-run on it.
- **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.
default `to-phase: consistency-quality` so newsletter resumes at **Step 5
(fact-check)** and the new claims get verified, then editorial (5.5) →
persona (6) → headless (6.5) re-run.
- The operator may override with explicit `--to-phase`.
> **Why these specific phases (the off-by-one trap).** `to-phase` is written as
> `currentPhase`, which records the *last completed* phase; `/linkedin:newsletter`'s
> resumption table (Step 0) then resumes at the step **after** it. So `draft`
> resumes at **Step 4** and `consistency-quality` resumes at **Step 5** (the
> fact-check). A pivot therefore never resets to `factcheck-sweep` — that would
> resume at Step 5.5 and **skip** the fact-check, violating this command's
> invariant that a pivot always invalidates the fact-check (Step 3, `gatesToRerun`).
## Step 3 — Log the pivot + reset the phase (the ritual)
1. **Append a pivot entry** to `articles.NN.pivots[]`: