fix(ms-ai-architect): R11 — første korpus-edit; 4 ratifiserte O2-subtraksjoner anvendt (idx 17, 19, 33, 14-redusert)
Operatør-ratifisert 2026-08-03. Sletter udokumenterte påstander fra tre KB-filer: score-threshold-båndene og 'Automatic indexing av vectors' (rag-caching-optimization), to ikke-støttede AISPM-attribusjoner (ai-threat-modeling-stride), og SharePoint som feedback-lagring (feedback-loops). idx 14 er den REDUSERTE subtraksjonen — 'Automatically' beholdes, siden linje 566 hevder automatikk. Ikke ratifisert og ikke anvendt: idx 26, 27, 36, 18. Driver ankrer på file_text_verbatim, aldri linjenummer, og avbryter uten å skrive ved tvetydig anker, ikke-sletting eller ny ordform. 11 tester. Suite 1032/1032. [skip-docs]
This commit is contained in:
parent
4042d0b94a
commit
957ebef6da
5 changed files with 231 additions and 7 deletions
149
scripts/kb-eval/apply-o2-ratified.mjs
Normal file
149
scripts/kb-eval/apply-o2-ratified.mjs
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
#!/usr/bin/env node
|
||||
// apply-o2-ratified.mjs — R11 §9.4. Applies the O2 subtractions the operator
|
||||
// ratified 2026-08-03, and only those.
|
||||
//
|
||||
// Ratified: idx 17, 19, 33 as attested; idx 14 as the REDUCED subtraction
|
||||
// (`/ SharePoint` only). Deliberately NOT ratified and therefore absent from
|
||||
// RATIFIED: idx 26 (renumbering artifact unresolved), idx 27 (cond 3 still
|
||||
// `human_must_confirm`), idx 36 (needs the out-of-envelope companion edit at
|
||||
// line 310, now owned by gap G7), idx 18 (no reduction exists — also G7).
|
||||
//
|
||||
// Every string comes from the tracked evidence in data/r11-o2-returns/, never
|
||||
// from transcription. The one amendment (idx 14) is expressed as a derivation
|
||||
// over the attested verbatim and asserts its own effect, so a drifted record
|
||||
// aborts rather than silently writing something else.
|
||||
//
|
||||
// Anchoring is on `file_text_verbatim`, NEVER on a line number: `line` differs
|
||||
// from `real_line` in 9 of 17 records, and idx 17 shifts idx 19's lines in the
|
||||
// file they share. The verbatim must occur EXACTLY once or the run aborts.
|
||||
//
|
||||
// Recovery contract: writes are crash-safe (atomicWriteSync tmp+rename — a reader
|
||||
// sees the old file or the new one, never a partial). An interrupted run is
|
||||
// recovered by re-running: an already-applied edit no longer finds its verbatim,
|
||||
// which aborts the run, writing nothing, rather than corrupting the file.
|
||||
//
|
||||
// Usage: node scripts/kb-eval/apply-o2-ratified.mjs [--dry]
|
||||
import { readFileSync, readdirSync, realpathSync } from 'node:fs';
|
||||
import { join, dirname } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
import { isDeletionOnly, novelWordForms } from './lib/o2-return-check.mjs';
|
||||
import { atomicWriteSync } from '../kb-update/lib/atomic-write.mjs';
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
const PLUGIN_ROOT = join(__dirname, '..', '..');
|
||||
const RETURNS = join(PLUGIN_ROOT, 'scripts/kb-eval/data/r11-o2-returns');
|
||||
|
||||
/**
|
||||
* The ratified reduction for idx 14: delete only the `/ SharePoint` alternative.
|
||||
* `Automatically` MUST survive — line 566 of the same file restates automaticity
|
||||
* in Norwegian, so deleting it would leave a remainder the file contradicts.
|
||||
* @param {string} verbatim
|
||||
* @returns {string}
|
||||
*/
|
||||
export function reduceSharePointOnly(verbatim) {
|
||||
const out = verbatim.replace('Dataverse / SharePoint', 'Dataverse');
|
||||
if (out === verbatim) throw new Error('idx 14 reduction is a no-op — record drifted');
|
||||
if (!out.includes('Automatically add')) throw new Error('idx 14: `Automatically` must survive');
|
||||
return out;
|
||||
}
|
||||
|
||||
// Frozen manifest — the operator's ratification, 2026-08-03. `amend: null` means
|
||||
// apply the attested `proposed_remainder` byte-for-byte.
|
||||
export const RATIFIED = [
|
||||
{ idx: 17, amend: null },
|
||||
{ idx: 19, amend: null },
|
||||
{ idx: 33, amend: null },
|
||||
{ idx: 14, amend: reduceSharePointOnly },
|
||||
];
|
||||
|
||||
/**
|
||||
* The remainder actually written for a record: attested, or the ratified amendment.
|
||||
* @param {object} row
|
||||
* @param {{amend: ((v: string) => string) | null}} entry
|
||||
* @returns {string}
|
||||
*/
|
||||
export function amendedRemainder(row, entry) {
|
||||
return entry.amend ? entry.amend(row.file_text_verbatim) : row.proposed_remainder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace the anchored block with its remainder. Pure — no I/O. Throws on any
|
||||
* condition that would make the write unsafe rather than writing something else.
|
||||
* @param {string} content
|
||||
* @param {string} verbatim
|
||||
* @param {string} remainder
|
||||
* @returns {string}
|
||||
*/
|
||||
export function applyEdit(content, verbatim, remainder) {
|
||||
const occurrences = content.split(verbatim).length - 1;
|
||||
if (occurrences !== 1) {
|
||||
throw new Error(`ABORT — anchor occurs ${occurrences} times, expected exactly 1`);
|
||||
}
|
||||
if (!isDeletionOnly(verbatim, remainder)) {
|
||||
throw new Error('ABORT — remainder is not deletion-only w.r.t. the anchor');
|
||||
}
|
||||
const novel = novelWordForms(verbatim, remainder);
|
||||
if (novel.length) {
|
||||
throw new Error(`ABORT — remainder introduces novel word form(s): ${novel.join(', ')}`);
|
||||
}
|
||||
return content.replace(verbatim, remainder);
|
||||
}
|
||||
|
||||
function loadRows() {
|
||||
return readdirSync(RETURNS).filter((f) => f.endsWith('.json')).sort()
|
||||
.flatMap((f) => JSON.parse(readFileSync(join(RETURNS, f), 'utf8')));
|
||||
}
|
||||
|
||||
export function run({ dry = false } = {}) {
|
||||
const byIdx = new Map(loadRows().map((r) => [r.idx, r]));
|
||||
|
||||
// Group by file so two edits sharing a file compose in memory and write once.
|
||||
const perFile = new Map();
|
||||
for (const entry of RATIFIED) {
|
||||
const row = byIdx.get(entry.idx);
|
||||
if (!row) throw new Error(`ABORT — no return record for idx ${entry.idx}`);
|
||||
if (row.verdict !== 'O2_CANDIDATE') {
|
||||
throw new Error(`ABORT — idx ${entry.idx} is ${row.verdict}, not an O2 candidate`);
|
||||
}
|
||||
if (!perFile.has(row.file)) perFile.set(row.file, []);
|
||||
perFile.get(row.file).push({ entry, row });
|
||||
}
|
||||
|
||||
const planned = [];
|
||||
for (const [rel, edits] of perFile) {
|
||||
const abs = join(PLUGIN_ROOT, rel);
|
||||
const before = readFileSync(abs, 'utf8');
|
||||
let out = before;
|
||||
for (const { entry, row } of edits) {
|
||||
out = applyEdit(out, row.file_text_verbatim, amendedRemainder(row, entry));
|
||||
}
|
||||
// Post-condition: every anchor is gone, and the file actually changed.
|
||||
for (const { row } of edits) {
|
||||
if (out.includes(row.file_text_verbatim)) {
|
||||
throw new Error(`ABORT — idx ${row.idx} anchor still present after edit`);
|
||||
}
|
||||
}
|
||||
if (out === before) throw new Error(`ABORT — ${rel} unchanged`);
|
||||
planned.push({ rel, abs, out, idxs: edits.map((e) => e.entry.idx) });
|
||||
}
|
||||
|
||||
console.log(`Ratified edits: ${RATIFIED.length} across ${planned.length} files`);
|
||||
for (const p of planned) console.log(` ~ ${p.rel} (idx ${p.idxs.join(', ')})`);
|
||||
if (dry) {
|
||||
console.log('\n(dry run — no writes)');
|
||||
return planned;
|
||||
}
|
||||
for (const p of planned) atomicWriteSync(p.abs, p.out);
|
||||
console.log(`\nWrote ${planned.length} files.`);
|
||||
return planned;
|
||||
}
|
||||
|
||||
const isMain = (() => {
|
||||
try {
|
||||
return realpathSync(process.argv[1]) === realpathSync(fileURLToPath(import.meta.url));
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
})();
|
||||
if (isMain) run({ dry: process.argv.includes('--dry') });
|
||||
Loading…
Add table
Add a link
Reference in a new issue