fix(linkedin-studio): N20 — CLAUDE_PLUGIN_ROOT-normalisering av load-bearing ref-stier [skip-docs]

81 load-bearing stier i 16 kommandofiler pinnet til ${CLAUDE_PLUGIN_ROOT}/.
Rene prefikser: 81 inn / 81 ut, ingen linje lagt til eller fjernet.

FASIT-VERIFISERING (premiss-sjekk mot BASELINE, ikke bare HEAD): planens
68 linjer / 17 filer er RIKTIG mot sin egen baseline 882f6ee. HEAD ga 73/18 —
altså ekte kode-drift siden baseline (newsletter.md 11→15, calendar.md 0→1),
ikke en telle-feil som i N19.

SCOPE UTVIDET ETTER GO: planens grep (references|hooks/scripts|scripts) har en
blindsone — skills/ assets/ config/ render/ agents/ commands/ er samme defekt-
klasse og allerede anerkjent som pin-verdig av mønsteret (audit.md:24 og
competitive.md:22 pinner skills/, batch.md:214 og pipeline.md:211 pinner
assets/). Under planens scope kunne dens EGEN verifisering ikke passere:
quick.md:229-230 ville stått upinnet rett over en pinnet linje, og setup.md
(3 load-bearing Read, 0 pins) var ikke i settet i det hele tatt.
Full defektklasse: 117 linjer / 19 filer → 81 pinnet, 36 bevisst urørt.

KLASSIFISERINGSREGEL (utledet av mønsteret, ikke oppfunnet):
- PIN = stien skal åpnes/kjøres/skrives — Read/Reference/See-pekere,
  ref-fil-lister, Bash/node/npm, edit-mål.
- PROSA = stien står som sitat bak en påstand, eller som beskrivelse av
  oppførsel/plassering. Ingen inline «(see …)» er pinnet noe sted i repoet
  (0 av 25 filer) — den grensen er arvet, ikke satt her.

36 BEVISST URØRTE, tre klasser:
1. Inline sitat/attribusjon (17): carousel.md:37 · pipeline.md:70,75 ·
   newsletter.md:999,1082,1211,1433 · report.md:273 · video.md:69 ·
   react.md:115 · calendar.md:87 · profile.md:28,34 · firsthour.md:112 ·
   monetize.md:343,492 · outreach.md:922
2. Beskrivelse av oppførsel/plassering (10): newsletter.md:50,1097,1205,1694,
   1744,1944 · report.md:45 · linkedin.md:218 · import.md:121,200
3. newsletter.md fase-tabellens Tooling-kolonne (9): :100,102,105,106,111,112,
   113,115,116 — tabellen er et register, og hvert steg pinner sitt eget
   faktiske kall i brødteksten.

S13-LINTEN (M0 data-dir): alle assets/-stiene som ble pinnet står eksplisitt i
lintens NEGATIVE13-unntaksliste, og lintens egen kommentar (test-runner.sh:552)
sier at shipped read-only assets SKAL bære ${CLAUDE_PLUGIN_ROOT}.

VERIFISERING: 76 unike pinnede stier hentet ut og resolvet med cwd=/tmp —
75 finnes. Den ene som ikke gjør det, config/personas.local.md, er en
gitignored valgfri fallback («else personas.template.md») som var pinnet på
newsletter.md:205 før denne endringen. Re-grep: gjenstående 36 er utelukkende
prosa, listet over.

Alle ti suiter grønne, alle floors uendret: test-runner 270/0 (269 assertions
>= floor 251) · trends 300/0 · analytics 202/0 · hooks 191/0 · brain 134/0 ·
editions 72/0 · render 63/0 · specifics-bank 45/0 · tests 35/0 ·
contract-gate 33/0.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M31cw43gqiSSDrKzcwUi7L
This commit is contained in:
Kjell Tore Guttormsen 2026-07-31 17:21:22 +02:00
commit e7276cf8bb
16 changed files with 81 additions and 81 deletions

View file

