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
64
scripts/kb-eval/check-g7-queue.mjs
Normal file
64
scripts/kb-eval/check-g7-queue.mjs
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
#!/usr/bin/env node
|
||||
/**
|
||||
* Check the G7 review queue against the live corpus.
|
||||
*
|
||||
* Exit 0 = every open entry still anchors to real text; exit 1 = drift or a
|
||||
* schema fault. Drift is a finding, never a silent pass: an entry that stops
|
||||
* matching is exactly the case G7 exists to prevent — a real defect leaving the
|
||||
* programme unnoticed because someone edited around it.
|
||||
*
|
||||
* Read-only. Nothing in the queue is machine-appliable; resolution is a human
|
||||
* review act (form b, ratified 2026-08-03).
|
||||
*
|
||||
* node scripts/kb-eval/check-g7-queue.mjs [--json]
|
||||
*/
|
||||
import { readFileSync } from 'node:fs';
|
||||
import { validateQueue } from './lib/g7-queue.mjs';
|
||||
|
||||
const QUEUE = 'scripts/kb-eval/data/g7-review-queue.json';
|
||||
|
||||
const asJson = process.argv.includes('--json');
|
||||
|
||||
let queue;
|
||||
try {
|
||||
queue = JSON.parse(readFileSync(QUEUE, 'utf8'));
|
||||
} catch (err) {
|
||||
console.error(`cannot read ${QUEUE}: ${err.message}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const entries = queue.entries ?? [];
|
||||
const { ok, findings } = validateQueue(entries, (p) => readFileSync(p, 'utf8'));
|
||||
|
||||
const open = entries.filter((e) => e.status === 'open');
|
||||
const resolved = entries.filter((e) => e.status === 'resolved');
|
||||
|
||||
if (asJson) {
|
||||
console.log(JSON.stringify({ ok, open: open.length, resolved: resolved.length, findings }, null, 2));
|
||||
process.exit(ok ? 0 : 1);
|
||||
}
|
||||
|
||||
console.log(`G7 review queue — ${open.length} open, ${resolved.length} resolved\n`);
|
||||
|
||||
const byClass = (cls) => open.filter((e) => e.class === cls);
|
||||
for (const cls of ['multi-locator', 'replacement']) {
|
||||
const rows = byClass(cls);
|
||||
if (rows.length === 0) continue;
|
||||
console.log(` ${cls} (${rows.length}):`);
|
||||
for (const e of rows) {
|
||||
console.log(` ${e.id.padEnd(8)} ${e.file.split('/').pop()}`);
|
||||
}
|
||||
console.log();
|
||||
}
|
||||
|
||||
if (findings.length > 0) {
|
||||
console.log(`FINDINGS (${findings.length}):`);
|
||||
for (const f of findings) {
|
||||
console.log(` [${f.kind}] ${f.id ?? ''} ${f.message}`);
|
||||
}
|
||||
console.log('\nA drifted anchor means the file changed under a queued defect.');
|
||||
console.log('Re-derive the anchor from the live file — do not delete the entry.');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log('All open entries still anchor to live corpus text. exit 0');
|
||||
86
scripts/kb-eval/data/g7-review-queue.json
Normal file
86
scripts/kb-eval/data/g7-review-queue.json
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
{
|
||||
"_meta": {
|
||||
"gap": "G7",
|
||||
"form": "(b) named queue into the human review phase",
|
||||
"ratified": "2026-08-03",
|
||||
"rationale": "Measured in R11 §9.6: 2 of the 4 subtractions applied in 957ebef left a residue, so residues are the normal by-product of a delete-only envelope rather than an exception. Two of the members are replacements, not multi-locator cases, which a deletion-oriented O4 class would not have fixed. A queue absorbs both classes; an O4 return contract would have been mis-sized against the evidence.",
|
||||
"contract": "Anchors are verbatim strings, never line numbers (line ≠ real_line in 9 of 17 R11 records). An open entry whose anchor no longer occurs in its file is drift, and check-g7-queue.mjs fails rather than passing it silently. Nothing in this queue is machine-appliable by definition — every entry is outside the O2 envelope. Resolution is a human review act.",
|
||||
"evidence": "docs/r11-pilot-results.md §9.4, §9.5, §9.6; docs/ref-kb-correctness-program-2026-06.md §8 G7"
|
||||
},
|
||||
"entries": [
|
||||
{
|
||||
"id": "idx-17",
|
||||
"file": "skills/ms-ai-engineering/references/rag-architecture/rag-caching-optimization.md",
|
||||
"class": "replacement",
|
||||
"status": "resolved",
|
||||
"raised": "2026-08-03",
|
||||
"summary": "The idx 17 subtraction (957ebef) deleted the three score-threshold bands but left the lead-in ending in a colon, promising an enumeration that no longer existed, immediately followed by a **Verified** stamp. The subtraction was correct; the paragraph it left was not. V1/V2/V2b/V3 are string invariants over deleted text and cannot see document coherence, so the machine could not have caught it.",
|
||||
"evidence": "docs/r11-pilot-results.md §9.6",
|
||||
"anchors": [],
|
||||
"resolution": "Operator-ratified 2026-08-03: colon changed to a period, making the lead-in a complete and independently true sentence that the **Verified** stamp correctly covers. Corpus swept for the same defect shape (bold lead-in ending in colon, blank line, **Verified**) — no other occurrence."
|
||||
},
|
||||
{
|
||||
"id": "idx-33",
|
||||
"file": "skills/ms-ai-security/references/ai-security-engineering/ai-threat-modeling-stride.md",
|
||||
"class": "replacement",
|
||||
"status": "open",
|
||||
"raised": "2026-08-03",
|
||||
"summary": "The idx 33 subtraction removed AI asset inventory via Azure Resource Graph and the Purview Insider Risk Management bullet from an unsupported Defender for Cloud AISPM attribution. Correct — but the same capabilities survive in this file under Cloud Adoption Framework Secure AI attribution, stamped 'Verified MCP 2026-04', and are asserted in four other corpus files. The edit's benefit is corpus-wide smaller than the single line suggested. Whether the CAF attribution is itself supported has not been checked.",
|
||||
"evidence": "docs/r11-pilot-results.md §9.5 (cross-corpus check), §9.6",
|
||||
"anchors": [
|
||||
"Oppdatert 2026-04: inkluderer nå AI asset inventory via Azure Resource Graph"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "idx-26",
|
||||
"file": "skills/ms-ai-governance/references/responsible-ai/transparency-documentation-standards.md",
|
||||
"class": "multi-locator",
|
||||
"status": "open",
|
||||
"raised": "2026-08-03",
|
||||
"summary": "The Responsible AI Scorecard component list names Error analysis (item 4) and Counterfactual analysis (item 5). Both were measured false against first-party docs 2026-08-03: the canonical scorecard segments are summary/model overview, data analysis, model performance, cohorts, top important factors, fairness insights and causal insights; Error analysis and Counterfactual analysis are Responsible AI *dashboard* components. The delete-only reduction could only remove item 5, because line 300 asserts Error analysis as scorecard content too — so a partial fix would have left a known-false claim standing while introducing a renumbering artifact (1,2,3,4,6,7). Operator declined the partial fix 2026-08-03 and sent the whole case here. Correct repair spans both locators.",
|
||||
"evidence": "docs/r11-pilot-results.md §9.6; https://learn.microsoft.com/azure/machine-learning/how-to-responsible-ai-scorecard",
|
||||
"anchors": [
|
||||
"4. **Error analysis**: Error rates per cohort, confusion matrices",
|
||||
"| **Risk assessment** | Responsible AI Scorecard: Error analysis, fairness assessment |"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "idx-27",
|
||||
"file": "skills/ms-ai-governance/references/responsible-ai/transparency-documentation-standards.md",
|
||||
"class": "replacement",
|
||||
"status": "open",
|
||||
"raised": "2026-08-03",
|
||||
"summary": "Failed out of O2 on cond 3 (§9.6): the cited source DOES establish the mechanism — faqs-generative-orchestration states 'Makers can require user confirmation before executing tools that modify data' — so deleting the Plugin-actions row would destroy source-confirmed information. The defect is modality, not fabrication: the file presents confirmation prompts as a built-in disclosure, whereas the source makes them maker-configured. The Chat-interface row in the same table is imprecise for the same reason: the FAQ documents a default transparency message ('Just so you are aware, I sometimes use AI to answer your questions.'), not a 'Powered by AI' badge. Repair is a replacement, outside the delete-only envelope.",
|
||||
"evidence": "docs/r11-pilot-results.md §9.6; https://learn.microsoft.com/microsoft-copilot-studio/faqs-generative-orchestration",
|
||||
"anchors": [
|
||||
"| **Plugin actions** | Confirmation prompts før sensitive actions (send email, delete file) |",
|
||||
"| **Chat interface** | \"Powered by AI\" badge i chat window |"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "idx-36",
|
||||
"file": "skills/ms-ai-security/references/ai-security-engineering/ai-threat-modeling-stride.md",
|
||||
"class": "multi-locator",
|
||||
"status": "open",
|
||||
"raised": "2026-08-03",
|
||||
"summary": "Applying idx 36 alone yields a severity table more precise than the prose that cites it, so a companion edit is required for the prose to match the narrowed table. Held back from 957ebef as out of envelope.",
|
||||
"evidence": "docs/r11-pilot-results.md §9.4, §9.5",
|
||||
"anchors": [
|
||||
"Øker severity bar; krever mer robust adversarial defenses"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "idx-18",
|
||||
"file": "skills/ms-ai-engineering/references/rag-architecture/rag-caching-optimization.md",
|
||||
"class": "multi-locator",
|
||||
"status": "open",
|
||||
"raised": "2026-08-03",
|
||||
"summary": "No reduction exists. The surviving claim is a whole titled section on Azure AI Search built-in caching plus a **Verified** row in the verification table, so no deletion confined to a single locator can repair the file. This is the member that most clearly motivated G7.",
|
||||
"evidence": "docs/r11-pilot-results.md §9.3, §9.4",
|
||||
"anchors": [
|
||||
"**Automatic Caching Behavior:**",
|
||||
"| Azure AI Search caching | **Verified** | Microsoft Learn docs (4, 6) |"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
100
scripts/kb-eval/lib/g7-queue.mjs
Normal file
100
scripts/kb-eval/lib/g7-queue.mjs
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
/**
|
||||
* G7 review queue — the named queue into the human review phase.
|
||||
*
|
||||
* G7 is the gap for corrections that are RIGHT but larger than the O2 envelope
|
||||
* (one locator + deletion-only). Measured in R11 §9.6: of the four subtractions
|
||||
* applied in 957ebef, two left a residue. Residues are the normal by-product of
|
||||
* a delete-only envelope, not an exception — and two of the five members are
|
||||
* replacements rather than multi-locator cases, which a deletion-oriented O4
|
||||
* class would not have fixed. Hence a queue (form b), ratified 2026-08-03.
|
||||
*
|
||||
* This module validates the queue. It deliberately does NOT apply anything:
|
||||
* every entry here is by definition outside the machine-appliable envelope.
|
||||
*
|
||||
* Anchors are verbatim strings, never line numbers — line numbers drift, and
|
||||
* §9.4 measured `line` ≠ `real_line` in 9 of 17 records. An open entry whose
|
||||
* anchor no longer occurs is reported as drift rather than quietly passing,
|
||||
* because the whole purpose of the queue is that a defect cannot fall out of
|
||||
* the programme unnoticed.
|
||||
*/
|
||||
|
||||
export const ENTRY_CLASSES = new Set(['multi-locator', 'replacement']);
|
||||
export const ENTRY_STATES = new Set(['open', 'resolved']);
|
||||
|
||||
const REQUIRED = ['id', 'file', 'class', 'status', 'raised', 'summary', 'evidence', 'anchors'];
|
||||
|
||||
/**
|
||||
* @param {unknown} entries queue entries
|
||||
* @param {(path: string) => string} readFile
|
||||
* @returns {{ ok: boolean, findings: Array<{id?: string, kind: string, message: string}> }}
|
||||
*/
|
||||
export function validateQueue(entries, readFile) {
|
||||
const findings = [];
|
||||
|
||||
if (!Array.isArray(entries)) {
|
||||
return { ok: false, findings: [{ kind: 'schema', message: 'queue must be an array of entries' }] };
|
||||
}
|
||||
|
||||
const seen = new Set();
|
||||
|
||||
for (const entry of entries) {
|
||||
const id = typeof entry?.id === 'string' ? entry.id : '<no id>';
|
||||
|
||||
const missing = REQUIRED.filter((f) => entry?.[f] === undefined);
|
||||
if (missing.length > 0) {
|
||||
findings.push({ id, kind: 'schema', message: `missing required field(s): ${missing.join(', ')}` });
|
||||
continue;
|
||||
}
|
||||
|
||||
if (seen.has(entry.id)) {
|
||||
findings.push({ id, kind: 'schema', message: `duplicate id: ${entry.id}` });
|
||||
continue;
|
||||
}
|
||||
seen.add(entry.id);
|
||||
|
||||
if (!ENTRY_CLASSES.has(entry.class)) {
|
||||
findings.push({ id, kind: 'schema', message: `unknown class: ${entry.class}` });
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!ENTRY_STATES.has(entry.status)) {
|
||||
findings.push({ id, kind: 'schema', message: `unknown status: ${entry.status}` });
|
||||
continue;
|
||||
}
|
||||
|
||||
// A resolved entry has to say what closed it. Without that, "resolved" is
|
||||
// indistinguishable from "quietly dropped" — the exact failure G7 exists to
|
||||
// prevent. Resolved entries are exempt from anchor checking, since a real
|
||||
// fix is expected to have changed the text the anchor pointed at.
|
||||
if (entry.status === 'resolved') {
|
||||
if (typeof entry.resolution !== 'string' || entry.resolution.trim() === '') {
|
||||
findings.push({ id, kind: 'schema', message: 'a resolved entry must record a resolution' });
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!Array.isArray(entry.anchors) || entry.anchors.length === 0) {
|
||||
findings.push({ id, kind: 'schema', message: 'an open entry must carry at least one anchor' });
|
||||
continue;
|
||||
}
|
||||
|
||||
let text;
|
||||
try {
|
||||
text = readFile(entry.file);
|
||||
} catch {
|
||||
findings.push({ id, kind: 'file_unreadable', message: `cannot read ${entry.file}` });
|
||||
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(', ')}`,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return { ok: findings.length === 0, findings };
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue