# 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 `/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 "/linkedin/series-distillate.json" \ --series seres --extract "/extract.json" # Step 2.5 (before prose): check the proposed skeleton against everything published node --import tsx src/cli.ts distil-check \ --distillate "/linkedin/series-distillate.json" \ --skeleton "/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.44–0.55, unrelated material 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 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 ```