Fem grep-lokaliserte gjeldsposter fra STATE FUNN, pluss to anti-erosjons-lints
som gjør to av dem terminale i stedet for gjenoppdaget en gang til.
1. .claude-plugin/plugin.json description — v4.0.0-arvet release-notat med
historiske tellinger («→ 19 agents», «→ 27 commands», «→ 29 commands»)
erstattet av en beskrivelse av hva pluginen ER. Dette er teksten /plugin
viser. Ingen tellinger i teksten: de drifter, og CHANGELOG.md eier historikken.
2. CHANGELOG hadde TO «## [0.6.0]»-overskrifter (2026-07-17 = ekte, 2026-02-07 i
halen). Halens datert om til «## [0.6.0-legacy]». Sjette gangs gjenoppdagelse
— derfor lint, se punkt 6.
3. De-niche: 7 «thought leader»-treff utenfor commands/ rettet — README (mot
kommandoens egen frontmatter-ordlyd), skills/linkedin-networking (2:
FUNN-listen navnga bare :9, men :185 var samme defekt), analytics-interpreter,
analytics-tools-guide, collaborations-guide, linkedin-visual-style.
BEVISST BEHOLDT: de 5 blocklist-linjene, de to negative eksemplene
(opportunity-generation, det ironiske sitatet i video-strategy-guide), og
network-builder:562 — et WebSearch-uttrykk som treffer hvordan ANDRE
merker seg selv, allerede paret med «Creator».
4. Hardkodet «KTG» i generisk operatør-tiltale fjernet. FUNN navnga tre linjer i
language-reviewer; klassen var 21 linjer over 4 agenter, newsletter.md,
edition-state-templaten og specifics-bank sitt renderte NN-kilder.md-artefakt.
BEVISST BEHOLDT som proveniens: CHANGELOG, tests/fixtures/* (de ER KTGs
faktiske redaktørrunde, kalibreringsfasiten), adopter-notatene som ærlig
flagger KTG-interne verktøy (contract-gate, specifics-bank), «KTG
cover-directive» og «answers KTG's question» — attribusjon, ikke tiltale.
5. Step 9 persisterte tre nøkler templaten aldri deklarerte: randsoneGate (N9),
hookForge + personaSweep.deling (N24). Nå deklarert i BÅDE artikkel-shapen og
_doc. Additivt, schemaVersion forblir 1. _doc.hookForge bærer N22-invarianten
videre: en variant har målt lengde / krok-type / kroppsspor og ALDRI en
predikert engagement-score.
6. To nye ubetingede lints (Section 16aa), begge mutasjonstestet mot de FAKTISKE
filene, ikke bare mot self-testen:
- CHANGELOG-dublettvakt (+ in-memory non-vakuitets-self-test som skiller en
ekte dublett fra en bevisst «-legacy»-overskrift)
- Step 9-nøkkeldeklarasjon (shape + _doc)
Mutasjon 1 (gjeninnfør dubletten) → rødt. Mutasjon 2 (fjern hookForge fra
shapen) → rødt. Mutasjon 3 (fjern _doc.randsoneGate, fila fortsatt gyldig
JSON) → rødt. Restaurert → grønt.
Ti suiter grønne: test-runner 300 → 303 (floor 281 → 284) · trends 300 ·
analytics 202 · hooks 191 · brain 134 · editions 72 · render 63 ·
specifics-bank 45 · tests 35 · contract-gate 33.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017vMLTF7J13jJmgqwwJyivt
130 lines
4.4 KiB
TypeScript
130 lines
4.4 KiB
TypeScript
import { describe, test } from "node:test";
|
|
import assert from "node:assert/strict";
|
|
|
|
import { renderKilder } from "../src/kilder.js";
|
|
import type { LivedSpecifics, Slot } from "../src/binding.js";
|
|
import type { Bank, Specific } from "../src/types.js";
|
|
|
|
function mkSpecific(over: Partial<Specific> & { id: string }): Specific {
|
|
return {
|
|
id: over.id,
|
|
type: over.type ?? "other",
|
|
content: over.content ?? "x",
|
|
topicTags: over.topicTags ?? ["t"],
|
|
provenance: over.provenance ?? { capturedAt: "2026-06-20", source: "test" },
|
|
verification: over.verification ?? "n/a",
|
|
status: over.status ?? "active",
|
|
};
|
|
}
|
|
|
|
const mkBank = (specifics: Specific[]): Bank => ({ schemaVersion: 1, specifics });
|
|
const ls = (slots: Slot[]): LivedSpecifics => ({ slots, status: "pending" });
|
|
|
|
describe("renderKilder — the NN-kilder.md artifact", () => {
|
|
test("golden render of a mixed edition (specific-number-unverified + abstrakt + ekstern + unresolved)", () => {
|
|
const bank = mkBank([
|
|
mkSpecific({
|
|
id: "num1",
|
|
type: "number",
|
|
verification: "unverified",
|
|
content: "Sparte to dager i uka.",
|
|
topicTags: ["saksbehandling", "tid"],
|
|
provenance: { capturedAt: "2026-06-19", source: "seres/05" },
|
|
}),
|
|
]);
|
|
const livedSpecifics = ls([
|
|
{
|
|
slotId: "kp1",
|
|
kind: "key-point",
|
|
label: "Saksbehandling ble raskere",
|
|
binding: { type: "specific", specificId: "num1" },
|
|
},
|
|
{
|
|
slotId: "kp2",
|
|
kind: "key-point",
|
|
label: "Verktøyet bærer faget",
|
|
binding: { type: "abstrakt", rationale: "Poenget er prinsippet, ikke et enkelttilfelle." },
|
|
},
|
|
{
|
|
slotId: "kp3",
|
|
kind: "key-point",
|
|
label: "LinkedIn ranker på relevans",
|
|
binding: { type: "ekstern" },
|
|
},
|
|
{
|
|
slotId: "kp4",
|
|
kind: "key-point",
|
|
label: "Noe uavklart",
|
|
binding: { type: "unresolved" },
|
|
},
|
|
]);
|
|
|
|
const expected = `# Kilder — Maskinrommet (05)
|
|
|
|
> Auto-generert fra edition-state \`livedSpecifics\` + specifics-bank. Per lastbærende påstand: hva backer den.
|
|
> Levd materiale er operatørens egne ord, aldri AI-oppdiktet (retning §3). Tall er uverifiserte til fakta-sjekket (regel 6/7).
|
|
|
|
## Lastbærende påstander
|
|
|
|
### kp1 · Saksbehandling ble raskere
|
|
- **Binding:** levd materiale (number) · \`num1\` · uverifisert ⚠
|
|
- **Materiale:** «Sparte to dager i uka.»
|
|
- **Opphav:** seres/05, fanget 2026-06-19
|
|
- **Tema-tagger:** saksbehandling, tid
|
|
|
|
### kp2 · Verktøyet bærer faget
|
|
- **Binding:** abstrakt (med vilje) — Poenget er prinsippet, ikke et enkelttilfelle.
|
|
|
|
### kp3 · LinkedIn ranker på relevans
|
|
- **Binding:** ekstern kilde — (fylles av research, Step 2)
|
|
|
|
### kp4 · Noe uavklart
|
|
- **Binding:** ⛔ uavklart — må bindes før skjelett-gaten (vaghet avvises)
|
|
|
|
## Dekning
|
|
- Lastbærende slots: 4
|
|
- Levd materiale: 1
|
|
- Abstrakt (escape): 1
|
|
- Ekstern: 1
|
|
- Uavklart (BLOCK): 1
|
|
|
|
Tall som må verifiseres (regel 6/7): kp1
|
|
`;
|
|
|
|
assert.equal(renderKilder({ articleId: "05", title: "Maskinrommet", livedSpecifics, bank }), expected);
|
|
});
|
|
|
|
test("a verified named-case renders ✓ and no number-verification line", () => {
|
|
const bank = mkBank([
|
|
mkSpecific({ id: "c1", type: "named-case", verification: "verified", content: "Etat X." }),
|
|
]);
|
|
const out = renderKilder({
|
|
articleId: "01",
|
|
title: "T",
|
|
livedSpecifics: ls([
|
|
{ slotId: "kp1", kind: "key-point", label: "L", binding: { type: "specific", specificId: "c1" } },
|
|
]),
|
|
bank,
|
|
});
|
|
assert.match(out, /levd materiale \(named-case\) · `c1` · verifisert ✓/);
|
|
assert.match(out, /Tall som må verifiseres \(regel 6\/7\): ingen/);
|
|
});
|
|
|
|
test("a dangling specific id renders an explicit dangling note", () => {
|
|
const out = renderKilder({
|
|
articleId: "01",
|
|
title: "T",
|
|
livedSpecifics: ls([
|
|
{ slotId: "kp1", kind: "key-point", label: "L", binding: { type: "specific", specificId: "ghost" } },
|
|
]),
|
|
bank: mkBank([]),
|
|
});
|
|
assert.match(out, /⛔ levd materiale — id `ghost` mangler i banken \(dangling\)/);
|
|
});
|
|
|
|
test("an empty slot-map renders the 'ingen slots' placeholder, not an empty section", () => {
|
|
const out = renderKilder({ articleId: "01", title: "T", livedSpecifics: ls([]), bank: mkBank([]) });
|
|
assert.match(out, /_Ingen lastbærende slots bundet ennå\._/);
|
|
assert.match(out, /- Lastbærende slots: 0/);
|
|
});
|
|
});
|