@ -207,7 +207,7 @@ the edition left off before doing anything.
4. **None / insufficient** — Step 1 will **define personas interactively**.
Exactly one persona is the **primær**. The resolved set feeds BOTH the Step 6
resonance sweep AND the Step 6.5 headless package; see
`config/personas.template.md` → "Per-artifact personas".
`${CLAUDE_PLUGIN_ROOT}/config/personas.template.md` → "Per-artifact personas".
6. **Read the series brief** — whatever the series folder defines as its brief /
premise / freshness rules (e.g. `<serie>/brief.md`, or the resolved brief
recorded in `edition-state.json`). This anchors angle and scope.
@ -421,7 +421,7 @@ Settle these dimensions (most should come from context, not questions):
`articles.NN.personas` (each entry: name, tier, the five fields, source) at the
**Step 1.5 checkpoint** (the first machine-state write; Step 2 then adds the
research notes) — it is then stable across sessions and is the single source
every later sweep reads. See `config/personas.template.md` →
every later sweep reads. See `${CLAUDE_PLUGIN_ROOT}/config/personas.template.md` →
"Per-artifact personas".
- **Key points** — the 24 load-bearing claims the edition must make.
- **Tone** — respected-peer vs. teaching-down; calibrated to the primær.
@ -633,7 +633,7 @@ Next: Step 2 — Research (scoped by the binding: fill ekstern/unresolved, verif
1. **Decompose** the binding's `ekstern`/`unresolved` slots + unverified numbers
(step 0) into 25 *scoped, orthogonal* research sub-questions. Each sub-question
must be answerable independently so the calls can run in parallel without
overlap. Reuse the multi-source synthesis discipline from `commands/react.md`
overlap. Reuse the multi-source synthesis discipline from `${CLAUDE_PLUGIN_ROOT}/commands/react.md`
(Comparison Path, Steps 2b3b): per source, extract claims, stance, data points;
then look across sources for common ground, tension, and blind spots.
@ -1083,7 +1083,7 @@ turning-points the spine already named.
only afterward.)
3. **Expand with the `content-repurposer` muscle.** Reuse
`agents/content-repurposer.md` (its article→long-form conversion discipline)
`${CLAUDE_PLUGIN_ROOT}/agents/content-repurposer.md` (its article→long-form conversion discipline)
for individual section expansions — invoke it via `Task`
(`subagent_type: linkedin-studio:content-repurposer`) when useful, *from this
command layer* (foreground, principle 4). The command owns assembly and
@ -1212,7 +1212,7 @@ mechanical noise.
> which is the §A/quality reference applied by reasoning.
> **Adopter note.** The gate is a KTG-internal tool: it needs the contract-gate deps
> (`scripts/contract-gate/node_modules` — run `npm install` there once) and, for
> (`${CLAUDE_PLUGIN_ROOT}/scripts/contract-gate/node_modules` — run `npm install` there once) and, for
> ratify, the maskinrommet contract. If either is absent (a plain adopter ships
> neither), this Step is a **no-op** and the §B/§C1 discipline falls back to the Step 4
> quality-pass reasoning. It never blocks an adopter.
@ -1496,7 +1496,7 @@ reopening locked texts — the biggest single process error of the series (plan
1. **Load the active personas** chosen in Step 1, with exactly one marked
**primær**. Each persona's five fields (rolle, avkobler, overbeviser,
ekspertise, sjargong) come from `config/personas.local.md` (or the template).
ekspertise, sjargong) come from `${CLAUDE_PLUGIN_ROOT}/config/personas.local.md` (or the template).
2. **Fan out one `persona-reviewer` call per persona, in parallel** — issue them
in a SINGLE message (multiple `Task` tool-uses, `subagent_type:
@ -1583,7 +1583,7 @@ in-session pass hid. `content-reviewer` is genuinely new (argument integrity,
which no in-session gate measures). Do NOT collapse the pairs.
**Procedure** (this is the same package the standalone `/linkedin:headless-review`
command runs — see `commands/headless-review.md` for the full cold contract):
command runs — see `${CLAUDE_PLUGIN_ROOT}/commands/headless-review.md` for the full cold contract):
1. **Freeze the draft.** Snapshot the persona-passed `NN-utkast.md` so the
reviewers judge a stable artifact and the report names exactly what was read:
@ -1596,7 +1596,7 @@ command runs — see `commands/headless-review.md` for the full cold contract):
default `en` — tells `language-reviewer` and `voice-scrubber` which language's
rules to grade against; Norwegian-specific checks fire only when `language: no`),
the writing contract *if it ships* (`<serie>/../../docs/skrivekontrakt.md`
plugin mirror → `references/longform-quality-rules.md`; absent for an adopter,
plugin mirror → `${CLAUDE_PLUGIN_ROOT}/references/longform-quality-rules.md`; absent for an adopter,
the craft agents' in-tree checklists are self-contained), and the active
personas (`articles.NN.personas`, primær identified). Nothing else.
@ -2269,7 +2269,7 @@ time. Step 11 empties that queue into permanent, enforced homes.
- **`mechanical-block` / `mechanical-warn`** — provable by string, regex, or
count with ~zero false positives. **JA →** promote **atomically**: add the
rule to `scripts/contract-gate/src/rules.ts`, the matching row to the
rule to `${CLAUDE_PLUGIN_ROOT}/scripts/contract-gate/src/rules.ts`, the matching row to the
writing contract, and the manifest row that binds them; set the fold-in's
`decision: "promoted"` and `ruleId`. Then prove it: