feat(ms-ai-architect): G7 lukket som mekanisme — navngitt review-kø (form b), valgt på måling
Operatør-ratifisert 2026-08-03. G7 var gapet for korreksjoner som er RIKTIGE men større enn O2-konvolutten (én lokator + kun-sletting). Uten eier falt de stille ut: O2-triagen avviser dem, O3 dekker dem ikke, og review-sporet hadde ingen inngangskø. Formvalget ble tatt på §9.6-målingen, ikke på preferanse: - 2 av 4 anvendte subtraksjoner etterlot en rest -> rester er delete-only- konvoluttens NORMALE biprodukt, ikke et unntak. - 2 av 5 medlemmer (idx 17, 33) er ERSTATNINGER, ikke fler-lokator. En delete-orientert O4-klasse med egen retur-kontrakt ville ikke fikset dem — altså feil dimensjonert mot evidensen. Køen absorberer begge klasser. Artefakter (TDD, test-først): - data/g7-review-queue.json — 6 entries, tracked - lib/g7-queue.mjs — validateQueue, lukkede vokabular - check-g7-queue.mjs — exit 1 ved drift eller skjemafeil - tests/kb-eval/test-g7-queue.test.mjs — 15 tester Kontrakten: ankere er ORDRETTE strenger, aldri linjenummer (line != real_line i 9 av 17 R11-records). En åpen entry hvis anker slutter å matche gir anchor_drift og exit 1 — den kan ikke falle stille ut, som er hele hensikten. En resolved entry MÅ føre resolution, ellers er "resolved" ikke til å skille fra "stille droppet". Ingenting i køen er maskin-anvendbart per definisjon. Innhold: 5 åpne (26, 27, 33, 36, 18), 1 lukket (17). idx 27 kom hit ved å falle ut av O2 på cond 3; idx 26 ved operatørens avvisning av delvis fiks. Suite 1047/1047. [skip-docs]
This commit is contained in:
parent
c569bdc10e
commit
59c6c280b1
5 changed files with 379 additions and 1 deletions
128
tests/kb-eval/test-g7-queue.test.mjs
Normal file
128
tests/kb-eval/test-g7-queue.test.mjs
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
import { test } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { validateQueue, ENTRY_CLASSES, ENTRY_STATES } from '../../scripts/kb-eval/lib/g7-queue.mjs';
|
||||
|
||||
// A queue entry is the ONLY thing standing between a real defect and silent loss,
|
||||
// so the checks here are deliberately unforgiving about shape and about anchors
|
||||
// that no longer exist in the corpus.
|
||||
|
||||
const okEntry = () => ({
|
||||
id: 'idx-99',
|
||||
file: 'skills/x/references/y.md',
|
||||
class: 'multi-locator',
|
||||
status: 'open',
|
||||
raised: '2026-08-03',
|
||||
summary: 'Something the file asserts in two places.',
|
||||
evidence: 'docs/r11-pilot-results.md §9.6',
|
||||
anchors: ['the exact text'],
|
||||
});
|
||||
|
||||
const stub = (contents) => (p) => {
|
||||
if (!(p in contents)) throw new Error(`ENOENT ${p}`);
|
||||
return contents[p];
|
||||
};
|
||||
|
||||
test('a well-formed open entry whose anchor is present passes', () => {
|
||||
const r = validateQueue([okEntry()], stub({ 'skills/x/references/y.md': 'aaa the exact text bbb' }));
|
||||
assert.equal(r.ok, true);
|
||||
assert.deepEqual(r.findings, []);
|
||||
});
|
||||
|
||||
test('vocabularies are closed', () => {
|
||||
assert.deepEqual([...ENTRY_CLASSES].sort(), ['multi-locator', 'replacement']);
|
||||
assert.deepEqual([...ENTRY_STATES].sort(), ['open', 'resolved']);
|
||||
});
|
||||
|
||||
test('unknown class is rejected', () => {
|
||||
const e = { ...okEntry(), class: 'deletion' };
|
||||
const r = validateQueue([e], stub({ 'skills/x/references/y.md': 'the exact text' }));
|
||||
assert.equal(r.ok, false);
|
||||
assert.match(r.findings[0].message, /class/);
|
||||
});
|
||||
|
||||
test('unknown status is rejected', () => {
|
||||
const e = { ...okEntry(), status: 'wontfix' };
|
||||
const r = validateQueue([e], stub({ 'skills/x/references/y.md': 'the exact text' }));
|
||||
assert.equal(r.ok, false);
|
||||
assert.match(r.findings[0].message, /status/);
|
||||
});
|
||||
|
||||
test('a missing required field is reported by name', () => {
|
||||
const e = okEntry();
|
||||
delete e.evidence;
|
||||
const r = validateQueue([e], stub({ 'skills/x/references/y.md': 'the exact text' }));
|
||||
assert.equal(r.ok, false);
|
||||
assert.match(r.findings[0].message, /evidence/);
|
||||
});
|
||||
|
||||
test('an open entry MUST carry at least one anchor', () => {
|
||||
const e = { ...okEntry(), anchors: [] };
|
||||
const r = validateQueue([e], stub({ 'skills/x/references/y.md': 'the exact text' }));
|
||||
assert.equal(r.ok, false);
|
||||
assert.match(r.findings[0].message, /anchor/i);
|
||||
});
|
||||
|
||||
// The point of the whole mechanism: a defect must not be able to leave the queue
|
||||
// just because someone edited the file around it.
|
||||
test('an open entry whose anchor no longer occurs is a DRIFT finding, not a pass', () => {
|
||||
const r = validateQueue([okEntry()], stub({ 'skills/x/references/y.md': 'nothing matching here' }));
|
||||
assert.equal(r.ok, false);
|
||||
assert.equal(r.findings[0].kind, 'anchor_drift');
|
||||
assert.match(r.findings[0].message, /the exact text/);
|
||||
});
|
||||
|
||||
test('every anchor is checked, not just the first', () => {
|
||||
const e = { ...okEntry(), anchors: ['present', 'absent'] };
|
||||
const r = validateQueue([e], stub({ 'skills/x/references/y.md': 'present only' }));
|
||||
assert.equal(r.ok, false);
|
||||
assert.equal(r.findings.length, 1);
|
||||
assert.match(r.findings[0].message, /absent/);
|
||||
});
|
||||
|
||||
test('a missing file is reported rather than thrown', () => {
|
||||
const r = validateQueue([okEntry()], stub({}));
|
||||
assert.equal(r.ok, false);
|
||||
assert.equal(r.findings[0].kind, 'file_unreadable');
|
||||
});
|
||||
|
||||
test('a resolved entry MUST record how it was resolved', () => {
|
||||
const e = { ...okEntry(), status: 'resolved' };
|
||||
const r = validateQueue([e], stub({ 'skills/x/references/y.md': 'whatever' }));
|
||||
assert.equal(r.ok, false);
|
||||
assert.match(r.findings[0].message, /resolution/);
|
||||
});
|
||||
|
||||
test('a resolved entry is exempt from anchor checking', () => {
|
||||
const e = { ...okEntry(), status: 'resolved', resolution: 'colon -> period, ratified 2026-08-03' };
|
||||
const r = validateQueue([e], stub({ 'skills/x/references/y.md': 'anchor is long gone' }));
|
||||
assert.equal(r.ok, true);
|
||||
});
|
||||
|
||||
test('duplicate ids are rejected', () => {
|
||||
const r = validateQueue([okEntry(), okEntry()], stub({ 'skills/x/references/y.md': 'the exact text' }));
|
||||
assert.equal(r.ok, false);
|
||||
assert.match(r.findings[0].message, /duplicate/i);
|
||||
});
|
||||
|
||||
test('findings from several entries are all reported', () => {
|
||||
const a = { ...okEntry(), id: 'idx-1' };
|
||||
const b = { ...okEntry(), id: 'idx-2', anchors: ['gone'] };
|
||||
const r = validateQueue([a, b], stub({ 'skills/x/references/y.md': 'the exact text' }));
|
||||
assert.equal(r.ok, false);
|
||||
assert.equal(r.findings.length, 1);
|
||||
assert.equal(r.findings[0].id, 'idx-2');
|
||||
});
|
||||
|
||||
test('a non-array queue is rejected without throwing', () => {
|
||||
const r = validateQueue({ entries: [] }, stub({}));
|
||||
assert.equal(r.ok, false);
|
||||
assert.match(r.findings[0].message, /array/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'));
|
||||
const r = validateQueue(queue.entries, (p) => readFileSync(p, 'utf8'));
|
||||
assert.deepEqual(r.findings, [], `G7 queue drifted: ${JSON.stringify(r.findings, null, 2)}`);
|
||||
assert.equal(r.ok, true);
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue