feat(linkedin-studio): N9 — randsone-gates (krok/carousel/tittel) + deterministisk fakta-recheck + WebFetch [skip-docs]
C-4/C-9: Step 9 (post-lock) gets a scoped fact-reviewer (F1/F3) + language-reviewer (L1/L2) pass over the four feed-facing surfaces — delingstekst hook, samle post, carousel slide text, edition title — plus a title-strength gate, before the hook conversion binary. Placed at Step 9 (first phase where all four exist; operates on distribution copy only, never the locked body — body/canonical-title defect = pivot). C-6: deterministic post-factcheck fact-invariant after Step 5 — any later fold-in whose diff touches a sentence with numbers, names, or quotes fires a mandatory scoped fact-reviewer mini-recheck (not a judgment call), closing the pivot-heuristic blind spot; enforced at the 6.5 + 7 fold-in sites. C-7: WebFetch added to fact-checker + fact-reviewer (verbatim source text for quote precision / exact-figure attribution). TDD RED→GREEN: test-runner Section 16p (8 unconditional greps + non-vacuity self-test), anti-erosion floor 129 → 137. Suite 146 → 154/0. Domain-general. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014bE7VbkmR3cqHFEeGfzgwb
This commit is contained in:
parent
b30b0e208f
commit
34c6c854c0
5 changed files with 239 additions and 41 deletions
|
|
@ -1169,6 +1169,21 @@ Gate: [PASS — zero unresolved 🔴] (else REWORK/BLOCK with the open claims)
|
|||
Next: Step 5.5 — Editorial review (craft gate, BEFORE the persona sweep).
|
||||
```
|
||||
|
||||
> **Post-factcheck fact-invariant (Endring N9 / C-6) — deterministic recheck.**
|
||||
> Once this sweep clears, the article's facts are gated — but every later edit can
|
||||
> silently reintroduce an unchecked fact: an editorial fold-in (5.5), a persona
|
||||
> tightening (6), a headless fold-in (6.5), an annotation pass (7). So the
|
||||
> re-verification trigger is **deterministic — not a judgment call:** if a fold-in's
|
||||
> diff touches any sentence containing **numbers, names, or quotes**, it fires a
|
||||
> mandatory **scoped fact-reviewer** mini-recheck of *exactly those changed
|
||||
> sentences* (`subagent_type: linkedin-studio:fact-reviewer`, inputs limited to the
|
||||
> touched sentences + the sources they cite) before the phase advances. This
|
||||
> replaces the old "re-run only if it felt substantive" discretion and closes the
|
||||
> pivot-heuristic blind spot: a swapped statistic that adds no section and stays
|
||||
> under the +20 % word-count threshold never trips `/linkedin:pivot`, but it *does*
|
||||
> trip this trigger. A verified fact stays verified only if every number, name, or
|
||||
> quote that changes after the gate is re-checked. `[GATE]`
|
||||
|
||||
## Step 5.5: Editorial review — craft gate, BEFORE the persona sweep
|
||||
|
||||
The fact-checked draft now faces the **editor**: a single `editorial-reviewer`
|
||||
|
|
@ -1441,6 +1456,10 @@ command runs — see `commands/headless-review.md` for the full cold contract):
|
|||
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.
|
||||
**Post-factcheck fact-invariant applies (C-6):** independently of the substantive
|
||||
judgment, if any folded edit's diff touches a sentence with **numbers, names, or
|
||||
quotes**, fire the deterministic **scoped fact-reviewer** mini-recheck on those
|
||||
sentences (see the invariant after Step 5) before advancing.
|
||||
|
||||
7. **Persist + checkpoint state.** Record the run in `edition-state.json` →
|
||||
`articles.NN.headlessReview` (`frozenDraft`, per-reviewer `{reportPath,
|
||||
|
|
@ -1497,6 +1516,10 @@ editor is satisfied with the in-session draft. It does not gate lock.
|
|||
editor's manual annotation pass. Fold any resulting edits back **by
|
||||
tightening** (the Step 4 rule still holds) — then, if the edit was
|
||||
substantive, re-run the affected sweep (Step 5 or 6) before proceeding.
|
||||
**Post-factcheck fact-invariant applies (C-6):** any annotation edit whose diff
|
||||
touches a sentence with **numbers, names, or quotes** fires the deterministic
|
||||
**scoped fact-reviewer** mini-recheck on those sentences (see the invariant after
|
||||
Step 5), regardless of whether the edit felt substantive.
|
||||
|
||||
4. **Persist.** Set `currentPhase: "annotation"` in `edition-state.json` and
|
||||
write an "annotation rendered (optional) → next: visual assets" line to
|
||||
|
|
@ -1778,51 +1801,104 @@ Next: Step 9 — Hook / conversion gate (post-lock).
|
|||
|
||||
## Step 9: Hook / conversion gate — "would YOU click?" (AFTER lock)
|
||||
|
||||
The locked edition still has to earn the click. The **distribution text** — the
|
||||
delingstekst hook that fronts the post in the feed — now faces a final binary
|
||||
gate: would the primær persona, scrolling, actually stop and open this? This is
|
||||
the post-lock conversion sweep, distinct from the pre-lock resonance sweep
|
||||
(Step 6): it judges the **hook only**, binary, never the body.
|
||||
The locked edition still has to earn the feed. Before it ships, the
|
||||
**feed-facing surfaces** the reader actually meets — the **delingstekst hook**,
|
||||
the **samle post**, the **carousel slide text**, and the **edition title** —
|
||||
face two final gates: a **randsone fact + language gate** (so nothing false or
|
||||
clumsy reaches the feed in the most-read, most-requoted copy — C-4/C-9) and the
|
||||
**hook conversion binary** (would the primær persona, scrolling, actually stop
|
||||
and open this?). Both operate on **distribution copy only** — the post-lock
|
||||
sweep, distinct from the pre-lock resonance sweep (Step 6), never the locked
|
||||
body. Until N9 only the hook's click-worth was judged; the hook's *truth and
|
||||
language*, the samle post, the carousel slides and the title reached the feed
|
||||
ungated.
|
||||
|
||||
> **Order assertion (enforced).** This hook-gate (Step 9) runs AFTER lock
|
||||
> (Step 8) — it operates on the distribution text of an already-locked edition
|
||||
> and must never reopen the locked body. If the hook fails, you revise the
|
||||
> *delingstekst* (and re-render via Step 8's `build-linkedin.mjs`), not the
|
||||
> edition text. This ordering is the inverse of the resonance sweep, which runs
|
||||
> BEFORE lock — keeping the two apart is what fixed the Seres process. `[GATE]`
|
||||
> **Order assertion (enforced).** This gate (Step 9) runs AFTER lock (Step 8) — it
|
||||
> operates on the **distribution copy** of an already-locked edition and must never
|
||||
> reopen the locked body. Both the randsone fact/language gate and the hook binary
|
||||
> revise only distribution surfaces (delingstekst, samle, carousel text,
|
||||
> distribution title) and re-render via Step 8's `build-linkedin.mjs` /
|
||||
> `build-carousel.mjs` — never the edition text. A defect that traces into the
|
||||
> **locked article body or its canonical title** is a pivot (STOP →
|
||||
> `/linkedin:pivot`), not an in-place edit. This ordering is the inverse of the
|
||||
> resonance sweep, which runs BEFORE lock — keeping the two apart is what fixed the
|
||||
> Seres process. `[GATE]`
|
||||
|
||||
**Why Step 9 (placement, documented per plan N9).** The randsone gate lives here,
|
||||
not earlier, because Step 9 is the **first phase where all four surfaces exist**:
|
||||
the carousel slide text is authored in Step 7.5 (after the last content gate 6.5),
|
||||
and the delingstekst + samle post exist only from Step 8. Step 9 already operates on
|
||||
post-lock distribution copy, so gating and revising these surfaces here respects the
|
||||
lock invariant without breaking phase order.
|
||||
|
||||
**Procedure:**
|
||||
|
||||
1. **Take the distribution hook** — the first two lines of the edition's entry
|
||||
in `linkedin/edition-delingstekst.md` (and the `samle` hook, if shipping the
|
||||
collected post). This is what the reader sees before "…see more".
|
||||
1. **Collect the four distribution surfaces.** These are what the reader meets in
|
||||
the feed — and until N9 only the first was judged, and only for click-worth:
|
||||
- the **delingstekst hook** — the first two lines of the edition's entry in
|
||||
`linkedin/edition-delingstekst.md` (what the reader sees before "…see more");
|
||||
- the **samle post** — the `## Samle` section, if shipping the collected post;
|
||||
- the **carousel slide text** — `linkedin/NN/carousel.md`, for a carousel-format
|
||||
edition (authored in Step 7.5, after the last content gate — so it has never
|
||||
been fact- or language-checked);
|
||||
- the **edition title** — the edition's own headline (its distribution / feed
|
||||
title), never strength-gated before now.
|
||||
|
||||
2. **Run `persona-reviewer` in conversion mode** (`subagent_type:
|
||||
linkedin-studio:persona-reviewer`) for the **primær** persona
|
||||
only, from THIS command layer in the foreground. Pass
|
||||
**`mode: konverter`** (the after-lock, hook-only mode — NOT resonans). The
|
||||
agent returns a single binary verdict, **JA / NEI**, on «would YOU click?» —
|
||||
no axis scoring, no flags, no rewritten copy (principle: the jury judges,
|
||||
the editor writes).
|
||||
2. **Randsone fact + language gate (scoped — C-4/C-9).** Run two scoped reviewers
|
||||
over *only* these surfaces (never the locked body), issued from THIS command
|
||||
layer in the foreground, `subagent_type` namespaced:
|
||||
- `linkedin-studio:fact-reviewer` — **F1** (verifiable claims) + **F3** (number
|
||||
attribution) on any figure, named case, or quotation appearing in the four
|
||||
surfaces. A hook or slide that asserts a number the body never sourced is a 🔴.
|
||||
(WebFetch the source for any quoted or exact figure — C-7.)
|
||||
- `linkedin-studio:language-reviewer` — **L1** (repetition) + **L2** (anglicisms
|
||||
/ stiff register), graded against `edition-state.language`. The hook is the
|
||||
most-requoted line the edition has; a clumsy or anglicised hook is an L-BLOCK.
|
||||
Pass each reviewer only the four surfaces + the sources they cite + `language`.
|
||||
This is a **scoped mini-pass**, not a re-review of the article. `[GATE]`
|
||||
|
||||
3. **Gate on the binary.**
|
||||
3. **Title-strength gate (C-9).** The edition's own title was never strength-gated —
|
||||
only the hook's two lines were. Judge the **title-strength** of the **edition
|
||||
title** here: does it earn the open on its own, or is it a generic label? The
|
||||
strongest candidate becomes the distribution title. The verdict is **binding on
|
||||
the distribution title** (sharpen a weak one in place) and **advisory on the
|
||||
canonical (locked) title** — changing that is a body edit → STOP, run
|
||||
`/linkedin:pivot`; never edit the locked title in place.
|
||||
|
||||
4. **Resolve the randsone gate.**
|
||||
- **Fact 🔴 / L-BLOCK on a distribution surface** → revise that surface **by
|
||||
tightening** (hook, samle, carousel text, or distribution title — all
|
||||
distribution copy), **re-render** the affected artifact (POST.html via Step 8's
|
||||
`build-linkedin.mjs`; carousel via `build-carousel.mjs`), and re-run the scoped
|
||||
pass on what changed. The locked body stays frozen. Loop until clean.
|
||||
- **A defect that traces into the locked body or canonical title** → it is a
|
||||
pivot: STOP, run `/linkedin:pivot`. The randsone gate never reopens the locked
|
||||
body in place — the same invariant as the conversion loop. `[GATE]`
|
||||
|
||||
5. **Run the hook conversion binary** (`subagent_type:
|
||||
linkedin-studio:persona-reviewer`, **`mode: konverter`** — the after-lock,
|
||||
hook-only mode, NOT resonans) for the **primær** persona only. The agent returns
|
||||
a single binary verdict, **JA / NEI**, on «would YOU click?» — no axis scoring,
|
||||
no flags, no rewritten copy (the jury judges, the editor writes).
|
||||
- **JA** → the hook converts. Proceed to Step 10.
|
||||
- **NEI** → the hook does not earn the click. Revise the **delingstekst hook
|
||||
only** (sharpen the krok by tightening — close the gap, hold the body
|
||||
frozen), **re-render POST.html** via Step 8's `build-linkedin.mjs` (the
|
||||
body stays locked; only the distribution copy changed), and re-run the
|
||||
- **NEI** → revise the **delingstekst hook only** (sharpen the krok by tightening,
|
||||
body frozen), **re-render POST.html** via `build-linkedin.mjs`, and re-run the
|
||||
conversion check. Loop until JA. `[GATE]`
|
||||
|
||||
4. **Persist.** Record the conversion verdict in
|
||||
`edition-state.json` → `articles.NN.personaSweep.conversion`,
|
||||
and set `currentPhase: "hook-conversion-gate"`.
|
||||
6. **Persist.** Record the randsone-gate result in `edition-state.json` →
|
||||
`articles.NN.randsoneGate` (per-surface fact/language verdicts + title-strength +
|
||||
what was revised) and the conversion verdict in
|
||||
`articles.NN.personaSweep.conversion`; set `currentPhase: "hook-conversion-gate"`.
|
||||
|
||||
```
|
||||
Hook / conversion gate (post-lock).
|
||||
- Primær persona: <name> mode: konverter (hook only)
|
||||
- Verdict: JA — hook converts (else: NEI — revise delingstekst hook, re-render, re-check)
|
||||
Randsone + conversion gate (post-lock, distribution copy only).
|
||||
- Surfaces gated: delingstekst hook · samle post · carousel slide text · edition title
|
||||
- Fact (F1/F3): <N> 🔴 → resolved (or pivot if it traces to the body)
|
||||
- Language (L1/L2): <N> BLOCK → tightened
|
||||
- Title-strength: <verdict> (distribution title sharpened; canonical-title weakness → pivot)
|
||||
- Conversion (primær, konverter): JA (else: NEI — revise hook, re-render, re-check)
|
||||
- Body: untouched (locked in Step 8)
|
||||
Gate: [PASS — JA] (else loop on the distribution hook)
|
||||
Gate: [PASS — randsone clean + JA] (else loop on distribution copy, or pivot)
|
||||
Next: Step 10 — Scheduling.
|
||||
```
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue