Commit graph

12 commits

Author SHA1 Message Date
527391ab47 feat(linkedin-studio): wire lived-specifics extraction into newsletter pipeline (fix #2 slice 3)
Wire the kilde-så-draft binding (slices 1–2) into the live /linkedin:newsletter
pipeline so a draft is grounded in the operator's real material BEFORE any prose.

- New Step 1.5 "Lived-specifics extraction" BETWEEN brief (1) and research (2):
  query the bank → 3–5 sharp elicitation questions (vagueness refused, never
  fabricated — invariant 3) → `add` to the specifics-bank → populate the
  articles.NN.livedSpecifics slot-map → render the NN-kilder.md ledger. First
  phase to write machine state (persists brief + personas + binding).
- Deterministic binding-gate (`validate-binding`, exit 1 = BLOCK) wired as a new
  step 5 of the Step 2.5 skeleton-gate (renumbered 5→9): no prose is written on
  an unresolved load-bearing claim; livedSpecifics → "bound" on pass.
- Step 2 re-scoped BY the binding: research fills the ekstern/unresolved slots +
  verifies the operator's own numbers (regel 6/7) → flips bank unverified→verified
  → re-renders NN-kilder.md, instead of re-sourcing what is already grounded.
- Pipeline 17 → 18 phases: new `lived-specifics` phase id in edition-state
  _doc.phases; resumption table + pipeline overview + CLAUDE.md + README updated.
- Lint: new Section 15 "Specifics-Bank Binding" wires the specifics-bank suite
  into CI (suite green + case-count floor 28, KTG-only/adopter-skip); the
  assertion-count anti-erosion floor moves to Section 16.

Verification: structure gate 84/0/0 (was 83), specifics-bank 28/28, contract-gate
ratify 1:1, edition-state template valid JSON (18 phases), gitleaks clean on all
5 files. Design record: docs/fix2/slice2-binding.md.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 22:29:01 +02:00
33f18c35e6 feat(linkedin-studio): per-edition lived-specifics binding + kilder artifact (fix #2 slice 2)
Slice 2 of fix #2 (kilde-så-draft): the per-edition bridge between the global
specifics-bank (slice 1) and a draft. In scripts/specifics-bank:

- binding.ts — livedSpecifics slot-map model + validateBinding, the «vaghet
  avvises» gate (G4). BLOCK on unresolved / dangling specific-id / unjustified
  abstrakt; WARN on an unverified-number-backed slot (regel 6/7); PASS otherwise.
- kilder.ts — renderKilder, the read-only NN-kilder.md sources ledger, rendered
  deterministically from the slot-map + bank (coverage via validateBinding, so
  the artifact and the skeleton-gate cannot disagree).
- cli.ts — validate-binding (exit 1 = BLOCK) + render-kilder subcommands.

edition-state schema gains articles.NN.livedSpecifics ({ slots, status }) + _doc.
Design record + the Step 2 research re-scope spec: docs/fix2/slice2-binding.md.

Wiring into newsletter.md (Step 1.5 elicitation + Step 2.5 gate + pipeline 17->18)
is slice 3 — not in this commit.

Tests 28/28 (specifics-bank; +9 binding +4 kilder), tsc clean, plugin gate 83/0/0
unaffected, gitleaks clean on all 8 files.

[skip-docs] internal plumbing — no command/agent/pipeline surface change until
slice 3 wires it; documenting it in the user-facing root CLAUDE.md/README now would
overclaim unreachable functionality. Docs ARE updated where they belong: the design
record docs/fix2/slice2-binding.md, scripts/specifics-bank/README.md, and the
edition-state _doc. Root-doc update lands with the slice-3 wiring feat.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 21:33:44 +02:00
e26d9fb659 feat(linkedin-studio): wire contract-gate + ratify into newsletter Step 4.5 (fix #1 slice 3)
Slice 3 of fix #1 (§C2 bindende + akkumulerende): wire the deterministic
§B/§C1 contract-gate into the long-form pipeline as a pre-handoff step, so
mechanical violations die deterministically BEFORE the costly AI gates and
never reach the operator at a judgment gate ("rettelser fester seg").

Newsletter pipeline:
- New Step 4.5 (between Step 4 consistency/quality and Step 5 fact-check):
  (1) ratify the gate (rules.ts <-> §E-manifest 1:1; DRIFT escalates to
  operator, not a draft fix) and (2) gate the draft (exit 0 PASS / exit 1
  BLOCK -> fix by tightening + re-run; WARN = direction, never blocks).
  Adopter-safe: no-op when contract-gate deps / the maskinrommet contract
  are absent.
- Pipeline phase count 16 -> 17 across every LIVE surface (overview table +
  build-status + narrative + banner + schema pointer in newsletter.md;
  command-table rows in CLAUDE.md and README). Historical version-narrative
  entries left untouched.
- edition-state.template.json: new "contract-gate" phase string (Step 4.5),
  between consistency-quality and factcheck-sweep. JSON valid; phases 16->17.

CI integration (test-runner.sh, Section 14 — new):
- 14a: contract-gate suite stays green (npm test exit 0) with a 33-test
  case-count floor (anti-erosion).
- 14b: ratify invariant — rules.ts bound 1:1 to the §E-manifest.
- Both KTG-internal and adopter-safe (warn/skip, never fail, when deps or the
  contract are absent). set +e inside the test command-substitution keeps a
  red suite from aborting the runner; ratify exit 1 captured via if-subshell.
- SC6 anti-erosion section renumbered 14 -> 15; header comment updated.

Verification:
- Structure lint 81 -> 83/0/0 (contract-gate suite + ratify both green).
- Documented Step 4.5 command smoke-tested on a real draft
  (serier/silvija-seres-motsvar/utkast/04-utkast.md): 0 BLOCK / 2 WARN ->
  "Gate passert", exit 0.
- edition-state.template.json valid JSON; phases array = 17.

foldIns capture into the pipeline phases deferred to a later slice (scope A).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 20:34:49 +02:00
ff3467de79 feat(linkedin-studio): accumulation binding — ratify + §E foldIns (fix #1 slice 2)
Slice 2 av fix #1 («rettelser fester seg»): binder rules.ts til den menneskelige
sannhetskilden (skrivekontrakt §E), så en korreksjon gjort én gang håndheves for
alltid i stedet for å gjenoppdages hver utgave (diagnose-gap 2).

- ratify.ts: importerer RULES direkte (ingen skjør tekst-parsing), krever en
  bijeksjon mot §E gate-bindings-manifestet (id ↔ ref ↔ severity) + dangling-
  §-anker-sjekk. CLI: `--ratify [kontrakt]` / `npm run ratify`. Sti: arg > env
  MASKINROMMET_CONTRACT > default (relativt til modulen).
- edition-state.template: foldIns[] fangst-kø (per-artikkel proveniens,
  pending|promoted|rejected, mekanisk→rules.ts / dømmekraft→§C2).
- 12 nye tester (33 totalt), tsc rent. Live-kontrakt-integrasjonstest ratify-er
  det ekte §E-manifestet rent (10 regler bundet 1:1).

Companion: skrivekontrakt §E (JA-promoter-prosedyre + manifest) i maskinrommet.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 17:48:20 +02:00
f1506bb1fe feat(linkedin-studio): make long-form review language configurable [skip-docs]
Wave 3 / Step 9 of the remediation plan (Phase 1 — usable by a non-author).

The long-form review layer shipped Norwegian-locked: language-reviewer graded
unconditional Norwegian, voice-scrubber's gold standard was 'approved Norwegian
editions', and the editorial/content craft gates pointed at a 'skrivekontrakt §C2'
that does not ship. A non-Norwegian adopter would get English prose graded against
Norwegian idiom and a gate that depends on an unshipped contract.

- config/edition-state.template.json: add additive 'language' field (top-level,
  default 'en') + a _doc entry. Threads into the language-dependent agents.
- agents/language-reviewer.md: new 'Language parameter' section — Norwegian-specific
  checks (anglicism->Norwegian idiom, kanselli-stil) apply only when language=='no';
  any other value grades that language's equivalents and never flags idiomatic
  English as an anglicism. Default 'en'.
- agents/voice-scrubber.md: gold standard reframed to 'approved editions in the
  configured language'; the Norwegian-chronicle calibration is the language=='no'
  instantiation.
- agents/editorial-reviewer.md + agents/content-reviewer.md: the in-tree checklist
  is now the operative, self-contained source of truth; Maskinrommet §C2 is an
  optional upstream contract that does NOT ship (available only on the author's
  runs). The gates work for an adopter without it.
- commands/newsletter.md: thread 'language' through the Step 6.5 cold-inputs and the
  per-reviewer call inputs; the writing contract is now 'if it ships'.

Norwegian remains fully working when language: no (the author's case).

fact-reviewer.md was in the plan's file list but needed no change on inspection:
its F1-F4 checks (claims/quotes/numbers/sources) are language-agnostic; its
'Norwegian' mentions are boundary notes vs language-reviewer, which stay correct.

[skip-docs]: three-doc + version reconciliation is Step 21 (pre-review-gate); these
intermediate Wave commits are not pushed before the /trekreview gate.

Verify: edition-state JSON parses + has top-level language 'en'; language-reviewer
has 'language ==' references and no unconditional-Norwegian assertion; editorial
§C2 reframed to in-tree fallback ('operative source', 'does not ship'); agent
fixtures 35/35 pass; structural lint exit 0 (61 passed).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30 00:55:04 +02:00
9556a9529d feat(linkedin-studio): parameterize series path + de-brand render output [skip-docs]
Wave 3 / Step 8 of the remediation plan (Phase 1 — usable by a non-author).

The flagship long-form engine shipped bespoke-as-general: a maintainer-private
absolute series path and a hardcoded 'Maskinrommet' brand in the renderers. Both
are now generalized so a non-author can run the pipeline without inheriting the
author's filesystem or publication identity.

- commands/newsletter.md + config/edition-state.template.json: series-root default
  /Users/ktg/repos/maskinrommet/serier -> $HOME/linkedin-series; reconciled the
  prose that called the maskinrommet folder 'the default' so it no longer
  contradicts the new neutral default. LTL_SERIES_ROOT override contract preserved.
- render/build-linkedin.mjs + render/build-carousel.mjs: brand is now an LTL_BRAND
  env-var (empty default -> generic). Samle-post title, carousel footer brand-span,
  and cover-eyebrow fallback are de-branded; empty brand renders clean chrome. The
  operator sets LTL_BRAND=Maskinrommet in their own env to re-brand (same pattern
  as LTL_SERIES_ROOT).
- config/image-credit-caption.template.md: 'Maskinrommet-/serie-badge' -> generic
  'serie-badge'.

Out of scope (Step 9): the residual 'Maskinrommet skrivekontrakt §C2/§A'
references in newsletter.md are the writing-contract generalization, handled next.

[skip-docs]: three-doc + version reconciliation is Step 21 (pre-review-gate, per
plan: 'LAST so it captures everything'). These intermediate Wave commits are NOT
pushed before the /trekreview gate, so the three-doc obligation (which governs
pushed changes) is satisfied at Step 21, not per local checkpoint commit.

Verify: grep -rIn '/Users/ktg' config/ commands/ render/ (excl .local) -> no
matches; grep -rn 'Maskinrommet' render/ -> no matches (de-branded); node --check
on both render scripts -> OK; LTL_SERIES_ROOT still present in newsletter.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30 00:46:05 +02:00
c6bfda1516 feat(linkedin-studio): v3.1.0 — Endring 9 adversarial review-pakke + per-artefakt personas
Cold, adversarial review package for the long-form pipeline + configurable
per-edition personas. Motivated by Del 4 (Security Champions pivot): the
in-session editor + persona sweep shared the drafting session's framing-bias,
so the shipped version was never independently re-reviewed.

Headless package (9a/9b):
- New Step 6.5 (headless-review) in /linkedin:newsletter, after the persona
  sweep, before lock — the independence layer the in-session gates can't be.
- New standalone /linkedin:headless-review command (run in a fresh session for
  maximum isolation; reconstructs frozen draft + contract + personas from disk).
- 3 new Opus archetypes, each with a cardinal context-isolation block that
  refuses drafting-session framing as "context pollution":
  - content-reviewer (argument integrity C1–C5, ≤8 flags)
  - language-reviewer (Norwegian language L1–L5, ≤10 flags)
  - fact-reviewer (cold re-verification F1–F4, risk-sort + pivot-risk, WebSearch)
- Deliberate redundancy with fact-checker / editorial-reviewer documented so
  the pairs are never de-duplicated.

Pivot-reopen (9c):
- New /linkedin:pivot command: logs articles.NN.pivots[], resets currentPhase,
  un-locks, marks gates to re-run.
- Pivot-detection gate in Step 8 lock precondition (>20% word-count change or
  >2 new sections re-opens cleared gates). Del 4 v8→v11 worked example.

Per-artifact personas (new requirement):
- articles.NN.personas with resolution order (edition-state → series file →
  plugin library → interactive). One or more readers configurable per edition.

Schema/docs:
- edition-state.template.json: additive personas[], pivots[], headlessReview,
  headless-review phase (16 phases); personaSweep.resonance.wordCount baseline.
- 3 fasit fixtures + 3 structural lint tests (Del 4 worked cases).
- Counts: 24→26 commands, 16→19 agents, 15→16 newsletter phases.
- README + CLAUDE.md (plugin + root) + CHANGELOG synced.

Verification: 35 agent-fixture + 59 hook + 20 render tests green. Backward-
compatible (additive state); reload required before the 3 new agents resolve.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-29 13:01:24 +02:00
34463e4585 feat(linkedin): v2.4.0 — editorial-reviewer agent + Step 5.5 craft gate in /linkedin:newsletter
Endring 8 from the change spec (Del 4 production, Maskinrommet). The persona
resonance sweep measures reader-response (does it land?); nothing measured prose
craft or narrative architecture (is it well-made?). In Del 4 every persona
reported PASS, yet the editor found 8 fresh editorial points on first reading —
~6/8 craft/architecture blind spots no agent could see. v2.4.0 adds the missing
editor role.

New Step 5.5 (editorial-review) runs between fact-check (Step 5) and the persona
sweep (Step 6): a new editorial-reviewer agent (Opus) judges two axes —
prosa-handverk (em-dash density, verbatim repetition, postulated numbers,
contradictions, versal-tic) + narrativ-arkitektur (concrete instantiation,
theory-anchored hypotheses, series-title symmetry, equal action per addressee,
un-overloaded conclusion). Returns <=10 flags as direction (never copy), each
BLOCK/REWORK/NICE, operator-gated via SendUserFile. Runs before the persona
sweep so the personas measure resonance instead of stumbling on craft noise.
Mirrors the Maskinrommet writing-contract section C2 (bidirectional mirror rule).

- agents/editorial-reviewer.md (NEW, Opus, orange) + fasit fixture
  (editorial-reviewer-cases.md: Del 4 v5 gold standard, 8 points -> 2 axes +
  severities, 3 BLOCK / 5 REWORK, 6/8 blind spots) + structural lint (7 tests).
- Step 5.5 wired into commands/newsletter.md; pipeline 14 -> 15 phases.
- editorial-review phase + additive editorialReview state in
  config/edition-state.template.json; resumption: factcheck-sweep -> Step 5.5,
  editorial-review -> Step 6 (spec said fact-check; canonical key is
  factcheck-sweep).
- persona-reviewer contract unchanged: editorial-reviewer is supplementary
  (one measures craft, one measures response).
- All doc levels synced (plugin + root README/CLAUDE.md, CHANGELOG, plugin.json
  2.3.0 -> 2.4.0; agents 15 -> 16). 94 tests green.

Acceptance-criterion #8 (live run on Del 4 v5) delivered as fasit fixture:
a live run needs a session reload (new agent not invokable until then) + read
access to the Del 4 v5 draft in Maskinrommet.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-29 06:17:50 +02:00
b362f120f5 feat(linkedin): v2.3.0 — Step 7.5 visual-assets phase in /linkedin:newsletter
Endring 7 from the change-spec: make visual assets an explicit pipeline
phase. New Step 7.5 (visual-assets) between annotation (Step 7) and lock
(Step 8): cover (+ optional inline figures) or carousel deck, generated and
operator-gated BEFORE lock so build-linkedin.mjs picks up cover.png at lock
without a post-lock re-render. Pipeline 13 → 14 phases.

- commands/newsletter.md: Step 7.5 section, pipeline overview + build-status,
  resumption table (annotation → 7.5; new visual-assets → 8), Step 8
  precondition, reference-file list.
- config/edition-state.template.json: visual-assets phase + additive
  articles.NN.visualAssets schema (format / cover / figures / carousel).
- config/image-credit-caption.template.md (new): motif + credit + caption
  table, honest-about-AI credit, naming convention.
- Two generation routes, no lock-in: default mcp-image (cover-v<N>-kandidat.png)
  or external cover-raw.png. Operator-gate via SendUserFile → cp to cover.png.
  Carousel branch reuses render/build-carousel.mjs.
- Doc/orchestration-only — no new code. Commands (24) + agents (15) unchanged.
- Version sync 2.2.0 → 2.3.0 across plugin.json, CHANGELOG, README, CLAUDE.md,
  root README + root CLAUDE.md.

Correction: spec claimed build-linkedin.mjs handles fig1-4; verified it does
NOT — it embeds only cover.png by fixed name; figures are referenced in the
draft markdown and uploaded manually. Step 7.5 documents actual behavior.

All 8 acceptance criteria met. JSON valid (14 phases); 20/20 render tests pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-28 22:49:19 +02:00
7605be9089 feat(linkedin): v2.2.0 — harden longform gates from 2nd production run
Implements the 6-change spec from the Seres-serien production
(linkedin-plugin-endringsspec.md). All acceptance criteria met.

1. Avoid-patterns (modell-/navne-katalog, completeness-over-reader-action,
   self-referential overhead openings) → longform-quality-rules.md (rule 1+3)
   + user-profile.template.md.
2. Persona gate now BLOCKING with explicit hard-fail list (primær mistet meg /
   doesn't own action / sjargong-mur / modell-navne-katalog → BLOCK;
   "JA med store forbehold" = NEI) → persona-reviewer.md + personas.template.md.
3. Fact-check declared orthogonal to narrative strength + post-cutoff
   web-search mandate + high-frequency-error checklist → fact-checker.md.
4. NEW agent voice-scrubber.md (Opus) — de-AI scrub + Norwegian-chronicle
   voice-drift; gold standard = approved Norwegian editions, NOT the English
   post corpus. Wired into newsletter.md Step 4.
5. Operator gates = render+annotate rounds (build-html.mjs to file://) as
   primary flow, AskUserQuestion as receipt/fallback → newsletter.md 2.5+3a.
6. Edition state reconciled with STATE.md (ONE-system). edition-HANDOVER
   template deleted; narrative to <serie>/STATE.md, machine data
   (factcheckLog, personaSweep, immutableRules) to edition-state.json.

Agents 14 to 15; commands unchanged (24). Backward-compatible (additive
state-shape only). Docs updated across all three levels + CHANGELOG.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-28 20:50:56 +02:00
9296d594a0 feat(linkedin): v2.1.0 — skeleton + spine-prose gates BEFORE prose in /linkedin:newsletter
Two new pipeline phases gate the spine before any prose is written:

- Step 2.5 — Skeleton + section pitch: writes <serie>/NN-skjelett.md with
  the five-line spine (premiss / problem / anbefaling / gevinst / vei
  videre) + one-line section pitches. Operator-gate (JA / REVIDER / NEI)
  AND parallel persona-skjelett-sweep must both return JA before the
  pipeline can advance.
- Step 3a — Spine prose: one paragraph per section against the gated
  skeleton, ~20-30% of final edition length. Operator-gate on whether the
  axis lands now that there is prose on it. Old Step 3 (Draft) split into
  3a (spine) and 3b (full expansion); 3b owns the multi-session
  draft-cursor logic.

Third persona-reviewer mode added: skjelett (alongside resonans + konverter).
Five spine axes scored HOLDER / TVILER / MANGLER, max 3 direction-only flags,
per-pitch section-pay-in check. Reads the skeleton + pitches only.

Pipeline grows from 11 to 13 phases; commands (24) and agents (14) counts
unchanged. Encodes the Maskinrommet writing-contract section A discipline
(premiss / problem / anbefaling / gevinst / vei videre) into the pipeline.

Empirically motivated by the Seres-serien Del 3 + Del 4 production:
a spine error caught at the skeleton stage costs 5-15 min, the same
error caught at Step 6 (resonance) costs 4-12 h, post-lock it costs a
day of cascading rework (delingstekst, hooks, carousel, doc refs).

Backward-compatible: existing editions stop at currentPhase: "research"
and now resume at Step 2.5 instead of Step 3 — an intended deterministic
improvement, never a contract break. Steps 1, 2, 4-10 bit-for-bit
unchanged. Renderers (build-html.mjs, build-linkedin.mjs) untouched.

New phase strings in edition-state.template.json _doc.phases:
- skeleton-pitch (between research and draft)
- spine-prose (between skeleton-pitch and draft)

Files changed (10):
- plugin.json: 2.0.0 -> 2.1.0
- CHANGELOG.md: new [2.1.0] entry
- CLAUDE.md (plugin + marketplace): pipeline 11->13 phases noted
- README.md (plugin + marketplace): What's New v2.1.0 + version row
- agents/persona-reviewer.md: third mode skjelett added; resonans + konverter unchanged
- commands/newsletter.md: Step 2.5 + 3a + 3b sections, resumption + pipeline tables
- config/edition-state.template.json: 11 -> 13 phases in _doc.phases
- references/longform-quality-rules.md: Rule 8 (Skjelett foer prosa)

Verification: 9/9 criteria PASS pre-commit. Phase strings consistent
across template + command + resumption table. Renderer files git-untouched.
All 11 original step headings preserved (Step 0/1/2/4-10).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-28 10:09:47 +02:00
0e85effa3b refactor(linkedin): edition-state schema + retire 2 deterministic agents to scripts (S6)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-27 20:26:14 +02:00