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:
parent
adb8c5bde3
commit
4fad655a66
2 changed files with 97 additions and 5 deletions
|
|
@ -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[]`:
|
||||
|
|
|
|||
|
|
@ -939,3 +939,85 @@ cannot be (`:30–48`).
|
|||
- Disposition: **VERIFIED — NO CHANGE** (0 edits) · 0 deferrals · axes a/b/c/d all PASS.
|
||||
|
||||
---
|
||||
|
||||
## Session 11 — Longform companion (`pivot`)
|
||||
|
||||
> S11 status: re-grounded STATE's predicates against the actual `pivot.md` (162 lines) BEFORE talk.
|
||||
> Class agreed steps 1–2: **pure state-mutation / orchestration command, NOT post-emitting, invokes NO
|
||||
> agents, never drafts prose.** It re-opens a long-form edition after a late substantive change so the
|
||||
> cleared gates re-run before lock — mutating `edition-state.json` (append `pivots[]`, reset
|
||||
> `currentPhase`, un-lock, invalidate downstream verdicts), writing a next-step line to `<serie>/STATE.md`,
|
||||
> and deferring the actual gate-runs to `/linkedin:newsletter` (`:144`). Feed-post predicates
|
||||
> (hook 110–140, 1,200–1,800 band, no-body-link) do NOT apply — even further from feed-post than
|
||||
> `headless-review` (this one does not even produce a review of prose). The pivot heuristic + `pivots[]`
|
||||
> schema + gates-to-rerun chain are SHARED with `newsletter` (Step 8) and `headless-review` →
|
||||
> S9/S10 findings reused as baseline. **Unlike S9/S10 this was NOT a no-change session — P4 surfaced a
|
||||
> real cross-file off-by-one bug.**
|
||||
|
||||
### /linkedin:pivot — re-open a cleared edition for re-review, NOT post-emitting
|
||||
|
||||
**INTENT.** Make the post-lock re-open a NAMED RITUAL, not a manual scramble (the Del 4 Security
|
||||
Champions case, `:33–38`): log the pivot, reset `currentPhase` so newsletter's deterministic resumption
|
||||
re-runs the invalidated gates (fact-check 5 → editorial 5.5 → persona 6 → headless 6.5), un-lock if
|
||||
needed, and invalidate the stale downstream verdicts so a pre-pivot PASS cannot be mistaken for current.
|
||||
|
||||
**SIMULATE (grounded persona-sim).**
|
||||
- **Persona (operator):** KTG having made a late change to a LOCKED edition, running
|
||||
`/linkedin:pivot --article NN --reason "<...>"`.
|
||||
- **Walk:** Step 1 load state + locate article (missing → stop+report `:75`) → Step 2 measure scope
|
||||
(`wc -w` current vs baseline `personaSweep.resonance.wordCount`; baseline-absent → ask/default
|
||||
structural `:84–86`; ambiguous sections → confirm `:90`) → **Step 2 classify → default `--to-phase`**
|
||||
→ Step 3 append `pivots[]` + reset `currentPhase` + un-lock + invalidate verdicts (`:100–131`) →
|
||||
Step 4 write next-step line to `<serie>/STATE.md`, defer gates to newsletter (`:133–155`).
|
||||
- **Friction sought (the S2–S8 pattern):** **FOUND — a phase-reset off-by-one (P4).**
|
||||
|
||||
**EVALUATE (4 axes — re-tolket for a non-post-emitting orchestrator).**
|
||||
- **(a) intention fidelity — PASS after fix.** The ritual (log → reset → un-lock → invalidate → point)
|
||||
delivers the promise; `allowed-tools` complete and used. The off-by-one (below) made the *moderate*
|
||||
branch silently betray the stated intent ("so the new claims get verified") → fixed.
|
||||
- **(b) format bar (heuristic-consistency, NOT feed predicates) — PASS.** Three-way SSOT verified
|
||||
identical: pivot heuristic (>20 % words OR >2 sections, baseline = `personaSweep.resonance.wordCount`)
|
||||
== newsletter Step 8 lock-precondition (`:1348–1362`) == template `_doc.pivots` (`:30`). Newsletter
|
||||
Step 6 actually writes the baseline field (`:990–994`, `:1005`) → not a phantom. Cardinal rule
|
||||
("never reopen a locked text", newsletter `:1034–1036`) honoured: pivot is the *sanctioned* exception
|
||||
— it loudly un-locks + flags POST.html stale (`:124`) + invalidates verdicts (`:126–130`).
|
||||
- **(c) quality rules (state-integrity) — PASS.** `gatesToRerun` `:113` = the 4 real gate phase-names;
|
||||
invalidation targets (`personaSweep.resonance` · `editorialReview` · `headlessReview.status`) all
|
||||
exist in the schema; `factcheckLog` + `pivots[]` left intact (durable history `:130`).
|
||||
- **(d) agent-wiring + graceful degradation — PASS.** No agents (orchestrator). 3 degradation points
|
||||
enumerated (article-missing `:75` · baseline-absent `:84–86` · ambiguous-sections `:90`). 3/3 ref
|
||||
files exist.
|
||||
|
||||
**THE BUG (P4 — confirmed cross-file off-by-one).** Newsletter's resumption table is explicit
|
||||
(`:196–198`, `:203–221`): `currentPhase` = the *last completed* phase, and the pipeline resumes at the
|
||||
step **after** it (`draft`→Step 4, `consistency-quality`→Step 5, `factcheck-sweep`→Step 5.5). But
|
||||
pivot.md set the **moderate** default to `to-phase: factcheck-sweep` "so the new claims get verified" —
|
||||
which resumes at **Step 5.5 (editorial), SKIPPING the Step 5 fact-check** the moderate pivot exists to
|
||||
run. Self-contradictory, and a violation of the command's own invariant that "a pivot always invalidates
|
||||
the fact-check" (`:116–119`). The **structural** branch (`to-phase: draft`→Step 4→fact-check at 5) was
|
||||
functionally safe but mis-labelled "(Step 3b)". Root cause: conflating "the phase to resume AT" with the
|
||||
`currentPhase` (last-completed) value to write.
|
||||
|
||||
**THE FIX (4 surgical edits, confined to command-anatomy + Step 2).**
|
||||
1. Moderate default `:96–97`: `factcheck-sweep` → **`consistency-quality`** (resumes Step 5, fact-check
|
||||
runs) — the bug.
|
||||
2. Structural label `:92–95`: "(Step 3b) … full prose expansion" → "resumes **Step 4
|
||||
(consistency-quality)** … then fact-check (5) → editorial (5.5) → persona (6) → headless (6.5)".
|
||||
3. Off-by-one guard callout `:102–108`: explains `to-phase` is a `currentPhase` (last-completed) value
|
||||
and newsletter resumes at the NEXT step → why a pivot never resets to `factcheck-sweep`.
|
||||
4. Anatomy enum `:46`: dropped `factcheck-sweep` from the offered values (it skips fact-check, violating
|
||||
the always-invalidate-fact-check invariant) → `draft | consistency-quality`.
|
||||
`gatesToRerun` `:113` and the worked example `:64–70` (structural, `draft`) left intact — both correct.
|
||||
|
||||
**VERIFY.**
|
||||
- Re-grepped final file: `factcheck-sweep` now only at `:106` (guard, as the forbidden value) + `:123`
|
||||
(gatesToRerun gate-name); to-phase defaults = `draft` (`:93`) / `consistency-quality` (`:97`); enum
|
||||
`:46` = two values; guard landed `:102`.
|
||||
- `bash scripts/test-runner.sh` → `Passed: 81 · Failed: 0 · Warnings: 0`; counts **29/19/26** unchanged
|
||||
(count/version/model-consistency guards passed; .md-only edit, node/analytics tests not triggered).
|
||||
- **One NICE left (anti-gold-plating):** template `:30` says "+~530 words" (sum of the 2 named sections)
|
||||
vs worked-example 1400→1992 = +592; both "~", both +42 % — cosmetic, below the axis-pass bar, left.
|
||||
- Disposition: **FIXED** (4 edits, all in `commands/pivot.md`) · 0 deferrals · axes a/b/c/d all PASS
|
||||
post-fix.
|
||||
|
||||
---
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue