feat(linkedin-studio): N14 — foldIns-fangst + Step 11 retro + background-headless + språkregel-akkumulering [skip-docs]
Lukker sløyfen pluginen manglet: en rettelse operatøren gjør i utgave N håndheves i N+1 i stedet for å bli gjenoppdaget. Maskineriet fantes (foldIns-skjema + promote→ratify siden fix #1); det som manglet var wiring — ingenting skrev til køen og ingen fase tømte den. - Fangst (A2-F7): Steps 2.5/3a/5.5/6.5 appender rettelsen ordrett med trigger, decision "pending". Ubetinget og bevisst dum — å avgjøre ved fangst om noe "fortjener" en regel er nettopp slik køen holder seg tom og sløyfen dør. - Step 11 retro (A2-F8), ≤5 min ETTER scheduling: promoter køen med eksplisitt JA/NEI (mekanisk → atomisk contract-gate-promotering, teller kun med grønn --ratify; dømmekraft → operatørens språkregelfil; NEI → rejected, beholdes), effort-oppsummering fra MÅLT phaseLog (aldri re-estimert), og ÉN friksjons-spørring som besvares tilbake til operatøren. Faser 18 → 19; resumption-tabellen ruter scheduling → Step 11, retro → complete. - articles.NN.retro: additiv-valgfri (default null), schemaVersion forblir 1. - Background-headless (A2-F6): --background kjører pakken i en bakgrunnsagent som skriver rapporten til disk; drafting-sesjonen leser fila. Samme isolasjon som fersk sesjon, uten copy-paste-sømmen. Inline fan-out = eksplisitt fallback. - Språkregler (C-10): ${DATA}/language-rules/<lang>.md (opt-in, template). Leses TO ganger — av language-reviewer (fanger) og av Step 4 (forebygger). Shippet banliste forblir baseline; brukerfila utvider. - references/fold-in-loop.md (A2-F9): loopen dokumentert domene-generelt in-tree, så en adopter uten ekstern skrivekontrakt har hele sløyfen. Refs 28 → 29. Suiter (alle grønne): test-runner 197 → 217 (Section 16u: 18 ubetingede greps + non-vacuity self-test; fase-sveip 16 → 17 faser; floor 179 → 198) · hooks 174 · trends 300 · brain 134 · editions 72 · specifics-bank 45 · contract-gate 33 · tests 35 · render 60. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QxvWAjte7vPcF79QeSRvRJ
This commit is contained in:
parent
677eab9294
commit
f0532dce3f
9 changed files with 711 additions and 30 deletions
166
references/fold-in-loop.md
Normal file
166
references/fold-in-loop.md
Normal file
|
|
@ -0,0 +1,166 @@
|
|||
# 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/<lang>.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)
|
||||
Loading…
Add table
Add a link
Reference in a new issue