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
|
|
@ -24,6 +24,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
- **Auto-gull — the gold corpus grows on its own (A2-F2)** — Step 8 lock now appends the locked edition to `ingest/published/` tagged `provenance=published`, so the corpus the chronicle profile is distilled from grows one edition at a time (the author's own locked editions, never the engine's drafts). No manual curation.
|
||||
- Domain-general throughout: languages/formats come from the user's own corpus, never hardcoded. test-runner 140 → 146 (Section 16o: 6 unconditional voice-chain greps + self-test; anti-erosion floor 123 → 129).
|
||||
|
||||
### Added — randsone gates: gate the feed-facing surfaces (N9 / C-4, C-6, C-7, C-9)
|
||||
|
||||
- **Randsone fact + language gate (C-4/C-9)** — `/linkedin:newsletter` Step 9 (post-lock) now runs a **scoped** `fact-reviewer` (F1 verifiable claims + F3 number attribution) + `language-reviewer` (L1 repetition + L2 anglicisms/register) pass over the four feed-facing surfaces the reader actually meets — the **delingstekst hook**, the **samle post**, the **carousel slide text**, and the **edition title** — before the hook-conversion binary. Until now only the hook's click-worth was judged; its truth and language, the samle post, the carousel slides and the title reached the feed ungated. Placed at Step 9 because that is the first phase where all four surfaces exist (carousel is authored in Step 7.5; delingstekst/samle from Step 8), and Step 9 already operates on post-lock distribution copy — so the gate revises distribution surfaces + re-renders, never the locked body; a defect tracing into the locked body/canonical title is a pivot.
|
||||
- **Edition title-strength gate (C-9)** — the edition's own title now faces a strength gate at Step 9 (binding on the distribution title, advisory on the locked canonical title), not only the hook's two lines.
|
||||
- **Deterministic post-factcheck fact-recheck (C-6)** — a new invariant after Step 5: any later fold-in (5.5/6/6.5/7) whose diff touches a sentence with **numbers, names, or quotes** fires a mandatory **scoped `fact-reviewer`** mini-recheck of exactly those sentences — a deterministic trigger, **not a judgment call**, replacing the old "re-run if it felt substantive" discretion and closing the pivot-heuristic blind spot (a swapped statistic under the +20 % threshold never trips `/linkedin:pivot`, but does trip this).
|
||||
- **WebFetch on both fact agents (C-7)** — `fact-checker` and `fact-reviewer` gain `WebFetch`: quote precision (F2) and exact-figure attribution (F3) require the verbatim source text in context, not a search snippet.
|
||||
- test-runner 146 → 154 (Section 16p: 8 unconditional randsone-gate greps + self-test; anti-erosion floor 129 → 137). Domain-general; no KTG-specific values hardcoded.
|
||||
|
||||
### Built feedback (awaiting consumer-side proof)
|
||||
|
||||
- **MR-F9** (demand-sweep «innenfra og ut») — built; the (a)/(b)/(c) evidence gate is a runtime demonstration, proven consumer-side (plugin agents don't resolve in the dev repo).
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ description: |
|
|||
"is this accurate", "verify numbers", "back this up", "factual correctness".
|
||||
model: opus
|
||||
color: brown
|
||||
tools: ["Read", "WebSearch"]
|
||||
tools: ["Read", "WebSearch", "WebFetch"]
|
||||
---
|
||||
|
||||
# Fact Checker Agent
|
||||
|
|
@ -89,6 +89,14 @@ Always run the contradiction sweep (5) — a claim that survives a deliberate
|
|||
search for counter-evidence is far stronger than one that merely matched a
|
||||
confirming page.
|
||||
|
||||
**Fetch the verbatim source for quotes and figures (WebFetch).** A search
|
||||
*snippet* is enough to locate a source but not to confirm a **quotation** or an
|
||||
**exact figure** — for those, `WebFetch` the located page and read the original
|
||||
wording in context. A quote is verified only against the source's verbatim text
|
||||
(«Vi» vs «Vi i Nav» is a precision failure even when the gist matches); a figure
|
||||
is verified only against the number as the source actually states it. Never grade
|
||||
quote precision or number attribution off a snippet alone.
|
||||
|
||||
### Step 3: Assess Source Credibility
|
||||
|
||||
For each source, evaluate: is it primary or secondary, who published it, how
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ description: |
|
|||
"number attribution", "headless review", "fact-reviewer".
|
||||
model: opus
|
||||
color: gold
|
||||
tools: ["Read", "WebSearch"]
|
||||
tools: ["Read", "WebSearch", "WebFetch"]
|
||||
---
|
||||
|
||||
# Fact Reviewer Agent
|
||||
|
|
@ -211,6 +211,12 @@ source's exact wording and attribution and compare verbatim (F2). For figures,
|
|||
trace to a named source and confirm the source's precision matches the draft's
|
||||
(F3/F4 — "around a third" does not verify "37 %").
|
||||
|
||||
**Fetch the verbatim source, do not grade off a snippet (WebFetch).** F2 (quote
|
||||
precision) and F3 (number attribution) cannot be settled from a search *snippet* —
|
||||
`WebFetch` the located source and read the original wording/number in context, then
|
||||
compare verbatim. A quote or figure marked 🟢 must have been checked against the
|
||||
source's actual text, never a preview line.
|
||||
|
||||
### Step 3 — Score on the five dimensions
|
||||
|
||||
Score each claim 0–100 across the five dimensions. A contradicted claim is 🔴
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -64,7 +64,10 @@
|
|||
# the trends-brief-history/diff wiring guard (RE-R3e: scripts/trends/src/brief.ts has
|
||||
# 'export function diffSurfaced' AND 'parseSurfacedFrontmatter' AND the 'Nytt siden sist' section AND
|
||||
# the 'surfaced: ' frontmatter emit, scripts/trends/src/cli.ts wires 'selectPriorBriefFile', with a
|
||||
# non-vacuity self-test) in Section 16n; the assertion-count anti-erosion floor (SC6) in Section 18. All
|
||||
# non-vacuity self-test) in Section 16n; the randsone-gates guard (N9: newsletter.md Step 9 runs a scoped
|
||||
# fact-reviewer + language-reviewer pass over the carousel slide text AND edition title, gates title-strength,
|
||||
# declares the deterministic 'numbers, names, or quotes' recheck trigger, and both fact agents carry WebFetch,
|
||||
# with a non-vacuity self-test) in Section 16p; the assertion-count anti-erosion floor (SC6) in Section 18. All
|
||||
# are live below (Sections 8–18).
|
||||
#
|
||||
# Usage: bash scripts/test-runner.sh
|
||||
|
|
@ -1594,6 +1597,100 @@ fi
|
|||
|
||||
echo ""
|
||||
|
||||
# --- Section 16p: Randsone-Gates - feed-facing surfaces + deterministic fact-recheck + WebFetch (N9 / C-4,C-6,C-7,C-9) ---
|
||||
echo "--- Randsone Gates (N9) ---"
|
||||
|
||||
# N9 closes the border-zone gap: the hook (delingstekst), the samle post, the carousel slide text and the
|
||||
# edition title reach the feed today with NO fact/language gate (Step 5/6.5 run on the article BODY;
|
||||
# carousel is authored in Step 7.5 AFTER the last content gate; Step 9 judged only the hook's click-worth).
|
||||
# Four invariants must hold in the .md surfaces:
|
||||
# (C-4/C-9) Step 9's post-lock gate is extended with a scoped fact-reviewer + language-reviewer pass over
|
||||
# all four distribution surfaces (delingstekst hook, samle post, carousel slide text, edition
|
||||
# title candidates). Step 9 is the first phase where all four exist, and it already operates on
|
||||
# post-lock distribution copy the gate may revise without reopening the locked body.
|
||||
# (C-6) any post-factcheck fold-in whose diff touches a sentence with numbers, names or quotes fires a
|
||||
# mandatory scoped fact-reviewer mini-recheck - a DETERMINISTIC trigger ("not a judgment call"),
|
||||
# which also covers the pivot-heuristic blind spot (a swapped number no >20% rule would catch).
|
||||
# (C-7) both fact agents (fact-checker, fact-reviewer) carry WebFetch - quote precision needs the
|
||||
# verbatim source text, not just a search snippet.
|
||||
# The randsone predicate is compound (both reviewers AND both new surfaces), so a non-vacuity self-test
|
||||
# guards it (mirrors Section 16o). All checks are UNCONDITIONAL .md greps -> they raise the deps-absent floor.
|
||||
NL_N9="commands/newsletter.md"
|
||||
# Slice the Step 9 block (## Step 9: up to ## Step 10:) so the gate literals are scoped to that phase.
|
||||
STEP9_BLOCK=$(awk '/^## Step 9:/{f=1} /^## Step 10:/{f=0} f' "$NL_N9")
|
||||
|
||||
randsone_gated() { # $1 = text; wired iff BOTH reviewers AND BOTH new surfaces are named
|
||||
echo "$1" | grep -qF "fact-reviewer" \
|
||||
&& echo "$1" | grep -qF "language-reviewer" \
|
||||
&& echo "$1" | grep -qF "carousel slide text" \
|
||||
&& echo "$1" | grep -qF "edition title"
|
||||
}
|
||||
|
||||
RZ_SELFTEST_OK=1
|
||||
if ! randsone_gated "scoped fact-reviewer + language-reviewer over the carousel slide text and edition title candidates"; then
|
||||
RZ_SELFTEST_OK=0; echo " non-vacuity FAIL: a fully-wired randsone-gate probe was not detected"
|
||||
fi
|
||||
while IFS= read -r probe; do
|
||||
[ -z "$probe" ] && continue
|
||||
if randsone_gated "$probe"; then
|
||||
RZ_SELFTEST_OK=0; echo " false-positive FAIL: under-wired randsone-gate probe accepted -> $probe"
|
||||
fi
|
||||
done <<'NEGATIVE16P'
|
||||
language-reviewer over the carousel slide text and edition title but no fact reviewer here
|
||||
fact-reviewer + language-reviewer over the edition title only, no carousel surface
|
||||
fact-reviewer + language-reviewer over the carousel slide text only, no title surface
|
||||
NEGATIVE16P
|
||||
if [ "$RZ_SELFTEST_OK" -eq 1 ]; then
|
||||
pass "randsone-gate self-test: predicate needs BOTH fact+language reviewers AND both new surfaces (1 accepted, 3 under-wired rejected)"
|
||||
else
|
||||
fail "randsone-gate self-test failed - the randsone lint is vacuous or over-eager"
|
||||
fi
|
||||
|
||||
# (C-4/C-9) Step 9 gates all four distribution surfaces through fact + language reviewers
|
||||
if randsone_gated "$STEP9_BLOCK"; then
|
||||
pass "newsletter.md Step 9 runs the scoped fact-reviewer + language-reviewer randsone gate over carousel + title (C-4/C-9)"
|
||||
else
|
||||
fail "newsletter.md Step 9 missing the randsone fact+language gate - hook/samle/carousel/title still reach the feed ungated (C-4/C-9)"
|
||||
fi
|
||||
|
||||
# (C-9) the edition's own title now faces a strength gate at Step 9 (today only the hook's two lines were judged)
|
||||
if echo "$STEP9_BLOCK" | grep -qF "title-strength"; then
|
||||
pass "newsletter.md Step 9 gates the edition title-strength, not only the hook (C-9)"
|
||||
else
|
||||
fail "newsletter.md Step 9 missing the title-strength gate - the edition title still ships unjudged (C-9)"
|
||||
fi
|
||||
|
||||
# (C-6) deterministic post-factcheck fact-recheck trigger (nouns + mechanism + determinism marker)
|
||||
if grep -qF "numbers, names, or quotes" "$NL_N9"; then
|
||||
pass "newsletter.md declares the deterministic recheck trigger nouns ('numbers, names, or quotes', C-6)"
|
||||
else
|
||||
fail "newsletter.md missing the recheck trigger nouns 'numbers, names, or quotes' (C-6)"
|
||||
fi
|
||||
if grep -qF "scoped fact-reviewer" "$NL_N9"; then
|
||||
pass "newsletter.md routes the post-factcheck recheck to a scoped fact-reviewer (C-6)"
|
||||
else
|
||||
fail "newsletter.md missing 'scoped fact-reviewer' - the recheck has no mechanism (C-6)"
|
||||
fi
|
||||
if grep -qF "not a judgment call" "$NL_N9"; then
|
||||
pass "newsletter.md marks the recheck trigger deterministic ('not a judgment call', C-6)"
|
||||
else
|
||||
fail "newsletter.md missing the determinism marker 'not a judgment call' - the trigger is still discretionary (C-6)"
|
||||
fi
|
||||
|
||||
# (C-7) both fact agents carry WebFetch (verbatim source text for quote precision)
|
||||
if grep -qF "WebFetch" agents/fact-checker.md; then
|
||||
pass "fact-checker.md carries WebFetch (verbatim source text for quote precision, C-7)"
|
||||
else
|
||||
fail "fact-checker.md missing WebFetch in tools - quote precision has no verbatim-source fetch (C-7)"
|
||||
fi
|
||||
if grep -qF "WebFetch" agents/fact-reviewer.md; then
|
||||
pass "fact-reviewer.md carries WebFetch (verbatim source text for quote precision, C-7)"
|
||||
else
|
||||
fail "fact-reviewer.md missing WebFetch in tools - cold re-verification has no verbatim-source fetch (C-7)"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
# --- Section 18: Assertion-Count Anti-Erosion (SC6) ---
|
||||
# The lint self-modifies its own checks, so a green run could mask a silently dropped
|
||||
# assertion. Pin the total pass()+fail() invocations as a monotonic floor; the count
|
||||
|
|
@ -1625,12 +1722,15 @@ echo ""
|
|||
# 'surfaced: ' frontmatter grep) = 123; +6 for N8's six UNCONDITIONAL Section-16o checks (voice-chain
|
||||
# self-test + voice-trainer chronicle-voice-profile grep + newsletter chronicle-voice-profile grep +
|
||||
# newsletter chronicle-voice-drift-log read grep + newsletter Step-1 language-resolve grep + newsletter
|
||||
# Step-8 auto-gull compound grep) = 129.
|
||||
# Step-8 auto-gull compound grep) = 129; +8 for N9's eight UNCONDITIONAL Section-16p checks (randsone-gate
|
||||
# self-test + Step-9 randsone_gated compound grep + Step-9 title-strength grep + newsletter C-6 trigger-nouns
|
||||
# grep + newsletter C-6 scoped-fact-reviewer grep + newsletter C-6 determinism-marker grep + fact-checker
|
||||
# WebFetch grep + fact-reviewer WebFetch grep) = 137.
|
||||
# NB: the floor tracks the deps-absent MINIMUM (conditional TS suites warn-skip and drop
|
||||
# the count), so it is bumped only by UNCONDITIONAL new checks — NOT pinned to the
|
||||
# deps-present TOTAL_CHECKS (that would zero the warn-skip margin and false-fail a fresh
|
||||
# clone). Runs last so TOTAL_CHECKS sees every prior check.
|
||||
ASSERT_BASELINE_FLOOR=129
|
||||
ASSERT_BASELINE_FLOOR=137
|
||||
TOTAL_CHECKS=$((PASS + FAIL))
|
||||
if [ "$TOTAL_CHECKS" -ge "$ASSERT_BASELINE_FLOOR" ]; then
|
||||
pass "assertion-count anti-erosion: $TOTAL_CHECKS checks >= baseline floor $ASSERT_BASELINE_FLOOR"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue