# The fold-in loop — how a correction made once gets enforced forever **Capture → Classify → Promote → Enforce.** This is the plugin's answer to the single most expensive failure mode in a long-running writing practice: you correct the same thing in edition after edition, because the correction lives in a chat transcript that is gone by the next session. The loop makes a correction *stick*. You fix something in edition N; the machine enforces it in edition N+1 without you remembering it existed. > **Domain-general by construction.** Nothing here is specific to a topic, a > language, or an author. The stages and the artifacts they write are the same > for any adopter. Where this document says "the contract", read "whatever > document holds your writing rules" — the plugin ships one > (`references/longform-quality-rules.md`) and it is enough. --- ## The four stages | Stage | Where it runs | What it writes | Who decides | |-------|---------------|----------------|-------------| | **Capture** | `/linkedin:newsletter` Steps 2.5, 3a, 5.5, 6.5 — every step where the operator corrects prose | one row in `articles.NN.foldIns[]`, `decision: "pending"` | nobody — capture is unconditional | | **Classify** | `/linkedin:newsletter` Step 11 (retro) | `classification` on the row: `mechanical-block` / `mechanical-warn` / `judgment` | the command layer proposes, the operator confirms | | **Promote** | `/linkedin:newsletter` Step 11 (retro) | mechanical → a `scripts/contract-gate` rule; judgment → a line in the language-rules file / the craft checklist | **the operator** (JA/NEI per row) | | **Enforce** | Step 4.5 (contract-gate), Step 4 + `language-reviewer` (language rules) | nothing — it reads | deterministic; no model in the loop for the mechanical half | ### 1. Capture — never lose a correction The four fold-in steps are the four places where an operator's judgment enters the text: skeleton annotations (2.5), spine annotations (3a), editorial flags (5.5), cold-review flags (6.5). Each appends the correction **near-verbatim** with the trigger that surfaced it, and leaves it `pending`. Capture is deliberately dumb and unconditional. Deciding *at capture time* whether a correction is "worth" a rule is how the queue stays empty and the loop dies — a five-second append is cheaper than re-discovering the rule in three editions' time. Capture only what the operator actually corrected; do not invent rules the operator did not ask for. Not every correction becomes a rule. A fix that is true only of *this* edition's facts ("that number is 4.2, not 4.4") is edition-specific and does not belong in the queue; a fix that would be true of the *next* edition too ("never open with a meta-sentence about what the text will do") does. ### 2. Classify — mechanical or judgment Two kinds of correction, two different permanent homes: - **Mechanical** — provable by string, regex, or count with ~zero false positives ("this exact phrase is banned", "no more than one em-dash per 50 words"). It becomes a **gate rule** and is enforced before a draft ever reaches a human. - **Judgment** — real editorial discretion ("the conclusion overloads; it needs one grip, not four"). No gate can prove it. It becomes a **checklist line** read by the craft reviewer and by the drafting step. Misclassifying a judgment call as mechanical is the expensive error: a gate rule with false positives trains the operator to ignore the gate, and a gate nobody trusts enforces nothing. **When unsure, classify as judgment.** ### 3. Promote — the operator gates, atomically Each pending row gets an explicit **JA / NEI** from the operator. NEI marks the row `rejected` — kept for traceability, never deleted, because "we considered this and said no" is itself worth not re-discovering. JA promotes the row **atomically**: the rule entry, the contract row that documents it, and the manifest row that binds them are written together, and the fold-in row is marked `promoted` with the resulting rule id. A rule that exists in code but not in the contract is a gate enforcing something undocumented; a contract row with no rule is a rule nobody enforces. Both are drift, and both are what the ratify check exists to refuse: ```bash cd "${CLAUDE_PLUGIN_ROOT}/scripts/contract-gate" && node --import tsx src/cli.ts --ratify ``` **Green ratify is what makes the promotion count.** If it is red, the promotion is not finished. ### 4. Enforce — the loop's whole point - Mechanical rules run at **Step 4.5** (contract-gate) on the full draft, before the costly AI gates. A violation dies there. - Judgment rules are read at **Step 4** (drafting/consistency) and by **`language-reviewer`** at Step 6.5 — so they prevent the defect as well as catch it. A rule that only ever catches is worth half a rule. Language rules accumulate in a **user-owned, per-language file** under the per-user data dir (`${LINKEDIN_STUDIO_DATA:-$HOME/.claude/linkedin-studio}/language-rules/.md`, template: `config/language-rules.template.md`). The shipped ban-list in `references/longform-quality-rules.md` rule 3 stays the **baseline** and is never edited by the loop; the user file **extends** it. Absent that file, the baseline applies alone and nothing warns. --- ## Adopting the loop without a private writing contract The author's own setup keeps an upstream writing contract with a rule manifest outside this plugin. **You do not need one.** The plugin ships everything the loop requires: - rules as data — `scripts/contract-gate/src/rules.ts` - the baseline craft rules — `references/longform-quality-rules.md` - the accumulating language file — `config/language-rules.template.md` - the in-tree craft checklists — `agents/editorial-reviewer.md`, `agents/language-reviewer.md` If you run the plugin without an external contract, `--ratify` has nothing to bind against and says so plainly; promote to `rules.ts` + the language-rules file and treat those as your contract. **That absence is not a gap** — it is the default configuration. (Same mirror rule as `agents/editorial-reviewer.md`: an upstream contract, when it exists, is the source of truth and the in-tree copy must not drift from it; when it does not exist, the in-tree copy *is* the contract.) --- ## The other accumulation silos The fold-in loop is one of five places the plugin accumulates. Step 11 is where they are all in view at once, because a retro that only empties one queue leaves the operator believing the others are empty too: | Silo | What accumulates | Written by | |------|------------------|------------| | **fold-in loop** (this doc) | corrections → enforced rules | Steps 2.5/3a/5.5/6.5 → Step 11 | | **specifics-bank** (`scripts/specifics-bank`) | the operator's lived specifics + where each was used | Step 1.5 / Step 8 `record-usage` | | **brain** (`scripts/brain`, `consolidate`) | published-only knowledge from locked editions | Step 8 | | **voice** (`voice-trainer`, drift log) | the chronicle voice + its recurring drifts | Step 8 auto-gold / `voice-scrubber` | | **A/B learnings** (`/linkedin:ab-test`) | which variant actually won | `/linkedin:ab-test` adopt verdicts | Each is deterministic, each is read by an upstream phase, and each is independently useless if never read. **A silo nothing reads is a diary, not a loop.** --- ## Anti-patterns - **Capturing nothing because the queue "should stay clean".** An empty queue after a full edition means capture is broken, not that the edition was perfect. - **Promoting everything.** A rule set that grows every edition without ever rejecting becomes noise, and the gate loses the operator's trust. - **Promoting without ratify.** A promotion that leaves rules and contract out of sync has not happened, however good it felt. - **Emptying the queue silently.** The retro promotes with an explicit verdict per row; a queue drained by the model's own judgment is the operator's authority quietly taken away. - **Writing into the operator's own notes.** The retro asks one friction question and hands the answer back. Their register is theirs. --- ## Related - `${CLAUDE_PLUGIN_ROOT}/scripts/contract-gate/README.md` — the deterministic mechanical gate + the ratify binding - `${CLAUDE_PLUGIN_ROOT}/references/longform-quality-rules.md` — the shipped baseline rules (rule 3 = the ban-list the language file extends) - `${CLAUDE_PLUGIN_ROOT}/config/edition-state.template.json` — `articles.NN.foldIns` + `articles.NN.retro` schema - `${CLAUDE_PLUGIN_ROOT}/commands/newsletter.md` — Steps 2.5/3a/5.5/6.5 (capture) and Step 11 (classify → promote)