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]
149 lines
6 KiB
JavaScript
149 lines
6 KiB
JavaScript
#!/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') });
|