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:
parent
1c07afd056
commit
8ea54ec00e
3 changed files with 287 additions and 12 deletions
|
|
@ -23,6 +23,9 @@ export const ENTRY_STATES = new Set(['open', 'resolved']);
|
|||
|
||||
const REQUIRED = ['id', 'file', 'class', 'status', 'raised', 'summary', 'evidence', 'anchors'];
|
||||
|
||||
const isAnchor = (a) =>
|
||||
typeof a === 'string' || (a !== null && typeof a === 'object' && typeof a.file === 'string' && typeof a.text === 'string');
|
||||
|
||||
/**
|
||||
* @param {unknown} entries queue entries
|
||||
* @param {(path: string) => string} readFile
|
||||
|
|
@ -78,21 +81,50 @@ export function validateQueue(entries, readFile) {
|
|||
continue;
|
||||
}
|
||||
|
||||
let text;
|
||||
try {
|
||||
text = readFile(entry.file);
|
||||
} catch {
|
||||
findings.push({ id, kind: 'file_unreadable', message: `cannot read ${entry.file}` });
|
||||
// An anchor is either a verbatim string, checked against entry.file, or a
|
||||
// { file, text } pair checked against ITS OWN file. The pair form exists for
|
||||
// class-wide entries — one record for a defect that lives in many files.
|
||||
// Measured 2026-08-11: booking such a class as a single-file entry hid 41 of
|
||||
// 42 locators from this check, which is the 795d494 failure, a locator alive
|
||||
// only in prose while both gates go green. A malformed pair is a schema fault
|
||||
// rather than an anchor that quietly matches nothing.
|
||||
const malformed = entry.anchors.filter((a) => !isAnchor(a));
|
||||
if (malformed.length > 0) {
|
||||
findings.push({
|
||||
id,
|
||||
kind: 'schema',
|
||||
message: `anchor must be a verbatim string or a { file, text } pair: ${malformed
|
||||
.map((m) => JSON.stringify(m))
|
||||
.join(', ')}`,
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
const gone = entry.anchors.filter((a) => !text.includes(a));
|
||||
if (gone.length > 0) {
|
||||
findings.push({
|
||||
id,
|
||||
kind: 'anchor_drift',
|
||||
message: `anchor no longer occurs in ${entry.file}: ${gone.map((g) => JSON.stringify(g)).join(', ')}`,
|
||||
});
|
||||
const byFile = new Map();
|
||||
for (const a of entry.anchors) {
|
||||
const target = typeof a === 'string' ? entry.file : a.file;
|
||||
const text = typeof a === 'string' ? a : a.text;
|
||||
if (!byFile.has(target)) byFile.set(target, []);
|
||||
byFile.get(target).push(text);
|
||||
}
|
||||
|
||||
for (const [target, texts] of byFile) {
|
||||
let text;
|
||||
try {
|
||||
text = readFile(target);
|
||||
} catch {
|
||||
findings.push({ id, kind: 'file_unreadable', message: `cannot read ${target}` });
|
||||
continue;
|
||||
}
|
||||
|
||||
const gone = texts.filter((t) => !text.includes(t));
|
||||
if (gone.length > 0) {
|
||||
findings.push({
|
||||
id,
|
||||
kind: 'anchor_drift',
|
||||
message: `anchor no longer occurs in ${target}: ${gone.map((g) => JSON.stringify(g)).join(', ')}`,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue