feat(linkedin-studio): N8 — stemme-kjeden (chronicle-voice-profile + drift-logg + language-resolve + auto-gull) [skip-docs]

Bind the long-form VOICE before prose — the discipline the line already
applies to SOURCES (validate-binding), its strongest link.

- C-1/A2-F1: voice-trainer distils a chronicle-voice-profile from the
  published gold corpus (per language/format); newsletter Step 0/3a/3b bind
  drafting to it, with an explicit never-silent fallback to the short-form
  corpus (which the engine itself calls degrading for chronicle voice).
- C-2: Step 3a Pass 0 READS the chronicle-voice-drift-log as a negative list
  (it was append-only, never consumed); Step 3b re-reads it in expansion.
- C-3: Step 1 resolves + persists the top-level edition-state.language, making
  the template doc-string's "Resolved at Step 1" claim true; the Norwegian
  review axis (language-reviewer / voice-scrubber) now fires when the edition
  IS Norwegian instead of being silently off by default. Step 6.5 already
  consumed the field — resolving it closes the axis end-to-end.
- A2-F2: Step 8 lock appends the locked edition to ingest/published
  (provenance=published), so the gold corpus the profile distils from grows
  one edition at a time — never the engine's drafts.

Domain-general throughout (languages/formats from the user's own corpus).
test-runner 140 -> 146 (Section 16o: 6 unconditional voice-chain greps +
non-vacuity self-test; anti-erosion floor 123 -> 129). All six suites green
(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:
Kjell Tore Guttormsen 2026-07-23 22:58:38 +02:00
commit b30b0e208f
4 changed files with 197 additions and 13 deletions

View file

@ -16,6 +16,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- **`arcs` CLI verb + `/linkedin:trends --demand` mode** — render the §4 arc map (`node --import tsx src/cli.ts arcs`), reachable via the command's new demand mode (delegates to `demand-spotter`, keeps commands at 30). The morning brief also shows the per-candidate demand signal alongside the existing reader fields.
- Trends suite 276 → 300; test-runner 139 → 140; `tsc --noEmit` clean.
### Added — voice chain: bind voice BEFORE prose (N8 / C-1, C-2, C-3, A2-F1, A2-F2)
- **`chronicle-voice-profile` artifact (C-1/A2-F1)** — `voice-trainer` now distils a dedicated long-form voice profile (`${DATA}/profile/chronicle-voice-profile.md`) from the **published gold corpus only** (`ingest/published/`, `provenance=published`), one section per `(language, format)` pair present in the corpus — never from the English short-form `authentic-voice-samples.md` the engine itself calls degrading for chronicle voice, never from `provenance=ai-draft`. `/linkedin:newsletter` binds long-form drafting (Step 0 / 3a / 3b) to THAT profile as the primary reference, with an **explicit, never-silent fallback** to the short-form corpus when it is absent.
- **Drift log is READ, not just written (C-2)** — Step 3a gains a **Pass 0**: read `chronicle-voice-drift-log.md` as a *negative list* of the recurring AI-tells/drifts `voice-scrubber` logged on prior editions, so drafting avoids them BEFORE the scrub (the log was append-only, never consumed); Step 3b re-reads it during expansion.
- **Review language resolved + persisted at Step 1 (C-3)** — the calibration now **resolves the review `language`** (detect/ask) and persists the top-level `edition-state.language` at the Step 1.5 checkpoint, making the template's "Resolved at Step 1" doc-string true. Previously the default `"en"` was never set, so `language-reviewer`'s Norwegian checks and `voice-scrubber`'s gold standard silently never fired even for a Norwegian edition; Step 6.5 already consumed the field, so resolving it closes the axis end-to-end.
- **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).
### 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).