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:
Kjell Tore Guttormsen 2026-07-25 12:39:16 +02:00
commit f0532dce3f
9 changed files with 711 additions and 30 deletions

View file

@ -59,6 +59,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- **D-5: the shipped schedule is marked for what it is**`references/scheduling-strategy.md` now carries the same epistemic standard as the algorithm reference (**confidence: low / directional**, practitioner heuristic, no primary source) and defers to the operator's own grid, tested against their own analytics. The tables are a starting point, not a finding about anyone's audience.
- Suites: hooks 140 → 174 (25 slot-core + 9 session-start tests); test-runner 184 → 197 (Section 16t: 13 unconditional greps incl. a de-niche check that no publishing time is hardcoded in the slot path and no operator grid is committed, + non-vacuity self-test; anti-erosion floor 166 → 179).
### Added — the fold-in loop: a correction made once is enforced from then on (N14 / A2-F6, A2-F7, A2-F8, A2-F9, C-10)
- **Capture is finally wired (A2-F7)** — the `articles.NN.foldIns[]` queue and the promote→ratify machinery have shipped since fix #1, but **nothing ever wrote to the queue and no step ever emptied it**. All four steps where an operator's judgment enters the text — skeleton annotations (2.5), spine annotations (3a), editorial flags (5.5), cold-review flags (6.5) — now append the correction near-verbatim with its trigger, `decision: "pending"`. Capture is unconditional and deliberately dumb: deciding at capture time whether a correction "deserves" a rule is exactly how the queue stays empty and the loop dies. Edition-specific fixes (a wrong number) are explicitly *not* rules.
- **New Step 11 — retro (A2-F8), ≤5 min, AFTER scheduling** — three fixed jobs, one operator pass. (1) **Promote**: every pending fold-in gets an explicit JA/NEI; mechanical → an atomic `scripts/contract-gate` promotion (rule + contract row + manifest row) that only counts when `--ratify` is green, judgment → the operator's language-rules file; NEI marks the row `rejected` and **keeps** it. (2) **Effort summary**: read straight from the measured `articles.NN.phaseLog` (N12) — transitions, *recurred* phases (where the rounds actually went), lead time — never re-estimated, and "not measured" when the log is absent. (3) **One** process-friction question, answered back to the operator: the plugin never writes into the operator's own notes or register. It runs after delivery so nothing about shipping waits on it, and it is the last thing the edition does: the pipeline is not complete until the loop is closed. Phases 18 → 19; the resumption table routes `scheduling` → Step 11 and `retro` → complete, so an aborted session cannot drop the queue.
- **`articles.NN.retro` in edition-state** — additive-optional (default `null`), so schemaVersion stays **1** and pre-N14 editions load unchanged.
- **Background headless review (A2-F6)**`/linkedin:headless-review --background` runs the cold package in a background agent with its own context window that writes the consolidated report to disk; the drafting session then *reads the file*. Maximum independence used to cost a manual session switch plus pasting the report back — the seam is gone, and the isolation is identical (the background agent gets only the cold-contract inputs). The inline fan-out remains the explicit fallback; the isolation is never silently downgraded.
- **Language rules accumulate in a user file (C-10)**`${LINKEDIN_STUDIO_DATA:-$HOME/.claude/linkedin-studio}/language-rules/<lang>.md` (opt-in, template `config/language-rules.template.md`) holds the judgment-class corrections Step 11 promoted. It is read **twice**: by `language-reviewer` at Step 6.5 (catch) and by Step 4 while the draft is still being cleaned (**prevent**) — a rule that only ever catches is worth half a rule. The shipped ban-list (`longform-quality-rules.md` rule 3) stays the baseline and is never edited by the loop; the user file extends it, and its *Deliberate exceptions* section is honoured. Absent file ⇒ baseline alone, silently.
- **`references/fold-in-loop.md` (A2-F9)** — the capture → classify → promote → enforce loop documented **domain-generally and in-tree**, so an adopter with no external writing contract has the whole loop (that absence "is not a gap" — the same mirror rule `editorial-reviewer` uses). Names the other four accumulation silos (specifics-bank, brain, voice drift log, A/B learnings) that Step 11 surfaces together, because a retro that empties one queue teaches the operator the others are empty too. references 28 → 29.
- Suites: test-runner 197 → 217 (Section 16u: 18 unconditional greps + a non-vacuity self-test on the capture predicate; the phase-coverage sweep now spans **17** phases; anti-erosion floor 179 → 198). All nine suites green.
### Built feedback (awaiting consumer-side proof)
- **MR-F9** (demand-sweep «innenfra og ut») — built; the (a)/(b)/(c) evidence gate is a runtime demonstration, proven consumer-side (plugin agents don't resolve in the dev repo).