Commit graph

4 commits

Author SHA1 Message Date
8d2968a482 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
305b99c0e4 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
e69ea1f4c9 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
b6bb61246b refactor(linkedin)!: rename plugin linkedin-thought-leadership → linkedin-studio (v3.0.0)
BREAKING CHANGE: the marketplace slug, the agent namespace
(linkedin-studio:<agent>), and the runtime state-file path
(~/.claude/linkedin-studio.local.md) all change. Reinstall required;
existing state migrated in place (post metrics, streak, history preserved).
The /linkedin:* commands are unchanged — the command namespace is set
per-command in frontmatter and was always independent of the plugin slug.
Functionality is byte-identical to v2.4.0; this release is pure identity.

- dir + manifests: plugins/linkedin-studio + plugin.json + root marketplace.json
- agent namespace updated in commands/newsletter.md (only functional invoker)
- state path updated in 4 hook scripts + topic-rotation prompt + state template
- catch-all skill dir renamed skills/linkedin-studio (5 functional skills unchanged)
- docs + version bump to 3.0.0 across README badge, CHANGELOG, root README/CLAUDE.md
- historical records (CHANGELOG past entries, docs/ build artifacts,
  config-audit v5.0.0 snapshots) intentionally retain the old slug

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-29 11:32:02 +02:00