fix(linkedin-studio): N10 — konfigurerbart målnivå-spenn [skip-docs]
C-8: gjør «praktisk anvendbar på alle nivå» til en konfigurerbar gate-akse og fjerner den hardkodede primær-personaen. 6-fils scope (KTG-valgt) — gjør targetLevel til en EKTE ende-til-ende-dimensjon i stedet for et fantomfelt: - references/longform-quality-rules.md: «non-technical line manager» → brukerens konfigurerte target-level span (domene-generelt). - config/personas.template.md: «primær trumfer» krever nå obligatorisk sekundær i MOTSATT ende av spennet; sekundær-NEI kan kun avskrives som «signal, ikke svikt» med eksplisitt ceiling-begrunnelse. Ny tekniker-ende-seed (Persona 4, Løsningsutvikler/AI-ingeniør) — tidligere kun 3 leder-personas. - config/edition-state.template.json: additivt articles.NN.targetLevel-slot (default null, INGEN schemaVersion-bump — samme mønster som sourceTrendId/language). - commands/newsletter.md: persister resolvert targetLevel ved Step 1.5-checkpointet. - agents/persona-reviewer.md: leser articles.NN.targetLevel og plasserer personaene på spennet (fallback til persona-ekspertise om feltet er null). Premiss-korreksjon: planens 4-fils scope antok targetLevel fantes i edition-state via N7-broen; det stemte ikke (fantes kun i trend-store + fritekst-mapping). +2 filer (edition-state-slot + newsletter-persist) gjør konsumet reelt. TDD: ny Section 16q i test-runner (9 UNCOND. .md/.json-greps + non-vacuity self-test); en vacuity-bug i egen edition-state-check fanget og skjerpet (grep slot-literal "targetLevel": null, ikke bare ordet — sourceTrendId-docstringen inneholder allerede "targetLevel"). test-runner 154→163/0, anti-erosion floor 137→146. Øvrige fem suiter uendret grønne (trends 300 · brain 134 · hooks 140 · tests 35 · render 60). 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
34c6c854c0
commit
08106317db
6 changed files with 186 additions and 17 deletions
|
|
@ -98,6 +98,18 @@ Read the named persona from `config/personas.template.md` (or the project's
|
|||
**overbeviser**, **ekspertise**, **sjargong**. Judge as that reader — not as
|
||||
yourself, not as a generic audience. One run = one persona.
|
||||
|
||||
Then read `articles.NN.targetLevel` from the edition-state (set at Step 1 of
|
||||
`/linkedin:newsletter` — the N7 trend bridge / N6 proposal layer pre-fills it,
|
||||
the operator confirms it during calibration). It fixes where this persona sits on
|
||||
**the edition's configured target-level span**: the primær anchors the
|
||||
reader-facing end, and the active set MUST include a mandatory secondary at the
|
||||
OPPOSITE end of that span. Place this persona on the span accordingly — a
|
||||
secondary NO from a reader at the far end is a SIGNAL only when the persona
|
||||
genuinely sits at that end, and only with an explicit ceiling justification;
|
||||
otherwise it is a real flag. If `articles.NN.targetLevel` is absent (a
|
||||
manually-started edition that never resolved one), fall back to the persona's own
|
||||
**ekspertise** field to locate it on the span.
|
||||
|
||||
### Step 2: Read the input as that reader
|
||||
|
||||
Read top to bottom, read-only, once, the way this reader actually would.
|
||||
|
|
|
|||
|
|
@ -300,7 +300,10 @@ what genuinely changes the work.
|
|||
> point the operator confirms or edits* in the dimensions below, not a bypass of
|
||||
> calibration:
|
||||
> - `angle` → the edition **Angle**.
|
||||
> - `targetLevel` → the audience/tone calibration (**Audience** / **Tone**).
|
||||
> - `targetLevel` → the audience/tone calibration (**Audience** / **Tone**); the
|
||||
> resolved value is also persisted to `articles.NN.targetLevel` at the Step 1.5
|
||||
> checkpoint (below) so persona-reviewer can place the personas on the configured
|
||||
> target-level span (which end is primær, which is the mandatory secondary).
|
||||
> - `summary` + `rationale` + `readerQuestion` / `painPoint` → seed the **Key
|
||||
> points** and the **Leader-takeaway** (the reader-side framing this bridge
|
||||
> exists to carry: what the reader would DO with it).
|
||||
|
|
@ -471,6 +474,12 @@ Edition brief
|
|||
- If Step 1 ran the **trend-intake bridge**, persist `articles.NN.sourceTrendId`
|
||||
(the trend id) here too — the durable provenance link Step 10 reads to auto-`act`
|
||||
the trend. Omit it (leave `null`) for a manually-started edition.
|
||||
- Persist the resolved **`targetLevel`** (Step 1's Audience/Tone calibration; the
|
||||
trend bridge pre-fills it from the trend record) to `articles.NN.targetLevel` — the
|
||||
per-article position on the configured target-level span that persona-reviewer reads
|
||||
to place the personas (which end is primær, which is the mandatory secondary). Leave
|
||||
`null` if the operator did not resolve a level; the reader agents then fall back to
|
||||
each persona's own `ekspertise` field.
|
||||
- Persist the resolved **`language`** (Step 1) — write it to the **top-level**
|
||||
`language` (the series/edition-level review language; there is no per-article
|
||||
`language` field) so the language-dependent review agents grade against the
|
||||
|
|
|
|||
|
|
@ -33,7 +33,8 @@
|
|||
"pivots": "Per-article pivot log (Endring 9c). A pivot is a substantive change to a draft AFTER a gate had already cleared — e.g. a new argument anchor / section added late (the Del 4 Security Champions case: +~530 words, 2 new sections, +42 %). Each /linkedin:pivot invocation appends one entry and moves currentPhase back so the cleared gates (Steps 5–6.5) re-run on the pivoted version before lock. Heuristic (documented, checked at the Step 8 lock precondition): if the current draft's word count differs > 20 % from the version that last cleared Step 6, OR it has > 2 new sections, a pivot-reopen is suggested/required. Each entry: { timestamp, reason, fromPhase, toPhase, wordCountBefore, wordCountAfter, deltaPct, newSections, gatesToRerun: [phase…] }. Default [].",
|
||||
"foldIns": "Per-article accumulation queue (slice 2 of fix #1 — «rettelser fester seg»). Each correction KTG makes during an edition that is NOT yet a contract rule is captured here, then routed by the JA-promoter (maskinrommet/docs/skrivekontrakt.md §E) to a permanent home so it never has to be re-discovered: a MECHANICAL correction → a rules.ts gate rule (BLOCK/WARN) + a §B-row/§C1/§C2-box + a §E-manifest row (the contract-gate `ratify` check then asserts rules.ts ↔ §E-manifest stay in bijection); a JUDGMENT correction → a §C2-box only (stays with editorial-reviewer, no gate rule). Capture is per-article (provenance = which article surfaced it); promotion is series/contract-wide. Each entry: { id, date (ISO-8601), correction (what KTG corrected, near-verbatim), trigger (where/why it surfaced), classification: \"mechanical-block\" | \"mechanical-warn\" | \"judgment\" | null (set at the classify step), decision: \"pending\" | \"promoted\" | \"rejected\" (the JA-promoter outcome), ruleId: <rules.ts id> | null (set on promote for mechanical), note?: where a judgment/rejected fold-in landed }. Default []. Rejected fold-ins are kept for traceability, never deleted.",
|
||||
"language": "Review language for this series/edition (additive, default \"en\"). Threads into the long-form review agents so they grade against THIS language's rules: language-reviewer applies Norwegian-specific checks (anglicism→Norwegian idiom, «kanselli-stil») only when language == \"no\"; voice-scrubber's gold standard is the approved editions IN this language; any other value → the agents apply that language's equivalents and never grade prose against Norwegian idiom. \"no\" = Norwegian (the author's case). Resolved at Step 1 / load-context and passed to the language-dependent agents.",
|
||||
"sourceTrendId": "Per-article provenance link to the trend this edition was started from (N7 trend→newsletter bridge, MR-F3). Additive-optional (default null, NO schemaVersion bump — an edition started manually never sets it, and Step 10 reads its absence as \"no source trend\"). Set at the Step 1.5 checkpoint when Step 1's trend-intake read a candidate from the trends store (scripts/trends) to prefill the brief (angle / targetLevel / key-points / source-URLs). Its ONE runtime consumer is Step 10 (scheduling): when the article reaches scheduling AND sourceTrendId is set, the command flips that trend's store status to \"acted\" (trends CLI `act --id <sourceTrendId>`), closing the discovery→production loop deterministically instead of by hand. Value = the trend's store id (normalized title+url, as shown in the /linkedin:trends brief and `CLI list --json`)."
|
||||
"sourceTrendId": "Per-article provenance link to the trend this edition was started from (N7 trend→newsletter bridge, MR-F3). Additive-optional (default null, NO schemaVersion bump — an edition started manually never sets it, and Step 10 reads its absence as \"no source trend\"). Set at the Step 1.5 checkpoint when Step 1's trend-intake read a candidate from the trends store (scripts/trends) to prefill the brief (angle / targetLevel / key-points / source-URLs). Its ONE runtime consumer is Step 10 (scheduling): when the article reaches scheduling AND sourceTrendId is set, the command flips that trend's store status to \"acted\" (trends CLI `act --id <sourceTrendId>`), closing the discovery→production loop deterministically instead of by hand. Value = the trend's store id (normalized title+url, as shown in the /linkedin:trends brief and `CLI list --json`).",
|
||||
"targetLevel": "Per-article target-level for the edition (N10 / C-8). Additive-optional (default null, NO schemaVersion bump — same pattern as sourceTrendId/language; an edition that never resolves one leaves it null and the reader agents fall back to each persona's own `ekspertise` field). Fixes WHERE this edition sits on the configured target-level span (references/longform-quality-rules.md + config/personas.template.md): it tells persona-reviewer which end is the reader-facing (primær) end and which is the mandatory-secondary end, so «practically usable at all levels» is judged from both ends rather than one baked-in reader role. Set at Step 1 of /linkedin:newsletter — the N7 trend bridge / N6 proposal layer pre-fills it from the trend record's `targetLevel` (scripts/trends), the operator confirms or edits it during calibration, and it is persisted here at the Step 1.5 checkpoint (alongside sourceTrendId). Free-text level descriptor (e.g. \"line leader → solution engineer\", \"beginner → practitioner\"), NOT an enum — the span is domain-general and defined by the operator's persona set, never hardcoded."
|
||||
},
|
||||
"schemaVersion": 1,
|
||||
"series": {
|
||||
|
|
@ -92,7 +93,8 @@
|
|||
"foldIns": [],
|
||||
"locked": false,
|
||||
"scheduled": null,
|
||||
"sourceTrendId": null
|
||||
"sourceTrendId": null,
|
||||
"targetLevel": null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ cp config/personas.template.md config/personas.local.md
|
|||
```
|
||||
|
||||
`personas.local.md` is gitignored (via `*.local.md`) so your active overrides
|
||||
stay local. The template ships the three Seres seed personas below; clone,
|
||||
stay local. The template ships the four Seres seed personas below; clone,
|
||||
trim, or extend them per series.
|
||||
|
||||
---
|
||||
|
|
@ -23,11 +23,16 @@ trim, or extend them per series.
|
|||
- **Per-project selection.** `/linkedin:newsletter` (Step 1) picks the relevant
|
||||
personas from this library and marks the primary in the edition brief.
|
||||
- **«primær trumfer».** Exactly one persona is the **primær** reader. On
|
||||
conflict between personas, the primær weighs highest. A *secondary* NO caused
|
||||
by role mismatch or an expertise ceiling («this I already know cold») is a
|
||||
SIGNAL that the gate works — accept it, do not distort the text to chase it.
|
||||
A *primær* NO is **not** accepted: the text is revised until the primær
|
||||
reaches a clean YES.
|
||||
conflict between personas, the primær weighs highest. But the active set MUST
|
||||
include a **mandatory secondary at the opposite end of the target-level span**
|
||||
from the primær (a technical reader when the primær is non-technical, and vice
|
||||
versa) — one reader alone cannot certify a text «practically usable at all
|
||||
levels», so a lone primær JA is not enough. A *secondary* NO caused by role
|
||||
mismatch or an expertise ceiling («this I already know cold») is a SIGNAL that
|
||||
the gate works — but it may be waived as «signal, not failure» **only with an
|
||||
explicit ceiling justification** that names which end of the span hit its
|
||||
ceiling; an unexplained secondary NO is a real flag, not a free pass. A *primær*
|
||||
NO is **not** accepted: the text is revised until the primær reaches a clean YES.
|
||||
- **Two sweep modes** (same `persona-reviewer` agent): resonance mode (Step 6,
|
||||
BEFORE lock — «does the point land for this reader?») and conversion mode
|
||||
(Step 9, after lock — binary «would YOU click?» on the hook only).
|
||||
|
|
@ -64,8 +69,9 @@ can be promoted to a reusable block by pasting them into `personas.local.md`
|
|||
|
||||
The persona sweep is not advisory — it returns a **blocking verdict**
|
||||
(PASS / REWORK / BLOCK), and the bar is the **primær reader's genuine, unqualified
|
||||
JA**. The three Seres seed personas are the canonical set: **A = IT-divisjonsdirektør**
|
||||
(sekundær), **B = KI-seksjonsleder** (sekundær), **C = Linjeleder** (PRIMÆR — trumfer).
|
||||
JA**. The four Seres seed personas are the canonical set: **A = IT-divisjonsdirektør**
|
||||
(sekundær), **B = KI-seksjonsleder** (sekundær), **C = Linjeleder** (PRIMÆR — trumfer),
|
||||
**D = Løsningsutvikler/AI-ingeniør** (sekundær — the mandatory technical end opposite C).
|
||||
|
||||
- **Bar = C ekte JA.** A clean, unqualified yes from the primær. **«JA med store
|
||||
forbehold» = NEI.**
|
||||
|
|
@ -143,6 +149,27 @@ pipeline and the structural check key off them:
|
|||
- **sjargong** — Lav toleranse for teknisk sjargong; setter pris på presise,
|
||||
hverdagsnære formuleringer.
|
||||
|
||||
### Persona 4 — Løsningsutvikler/AI-ingeniør (sekundær)
|
||||
|
||||
> **Technical end of the target-level span** (opposite the primær persona). The
|
||||
> mandatory secondary the «primær trumfer» rule requires: if the primær (Linjeleder)
|
||||
> anchors the reader-facing end, this persona anchors the technical end, so a text
|
||||
> that claims to work «at all levels» is judged from both ends, not just one.
|
||||
|
||||
- **rolle** — Bygger og drifter AI-løsningene i praksis (utvikler, ML-ingeniør eller
|
||||
løsningsarkitekt); skriver koden, velger modellene, eier integrasjonene.
|
||||
- **avkobler** — Ledelses-abstraksjoner uten teknisk substans; «AI-magi» uten hvordan;
|
||||
påstander om hva som er mulig som ikke tåler et implementasjons-blikk; nedlatende
|
||||
forenkling av ting hen kan fra innsiden.
|
||||
- **overbeviser** — Konkret hvordan-det-faktisk-virker, ærlige begrensninger og
|
||||
feilmodi, reelle avveininger (latency/kost/kvalitet), og respekt for at
|
||||
dømmekraften i systemdesign er ekte fag.
|
||||
- **ekspertise** — Høy teknisk. Ekspertise-tak på grunnleggende forklaringer: en
|
||||
«hva er en API»-passasje faller flatt (sekundær-NEI her er et signal — men bare
|
||||
med eksplisitt ceiling-begrunnelse, jf. «primær trumfer»).
|
||||
- **sjargong** — Høy toleranse for teknisk/kode-sjargong; lav for lederspråk og
|
||||
konsulent-buzzwords.
|
||||
|
||||
---
|
||||
|
||||
## Adding a persona
|
||||
|
|
|
|||
|
|
@ -31,8 +31,10 @@ they will do about it.
|
|||
- If the reader cannot state the takeaway in one sentence, the text is not done —
|
||||
tighten until they can.
|
||||
- **Serve what the reader can DO, not what the author knows.** The text exists to
|
||||
give the *primær* reader (the non-technical line manager) something they can act
|
||||
on from their own chair — not to demonstrate the author's breadth. Completeness
|
||||
give the *primær* reader (whoever sits at the reader-facing end of the edition's
|
||||
configured **target-level span** — see `config/personas.template.md`) something
|
||||
they can act on from their own chair — not to demonstrate the author's breadth.
|
||||
Completeness
|
||||
is not a virtue: if a passage exists to be thorough rather than to move the
|
||||
reader's decision forward, cut it. Choose ONE concrete, verifiable (preferably
|
||||
Norwegian) case over an exhaustive list.
|
||||
|
|
|
|||
|
|
@ -67,8 +67,13 @@
|
|||
# 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).
|
||||
# with a non-vacuity self-test) in Section 16p; the configurable target-level span guard (N10: the hardcoded
|
||||
# 'non-technical line manager' is gone from references/ + config/, references/longform-quality-rules.md names
|
||||
# the 'target-level span', config/personas.template.md requires a mandatory secondary at the opposite end +
|
||||
# an explicit ceiling justification + seeds a technical-end persona, config/edition-state.template.json carries
|
||||
# the additive articles.NN.targetLevel slot, commands/newsletter.md persists it, agents/persona-reviewer.md
|
||||
# consumes it onto the span, with a non-vacuity self-test) in Section 16q; the assertion-count anti-erosion
|
||||
# floor (SC6) in Section 18. All are live below (Sections 8–18).
|
||||
#
|
||||
# Usage: bash scripts/test-runner.sh
|
||||
# bash 3.2-safe: plain arrays only, no `declare -A`, no `mapfile`/`readarray`.
|
||||
|
|
@ -1691,6 +1696,114 @@ fi
|
|||
|
||||
echo ""
|
||||
|
||||
# --- Section 16q: Configurable Target-Level Span (N10 / C-8) ---
|
||||
echo "--- Configurable Target-Level Span (N10) ---"
|
||||
|
||||
# N10 turns "practically usable at all levels" into a CONFIGURABLE gate axis and removes the
|
||||
# hardcoded primary persona. Six .md/.json surfaces must hold the invariants (all UNCONDITIONAL
|
||||
# greps -> they raise the deps-absent floor):
|
||||
# (C-8 absence) the hardcoded primary "non-technical line manager" is GONE from references/ + config/
|
||||
# — the quality-rule now points at the edition's CONFIGURED target-level span instead.
|
||||
# (C-8 span-ref) references/longform-quality-rules.md names the "target-level span" (domain-general),
|
||||
# not a single baked-in reader role.
|
||||
# (C-8 obligatorisk-sekundær) config/personas.template.md's «primær trumfer» rule now REQUIRES a
|
||||
# mandatory secondary at the opposite end of the span; a secondary NO may be waived
|
||||
# as «signal, not failure» ONLY with an explicit ceiling justification.
|
||||
# (C-8 tekniker-seed) the seed library gains a technical-end persona (today all three seeds are leaders).
|
||||
# (C-8 edition-state) config/edition-state.template.json carries an additive per-article targetLevel slot.
|
||||
# (C-8 persist) commands/newsletter.md persists the resolved targetLevel to articles.NN.targetLevel.
|
||||
# (C-8 consume) agents/persona-reviewer.md READS articles.NN.targetLevel and places personas on the span
|
||||
# — a compound predicate (targetLevel field + edition-state read + span placement), so a
|
||||
# non-vacuity self-test guards it (mirrors Section 16p).
|
||||
PR_N10="agents/persona-reviewer.md"
|
||||
|
||||
targetlevel_consumed() { # $1 = text; wired iff it reads targetLevel FROM edition-state AND places on the span
|
||||
echo "$1" | grep -qF "targetLevel" \
|
||||
&& echo "$1" | grep -qF "articles." \
|
||||
&& echo "$1" | grep -qF "target-level span"
|
||||
}
|
||||
|
||||
TL_SELFTEST_OK=1
|
||||
if ! targetlevel_consumed "read articles.NN.targetLevel and place the personas on the target-level span"; then
|
||||
TL_SELFTEST_OK=0; echo " non-vacuity FAIL: a fully-wired targetLevel-consumption probe was not detected"
|
||||
fi
|
||||
while IFS= read -r probe; do
|
||||
[ -z "$probe" ] && continue
|
||||
if targetlevel_consumed "$probe"; then
|
||||
TL_SELFTEST_OK=0; echo " false-positive FAIL: under-wired targetLevel probe accepted -> $probe"
|
||||
fi
|
||||
done <<'NEGATIVE16Q'
|
||||
place the personas on the target-level span but never read the field
|
||||
read articles.NN.targetLevel but do not mention the span placement here
|
||||
the targetLevel matters for the target-level span without an edition-state read
|
||||
NEGATIVE16Q
|
||||
if [ "$TL_SELFTEST_OK" -eq 1 ]; then
|
||||
pass "targetLevel-consumption self-test: predicate needs targetLevel + edition-state read (articles.) + span placement (1 accepted, 3 under-wired rejected)"
|
||||
else
|
||||
fail "targetLevel-consumption self-test failed - the N10 consumption lint is vacuous or over-eager"
|
||||
fi
|
||||
|
||||
# (C-8 absence) the hardcoded primary persona is gone from the shippable surfaces
|
||||
if grep -rn "non-technical line manager" references/ config/ >/dev/null 2>&1; then
|
||||
fail "N10: hardcoded 'non-technical line manager' still present in references/ or config/ — the primary persona is not de-hardcoded (C-8)"
|
||||
else
|
||||
pass "N10: no hardcoded 'non-technical line manager' in references/ or config/ (C-8 absence)"
|
||||
fi
|
||||
|
||||
# (C-8 span-ref) the quality rule points at the configured span, not a baked-in role
|
||||
if grep -qF "target-level span" references/longform-quality-rules.md; then
|
||||
pass "references/longform-quality-rules.md names the configured 'target-level span' (C-8 span-ref)"
|
||||
else
|
||||
fail "references/longform-quality-rules.md missing 'target-level span' — the served-reader rule is still role-hardcoded (C-8)"
|
||||
fi
|
||||
|
||||
# (C-8 obligatorisk-sekundær) the «primær trumfer» rule requires a mandatory secondary at the opposite end
|
||||
if grep -qF "opposite end of the target-level span" config/personas.template.md; then
|
||||
pass "config/personas.template.md requires a mandatory secondary at the opposite end of the span (C-8 obligatorisk-sekundær)"
|
||||
else
|
||||
fail "config/personas.template.md missing the 'opposite end of the target-level span' requirement — one reader can still self-certify all levels (C-8)"
|
||||
fi
|
||||
|
||||
# (C-8 ceiling-begrunnelse) a secondary NO may only be waived with an explicit ceiling justification
|
||||
if grep -qF "explicit ceiling justification" config/personas.template.md; then
|
||||
pass "config/personas.template.md gates the secondary-NO waiver behind an explicit ceiling justification (C-8 ceiling-begrunnelse)"
|
||||
else
|
||||
fail "config/personas.template.md missing 'explicit ceiling justification' — a secondary NO can still be waved away as 'signal, not failure' unproven (C-8)"
|
||||
fi
|
||||
|
||||
# (C-8 tekniker-seed) the seed library gains a technical-end persona (today all three seeds are leaders)
|
||||
if grep -qF "Technical end of the target-level span" config/personas.template.md; then
|
||||
pass "config/personas.template.md seeds a technical-end persona opposite the leader personas (C-8 tekniker-seed)"
|
||||
else
|
||||
fail "config/personas.template.md missing a 'Technical end of the target-level span' seed — the library is still leader-only (C-8)"
|
||||
fi
|
||||
|
||||
# (C-8 edition-state) the per-article targetLevel SLOT exists (additive, no schemaVersion bump).
|
||||
# Grep the slot's default literal `"targetLevel": null`, NOT the bare word — the latter already occurs
|
||||
# inside the sourceTrendId doc-string ("angle / targetLevel / key-points"), which would match vacuously.
|
||||
if grep -qF '"targetLevel": null' config/edition-state.template.json; then
|
||||
pass "config/edition-state.template.json carries the additive articles.NN.targetLevel slot (default null, C-8 edition-state)"
|
||||
else
|
||||
fail "config/edition-state.template.json missing the '\"targetLevel\": null' slot — persona-reviewer would consume a phantom field (C-8)"
|
||||
fi
|
||||
|
||||
# (C-8 persist) newsletter.md persists the resolved targetLevel to the per-article slot
|
||||
if grep -qF "articles.NN.targetLevel" commands/newsletter.md; then
|
||||
pass "commands/newsletter.md persists the resolved targetLevel to articles.NN.targetLevel (C-8 persist)"
|
||||
else
|
||||
fail "commands/newsletter.md never persists articles.NN.targetLevel — the slot stays null and consumption is vacuous (C-8)"
|
||||
fi
|
||||
|
||||
# (C-8 consume) persona-reviewer reads targetLevel from edition-state and places personas on the span
|
||||
PR_CONTENT=$(cat "$PR_N10")
|
||||
if targetlevel_consumed "$PR_CONTENT"; then
|
||||
pass "agents/persona-reviewer.md reads articles.NN.targetLevel and places personas on the target-level span (C-8 consume)"
|
||||
else
|
||||
fail "agents/persona-reviewer.md does not consume articles.NN.targetLevel onto the span — targetLevel is set but never read (C-8)"
|
||||
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
|
||||
|
|
@ -1725,12 +1838,16 @@ echo ""
|
|||
# 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.
|
||||
# WebFetch grep + fact-reviewer WebFetch grep) = 137; +9 for N10's nine UNCONDITIONAL Section-16q checks
|
||||
# (targetLevel-consumption self-test + no-hardcoded-line-manager absence grep + references target-level-span
|
||||
# grep + personas opposite-end obligatorisk-sekundær grep + personas explicit-ceiling-justification grep +
|
||||
# personas technical-end seed grep + edition-state targetLevel-slot grep + newsletter articles.NN.targetLevel
|
||||
# persist grep + persona-reviewer targetLevel-consumption compound grep) = 146.
|
||||
# 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=137
|
||||
ASSERT_BASELINE_FLOOR=146
|
||||
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