linkedin-studio/scripts/editions/README.md
Kjell Tore Guttormsen b54e450c3e feat(linkedin-studio): N11 — serie-destillat + skjelett-sjekk + specifics-bank bruks-logging [skip-docs]
Every long-form gate agent sees ONE edition, so «the reader has heard this
before» was structurally invisible — and the specifics-bank dedupe actively
encouraged re-surfacing the same material. At series cadence that is the
fastest-growing defect class. N11 makes it visible BEFORE the skeleton is
approved, at two grains:

- Series grain — new scripts/editions package: each locked edition's spent
  anecdotes/arguments/hooks are written to <serie>/linkedin/series-distillate.json
  at Step 8 lock (distil-append), and the next skeleton is checked against it at
  Step 2.5 (distil-check) with the finding folded into the annotation gate the
  operator already reads. Advisory, never blocking: a deliberate callback is a
  legitimate move, an unnoticed retread is not.
- Material grain — specifics-bank usedIn log: record-usage stamps «used in
  edition NN» on the specifics an edition actually consumed (read from the bound
  slot-map, so abstrakt/ekstern stamp nothing). At lock, so it means published;
  idempotent under a pivot re-lock. Additive-optional, schema stays v1.

Placement deviates from the plan text (${DATA}) after premise-verification:
per-series state belongs in the series root beside edition-state.json, where
Step 0 already resolves the path and no slug→path map is needed. Operator
approved. The distillate module also lands in scripts/editions now rather than
at N12, since the AC required a testable roundtrip and N12 planned that package
anyway — N12 extends it instead of creating it.

Similarity is character-trigram Jaccard, not word overlap: the plugin is
language-general and inflection (migrere/migreringen) breaks word tokens.
Calibrated on real paraphrase pairs — retellings 0.44-0.55, unrelated 0.04-0.06,
same-topic-different-story 0.24 — so the default threshold sits in the gap at
0.40. Word-Jaccard scored a shortened hook paraphrase at 0.11.

TDD (Iron Law): tests written first and verified red before implementation, for
both the new package and the bank logging.

Suites: editions 27/0 (new suite line + guard, floor 27) · specifics-bank
28 -> 45 · test-runner 163 -> 173 (Section 16r: 9 unconditional greps +
non-vacuity self-test; anti-erosion floor 146 -> 155) · trends 300/0 ·
brain 134/0 · hooks 140/0 · tests 35/0 · render 60/0. tsc --noEmit clean.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QxvWAjte7vPcF79QeSRvRJ
2026-07-25 06:29:01 +02:00

75 lines
3.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# editions — series-level memory for long-form (N11, serie-nivå-vern)
Every long-form gate agent sees **one** edition. At two editions a week the fastest-
growing defect class is not a bad edition — it is a **repeated** one: the anecdote, the
argument or the hook the reader already met three editions ago. That failure is
structurally invisible today, and the specifics-bank dedupe actively *encourages*
re-surfacing the same material.
So each locked edition leaves behind a **distillate** of the narrative units it spent,
and the Step 2.5 skeleton gate checks the next skeleton against it — **before prose
exists**, the only point where changing course is still cheap.
## Where the distillate lives
`<serie>/linkedin/series-distillate.json` — beside `edition-state.json` in the **series
root**, not in the per-user data dir. It is series-scoped state, the series folder is
already where the edition's state lives, and putting it there means it travels with the
series and needs no slug→path map.
Series-scoped is the point: this answers *"has this reader heard it?"*. Cross-series
re-use of the operator's raw material is a different grain, tracked by the
specifics-bank's `usedIn` log.
## Setup
```bash
cd scripts/editions && npm install
```
## Use
```bash
# Step 8 (lock): fold the edition's spent units into the series distillate
node --import tsx src/cli.ts distil-append \
--distillate "<serie>/linkedin/series-distillate.json" \
--series seres --extract "<abs>/extract.json"
# Step 2.5 (before prose): check the proposed skeleton against everything published
node --import tsx src/cli.ts distil-check \
--distillate "<serie>/linkedin/series-distillate.json" \
--skeleton "<abs>/skeleton.json" [--threshold 0.4] [--json]
```
`extract.json` = `{editionId, title, lockedAt, anecdotes[], arguments[], hooks[]}` — the
AI extract from the locked edition. `skeleton.json` = `{anecdotes?, arguments?, hooks?}`.
Exit codes: `0` on success — **including a REUSE verdict**. The check is advisory by
design (it reports into the annotation gate the operator already reads at Step 2.5),
unlike the binding gate's BLOCK. `2` on usage error.
## Model
- **Extraction is AI, comparison is code.** The command layer distils the units from the
locked prose; the store, the similarity and the verdict are plain deterministic code —
no model in the loop, no run-to-run variance.
- **Similarity = character-trigram Jaccard**, not word overlap. The plugin is
language-general and the languages it is used in are inflection-heavy: *migrere* /
*migreringen* are the same unit to a reader but different word tokens. Calibrated
against real paraphrase pairs (see `tests/distillate.test.ts`): retellings score
0.440.55, unrelated material 0.040.06, same-topic-different-story 0.24 — so the
default threshold sits in the gap at **0.40**. Word-Jaccard scored a shortened hook
paraphrase at 0.11 and would have let it through.
- **Units are compared within their kind.** An argument re-used as a hook is a new move,
not a retread.
- **A re-locked edition replaces its entry**, never duplicates it — `/linkedin:pivot`
re-opens and re-locks the same edition, and the reader still only met it once.
- **A missing distillate is not an error.** A series' first edition compares against
nothing and is CLEAR.
## Test
```bash
npm test # node:test, deterministic (callers supply lockedAt)
npm run build # tsc — must stay clean
```