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:
parent
9de38c4939
commit
b30b0e208f
4 changed files with 197 additions and 13 deletions
|
|
@ -176,9 +176,24 @@ the edition left off before doing anything.
|
|||
Step 2. Do not confuse `<serie>/STATE.md` (this edition's production state)
|
||||
with the plugin's own `STATE.md` / `docs/BUILD-HANDOVER.local.md` (which govern
|
||||
building the plugin itself).
|
||||
4. **Read the voice profile** — `${LINKEDIN_STUDIO_DATA:-$HOME/.claude/linkedin-studio}/voice-samples/authentic-voice-samples.md`
|
||||
and anything else under `${LINKEDIN_STUDIO_DATA:-$HOME/.claude/linkedin-studio}/voice-samples/`. Long-form must match the
|
||||
author's voice; this is the reference for every drafting and review phase.
|
||||
4. **Read the long-form voice profile** — the **chronicle-voice-profile**
|
||||
(`${LINKEDIN_STUDIO_DATA:-$HOME/.claude/linkedin-studio}/profile/chronicle-voice-profile.md`),
|
||||
the distilled chronicle voice for THIS edition's language/format, built by
|
||||
`voice-trainer` from the published gold corpus. This is the **primary reference
|
||||
for every long-form drafting and review phase**. Short-form feed posts and
|
||||
long-form editions are different registers of the same author, so long-form must
|
||||
**not** be drafted against the English short-form corpus
|
||||
(`${LINKEDIN_STUDIO_DATA:-$HOME/.claude/linkedin-studio}/voice-samples/authentic-voice-samples.md`) —
|
||||
that corpus encodes rules (e.g. no em-dash) that are wrong for chronicle voice
|
||||
and would degrade it. Read the `${LINKEDIN_STUDIO_DATA:-$HOME/.claude/linkedin-studio}/voice-samples/`
|
||||
corpus as a **secondary tributary** (register cues), never as the primary
|
||||
long-form reference.
|
||||
**Fallback (never silent).** If `chronicle-voice-profile.md` is absent — or has
|
||||
no section for this edition's language/format — say so plainly, note the
|
||||
chronicle voice is uncalibrated, and fall back to today's behavior (draft against
|
||||
`${LINKEDIN_STUDIO_DATA:-$HOME/.claude/linkedin-studio}/voice-samples/`), while
|
||||
flagging the gap so the operator can seed the gold corpus (Step 8 auto-gull grows
|
||||
it from each locked edition).
|
||||
5. **Resolve the active personas (per-artifact).** Personas are configured **per
|
||||
edition**, not from one fixed global file. Resolve the set for
|
||||
`articles.<currentArticle>` in this order (Step 1 finalizes + persists it):
|
||||
|
|
@ -258,12 +273,12 @@ Then display a short status:
|
|||
Edition: <series title> — article <currentArticle> "<title>"
|
||||
Last completed phase: <currentPhase> (or: NEW edition)
|
||||
Resuming at: Step <N> — <step name> (from the resumption table above)
|
||||
Voice profile: loaded | MISSING
|
||||
Chronicle voice profile: loaded (<language>/<format>) | MISSING → fallback to short-form corpus
|
||||
Persona library: <N> personas loaded (active set chosen in Step 1)
|
||||
```
|
||||
|
||||
If the voice profile or persona library is missing, say so plainly and continue —
|
||||
do not fabricate either.
|
||||
If the chronicle voice profile or persona library is missing, say so plainly and
|
||||
continue on the Step-0 fallback — do not fabricate either.
|
||||
|
||||
## Step 1: Brief + calibration
|
||||
|
||||
|
|
@ -321,6 +336,16 @@ Settle these dimensions (most should come from context, not questions):
|
|||
- **Tone** — respected-peer vs. teaching-down; calibrated to the primær.
|
||||
- **Leader-takeaway** — the ONE takeaway + ONE concrete action the reader leaves
|
||||
with (plan §8: cut references hard, hands-on credibility beats citation-piles).
|
||||
- **Language** — **Resolve the review language** for this edition: detect it from
|
||||
the series brief / prior editions / the drafting language, else ask once. Persist
|
||||
it to `edition-state.json` → `language` (e.g. `"no"` for Norwegian, `"en"` for
|
||||
English) at the **Step 1.5 checkpoint**. This is not cosmetic — it gates the
|
||||
language-dependent review agents: `language-reviewer`'s Norwegian-idiom /
|
||||
«kanselli-stil» checks and `voice-scrubber`'s gold standard fire against THIS
|
||||
language, and never grade prose against Norwegian idiom when the edition is not
|
||||
Norwegian. Left unresolved, the template default `"en"` silently disables the
|
||||
Norwegian axis even for a Norwegian edition (the C-3 bug this closes) — so resolve
|
||||
it explicitly, do not assume the default.
|
||||
|
||||
Use `AskUserQuestion` only for the genuinely open dimensions (cap ≈3). Good
|
||||
candidates: which personas are in scope + which is primær; the angle if the
|
||||
|
|
@ -446,6 +471,11 @@ 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 **`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
|
||||
right language from here on. This is the write that makes the template
|
||||
doc-string's "Resolved at Step 1" claim true.
|
||||
- Write `articles.NN.livedSpecifics` (the slot-map above, `status: "pending"`).
|
||||
- Set `currentPhase: "lived-specifics"` in `edition-state.json`.
|
||||
- Write a "lived-specifics bound → next: research (scoped by the binding)" line
|
||||
|
|
@ -771,9 +801,21 @@ Typically ~20–30 % of the edition's final length.
|
|||
|
||||
**Procedure:**
|
||||
|
||||
1. **Re-read the voice profile** (`${LINKEDIN_STUDIO_DATA:-$HOME/.claude/linkedin-studio}/voice-samples/`) before writing a
|
||||
single sentence — this is the existing LTL rule and it is not optional for
|
||||
long-form. Voice match starts at the spine, not at expansion.
|
||||
1. **Pass 0 — load the voice reference + the drift negatives** before writing a
|
||||
single sentence (voice match starts at the spine, not at expansion — the
|
||||
existing LTL rule, not optional for long-form):
|
||||
- Re-read the **chronicle-voice-profile**
|
||||
(`${LINKEDIN_STUDIO_DATA:-$HOME/.claude/linkedin-studio}/profile/chronicle-voice-profile.md`)
|
||||
for this edition's language/format — the reference the spine must match
|
||||
(fall back to `${LINKEDIN_STUDIO_DATA:-$HOME/.claude/linkedin-studio}/voice-samples/`
|
||||
per Step 0 if it is missing).
|
||||
- Read the **chronicle-voice-drift-log**
|
||||
(`${LINKEDIN_STUDIO_DATA:-$HOME/.claude/linkedin-studio}/voice-samples/chronicle-voice-drift-log.md`)
|
||||
as a **negative list**: the recurring AI-tells and voice drifts `voice-scrubber`
|
||||
logged on prior editions. Do NOT reproduce them in the spine — the log is no
|
||||
longer write-only; it steers drafting BEFORE the scrub, so the Step-4/persona
|
||||
passes and the scrubber find less each edition. If the log is absent, note it
|
||||
and proceed.
|
||||
|
||||
2. **For each section in `NN-skjelett.md`, write ONE paragraph that delivers
|
||||
that section's pitch.** No examples yet, no anecdotes, no research citations
|
||||
|
|
@ -865,8 +907,11 @@ turning-points the spine already named.
|
|||
|
||||
**Procedure:**
|
||||
|
||||
1. **Re-read the voice profile** (`${LINKEDIN_STUDIO_DATA:-$HOME/.claude/linkedin-studio}/voice-samples/`) before expanding —
|
||||
the voice was set at the spine; do not lose it in expansion.
|
||||
1. **Re-read the chronicle-voice-profile**
|
||||
(`${LINKEDIN_STUDIO_DATA:-$HOME/.claude/linkedin-studio}/profile/chronicle-voice-profile.md`,
|
||||
fallback `${LINKEDIN_STUDIO_DATA:-$HOME/.claude/linkedin-studio}/voice-samples/`)
|
||||
and the drift-log negatives (Step 3a Pass 0) before expanding — the voice was set
|
||||
at the spine; do not lose it, and do not let expansion reintroduce a logged drift.
|
||||
|
||||
2. **Expand section by section, against the spine.** Each section's paragraph
|
||||
from Step 3a is the *contract* for that section: the expansion must
|
||||
|
|
@ -1707,6 +1752,19 @@ produces the editor's single delivery artifact — `POST.html`, the
|
|||
body is frozen — Step 9 may only adjust the distribution hook, never the
|
||||
locked edition text.
|
||||
|
||||
5. **Auto-gull — grow the gold corpus (A2-F2).** A locked edition is now
|
||||
published-grade voice signal, so append it to the gold corpus the chronicle
|
||||
voice profile is distilled from:
|
||||
- Record the locked body (`NN-utkast.md`) as a new record under
|
||||
`${LINKEDIN_STUDIO_DATA:-$HOME/.claude/linkedin-studio}/ingest/published/`,
|
||||
tagged **`provenance=published`** (never `ai-draft`) with this edition's
|
||||
`language` and format in its frontmatter, so `voice-trainer`'s
|
||||
chronicle-voice-profile distillation (per language/format) picks it up.
|
||||
- This closes the loop the model-collapse fence protects: the corpus the
|
||||
chronicle voice is learned from is the author's OWN locked editions, growing
|
||||
one edition at a time — never the engine's drafts. The next edition's Step 0
|
||||
therefore binds to a sharper profile without any manual corpus curation.
|
||||
|
||||
```
|
||||
LOCK → delivery.
|
||||
- Preconditions: factcheck 🔴 = none, persona resonans primær = JA (else: STOP)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue