feat(linkedin-studio): accumulation binding — ratify + §E foldIns (fix #1 slice 2)

Slice 2 av fix #1 («rettelser fester seg»): binder rules.ts til den menneskelige
sannhetskilden (skrivekontrakt §E), så en korreksjon gjort én gang håndheves for
alltid i stedet for å gjenoppdages hver utgave (diagnose-gap 2).

- ratify.ts: importerer RULES direkte (ingen skjør tekst-parsing), krever en
  bijeksjon mot §E gate-bindings-manifestet (id ↔ ref ↔ severity) + dangling-
  §-anker-sjekk. CLI: `--ratify [kontrakt]` / `npm run ratify`. Sti: arg > env
  MASKINROMMET_CONTRACT > default (relativt til modulen).
- edition-state.template: foldIns[] fangst-kø (per-artikkel proveniens,
  pending|promoted|rejected, mekanisk→rules.ts / dømmekraft→§C2).
- 12 nye tester (33 totalt), tsc rent. Live-kontrakt-integrasjonstest ratify-er
  det ekte §E-manifestet rent (10 regler bundet 1:1).

Companion: skrivekontrakt §E (JA-promoter-prosedyre + manifest) i maskinrommet.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Kjell Tore Guttormsen 2026-06-20 17:48:20 +02:00
commit ff3467de79
6 changed files with 434 additions and 6 deletions

View file

@ -11,6 +11,8 @@
*/
import { runGateFile } from "./gate.js";
import { ratifyFile, defaultContractPath } from "./ratify.js";
import type { RatifyResult } from "./ratify.js";
import type { GateResult, Violation } from "./types.js";
function printViolation(v: Violation): void {
@ -49,12 +51,39 @@ function report(r: GateResult): void {
}
}
function reportRatify(r: RatifyResult): void {
console.log("Gate-bindings-ratify (rules.ts ↔ §E-manifest)");
console.log(
`Kontrakt: ${r.contractPath ?? "(ukjent)"} · ${r.ruleCount} regler / ${r.manifestCount} manifest-rader\n`,
);
if (r.issues.length) {
console.log(`DRIFT (${r.issues.length}):`);
for (const i of r.issues) console.log(` [${i.kind}] ${i.id}\n → ${i.detail}`);
console.log("");
console.log("✗ Ute av sync — rules.ts og §E-manifestet stemmer ikke. Rett før promotering teller.");
} else {
console.log(`✓ I sync — ${r.ruleCount} regler bundet 1:1 til §E-manifestet (ref + severity stemmer).`);
}
}
function runRatify(args: string[], jsonOut: boolean): never {
const argPath = args.find((a) => !a.startsWith("--"));
const path = argPath ?? process.env.MASKINROMMET_CONTRACT ?? defaultContractPath();
const result = ratifyFile(path);
if (jsonOut) console.log(JSON.stringify(result, null, 2));
else reportRatify(result);
process.exit(result.ok ? 0 : 1);
}
function main(): void {
const args = process.argv.slice(2);
const jsonOut = args.includes("--json");
if (args.includes("--ratify")) {
runRatify(args, jsonOut);
}
const path = args.find((a) => !a.startsWith("--"));
if (!path) {
console.error("Bruk: contract-gate <utkast.md> [--json]");
console.error("Bruk: contract-gate <utkast.md> [--json] | contract-gate --ratify [kontrakt.md] [--json]");
process.exit(2);
}
const result = runGateFile(path);