feat(ms-ai-architect): G7-ankere kan baere sin egen fil — klasse-entryen for de 42 er skrevet, og #17 og #14 er begge oppfylt

Operator ratifiserte utvidelsen 2026-08-11. Beslutning #17 (én klasse-entry)
og grunnen bak #14 (ingen lokator usynlig for gaten) var begge ekte og
kolliderte i skjemaet. De kolliderer ikke lenger.

SKJEMAET: et anker er enten en ordrett STRENG, sjekket mot entry.file som foer,
eller et { file, text }-PAR sjekket mot SIN EGEN fil. Bakoverkompatibelt — alle
47 eksisterende entries er uroert. Et misformet par er en schema-feil, ikke et
anker som stille matcher ingenting.

TDD, og testene maatte skjerpes foer de var ekte: fem tester skrevet og kjoert
FOERST. To av dem PASSERTE mot gammel kode — av feil grunn: den gamle koden
stringify-er ankerobjektet inn i drift-meldingen, saa /other\.md/ traff
tilfeldig. En test som ikke kan feile beviser ingenting. Begge fikk en
diskriminator (meldingen skal IKKE navne entry.file) og feilet deretter.
5 feilende -> implementasjon -> 20/20 i fila, 1052/1052 i suiten.

idx-26an: 42 { file, text }-ankere, ett per medlem, hvert re-verifisert ordrett
OG unikt i sin egen fil med split-telling foer skriving.

MEKANISMEN ER BEVIST, IKKE ANTATT: kjoert mot den ekte validatoren med ett
medlems anker fjernet i en lesestubb — ok=false, ett anchor_drift, riktig
entry-id, og meldingen navner agent-evaluation-testing-frameworks.md. Det er
nettopp garantien #14 fantes for, naa baaret av én entry i stedet for 42.

MAALT HASARD BOKFOERT I ENTRYEN: to av de 42 ankertekstene er strenge
prefikser av andre medlemmers ankere («**Total MCP calls:** 6» i
ai-services-cost-optimization.md, «**MCP Calls:** 3» i
reserved-capacity-planning.md). Hver er unik i SIN fil, saa koen er trygg — en
kryss-fil search-and-replace er det ikke. Slett per fil, og rest-soek etter
hver edit.

Koen: 47 -> 48 entries (28 open, 20 resolved). Bokfoeringen av #17 er dermed
komplett: 1 klasse-entry + 16 individuelle = 17. Ingen korpusfil roert.
This commit is contained in:
Kjell Tore Guttormsen 2026-08-11 22:23:13 +02:00
commit 8ea54ec00e
3 changed files with 287 additions and 12 deletions

View file

@ -119,6 +119,70 @@ test('a non-array queue is rejected without throwing', () => {
assert.match(r.findings[0].message, /array/i);
});
// A class-wide entry books ONE finding that lives in many files. Booking it as a
// single-file entry was measured to hide 41 of 42 locators from the gate — the
// 795d494 failure, a locator alive only in prose while both gates go green. So an
// anchor may also be a { file, text } pair, checked against ITS OWN file.
test('a { file, text } anchor is checked against its own file, not entry.file', () => {
const e = { ...okEntry(), anchors: [{ file: 'skills/x/references/other.md', text: 'over here' }] };
const r = validateQueue(
[e],
stub({ 'skills/x/references/y.md': 'nothing matching', 'skills/x/references/other.md': 'aaa over here bbb' }),
);
assert.equal(r.ok, true);
assert.deepEqual(r.findings, []);
});
test('a { file, text } anchor absent from its own file DRIFTS, and the finding names that file', () => {
const e = { ...okEntry(), anchors: [{ file: 'skills/x/references/other.md', text: 'over here' }] };
const r = validateQueue(
[e],
stub({ 'skills/x/references/y.md': 'over here', 'skills/x/references/other.md': 'empty' }),
);
assert.equal(r.ok, false);
assert.equal(r.findings[0].kind, 'anchor_drift');
assert.match(r.findings[0].message, /other\.md/);
// Discriminating: entry.file DOES contain the text, so a message blaming y.md
// would mean the anchor was looked up in the wrong file. Without this line the
// test passes against the old code, which serialises the object into the
// message and matches /other\.md/ by accident.
assert.doesNotMatch(r.findings[0].message, /y\.md/);
});
test('string and { file, text } anchors mix in one entry, and BOTH are checked', () => {
const e = { ...okEntry(), anchors: ['the exact text', { file: 'skills/x/references/other.md', text: 'gone' }] };
const r = validateQueue(
[e],
stub({ 'skills/x/references/y.md': 'the exact text', 'skills/x/references/other.md': 'not it' }),
);
assert.equal(r.ok, false);
assert.equal(r.findings.length, 1);
assert.match(r.findings[0].message, /gone/);
// Discriminating, for the same reason: the surviving string anchor is present
// in entry.file, so the drift must be reported against other.md.
assert.match(r.findings[0].message, /other\.md/);
assert.doesNotMatch(r.findings[0].message, /y\.md/);
});
test('an unreadable per-anchor file is reported, not thrown, and names that file', () => {
const e = { ...okEntry(), anchors: [{ file: 'skills/x/references/missing.md', text: 'whatever' }] };
const r = validateQueue([e], stub({ 'skills/x/references/y.md': 'the exact text' }));
assert.equal(r.ok, false);
assert.equal(r.findings[0].kind, 'file_unreadable');
assert.match(r.findings[0].message, /missing\.md/);
});
// Without this, a typo'd anchor object would be checked against nothing and the
// entry would pass — a defect leaving the programme unnoticed, which is the one
// outcome the queue exists to prevent.
test('a malformed anchor object is a schema fault, never a silent pass', () => {
const e = { ...okEntry(), anchors: [{ file: 'skills/x/references/other.md' }] };
const r = validateQueue([e], stub({ 'skills/x/references/y.md': 'x', 'skills/x/references/other.md': 'x' }));
assert.equal(r.ok, false);
assert.equal(r.findings[0].kind, 'schema');
assert.match(r.findings[0].message, /anchor/i);
});
test('the real queue file validates against the live corpus', async () => {
const { readFileSync } = await import('node:fs');
const queue = JSON.parse(readFileSync('scripts/kb-eval/data/g7-review-queue.json', 'utf8'));