feat(ms-ai-architect): G2 — generaliser stamp-guard til version-label-streng (TDD, kaster på malformert) [skip-docs]
This commit is contained in:
parent
36fd4cfe7b
commit
6f82572dba
2 changed files with 82 additions and 8 deletions
|
|
@ -148,6 +148,30 @@ export function buildKbHeader(meta) {
|
|||
return `${header}\n---\n`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format the `verified_by` judge-version label. Returns the WHOLE label (`judge-v3.1`).
|
||||
* Absence (undefined) → the current JUDGE_VERSION default; otherwise the value — an integer
|
||||
* major (`3` → judge-v3) or a `major.minor` string/float (`'3.2'`/`3.2` → judge-v3.2) — is
|
||||
* validated against a strict numeric-label regex and honoured. Mirrors the DATE_RE throw
|
||||
* discipline (stampVerifiedMeta): a malformed label THROWS rather than silently falling back
|
||||
* to the default, so a caller bug surfaces instead of shipping a mislabelled provenance stamp
|
||||
* into a public file.
|
||||
*
|
||||
* @param {number|string} [judgeVersion] — integer major or `major.minor`; absent → default
|
||||
* @returns {string} the `judge-v<label>` provenance label
|
||||
* @throws if judgeVersion is present but not a `\d+(\.\d+)?` numeric label
|
||||
*/
|
||||
function formatJudgeVersion(judgeVersion) {
|
||||
if (judgeVersion === undefined) return `judge-v${JUDGE_VERSION}`;
|
||||
const label = String(judgeVersion);
|
||||
if (!/^\d+(?:\.\d+)?$/.test(label)) {
|
||||
throw new Error(
|
||||
`formatJudgeVersion: malformed judge version '${judgeVersion}' (expected integer or major.minor)`,
|
||||
);
|
||||
}
|
||||
return `judge-v${label}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Born-verified gate (Spor 3 Port 2): stamp `verified`/`verified_by` onto a reference
|
||||
* meta IFF the judge verdict passed — otherwise REFUSE (throw). This encodes
|
||||
|
|
@ -157,7 +181,7 @@ export function buildKbHeader(meta) {
|
|||
* failing verdict means no file is born.
|
||||
*
|
||||
* @param {object} baseMeta — the reference meta (title/status/category/source/lastUpdated)
|
||||
* @param {{pass: boolean, judgeVersion?: number, by?: 'human'}} verdict
|
||||
* @param {{pass: boolean, judgeVersion?: number|string, by?: 'human'}} verdict
|
||||
* @param {string} today — YYYY-MM or YYYY-MM-DD, supplied by the caller (lib stays pure)
|
||||
* @returns {object} baseMeta with type='reference' + verified + verified_by set
|
||||
* @throws if the verdict is not passing, or today is not a valid date
|
||||
|
|
@ -169,14 +193,12 @@ export function stampVerifiedMeta(baseMeta, verdict, today) {
|
|||
if (!DATE_RE.test(String(today ?? ''))) {
|
||||
throw new Error(`stampVerifiedMeta: invalid today date '${today}' (expected YYYY-MM[-DD])`);
|
||||
}
|
||||
// Default stamp = the current JUDGE_VERSION label ('3.1'). An explicit integer override
|
||||
// is still honoured (judgeVersion:3 → judge-v3); a minor-bearing string override is not
|
||||
// yet (falls back to default) — tracked as a §8 follow-up, harmless since the pipeline
|
||||
// always uses the default.
|
||||
// Default stamp = the current JUDGE_VERSION label ('3.1'). An explicit override — integer
|
||||
// (judgeVersion:3 → judge-v3) OR minor-bearing string/float (judgeVersion:'3.2' → judge-v3.2)
|
||||
// — is honoured via formatJudgeVersion, which THROWS on a malformed label rather than silently
|
||||
// falling back (§8 follow-up closed: the string override is no longer dropped).
|
||||
const verifiedBy =
|
||||
verdict.by === 'human'
|
||||
? 'human'
|
||||
: `judge-v${Number.isInteger(verdict.judgeVersion) ? verdict.judgeVersion : JUDGE_VERSION}`;
|
||||
verdict.by === 'human' ? 'human' : formatJudgeVersion(verdict.judgeVersion);
|
||||
return { ...baseMeta, type: 'reference', verified: today, verified_by: verifiedBy };
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -208,6 +208,58 @@ test('stampVerifiedMeta honours an explicit judgeVersion', () => {
|
|||
assert.equal(stampVerifiedMeta(base, { pass: true, judgeVersion: 3 }, '2026-06-29').verified_by, 'judge-v3');
|
||||
});
|
||||
|
||||
// --- G2: version-label generalisation (formatJudgeVersion via stampVerifiedMeta) -----
|
||||
// The stamp guard now honours a minor-bearing label ('3.2' → judge-v3.2), preserves the
|
||||
// integer path (regression above), defaults on absence, and THROWS on a malformed label
|
||||
// rather than silently falling back — a mislabel would be a provenance lie in a public file.
|
||||
|
||||
test('stampVerifiedMeta honours a minor-bearing string judgeVersion override', () => {
|
||||
const base = { title: 'T', status: 'GA', category: 'c',
|
||||
source: 'https://learn.microsoft.com/x', lastUpdated: '2026-06' };
|
||||
assert.equal(
|
||||
stampVerifiedMeta(base, { pass: true, judgeVersion: '3.2' }, '2026-06-29').verified_by,
|
||||
'judge-v3.2',
|
||||
);
|
||||
});
|
||||
|
||||
test('stampVerifiedMeta honours a float judgeVersion override', () => {
|
||||
const base = { title: 'T', status: 'GA', category: 'c',
|
||||
source: 'https://learn.microsoft.com/x', lastUpdated: '2026-06' };
|
||||
assert.equal(
|
||||
stampVerifiedMeta(base, { pass: true, judgeVersion: 3.2 }, '2026-06-29').verified_by,
|
||||
'judge-v3.2',
|
||||
);
|
||||
});
|
||||
|
||||
test('stampVerifiedMeta defaults verified_by to judge-v3.1 when judgeVersion is absent', () => {
|
||||
const base = { title: 'T', status: 'GA', category: 'c',
|
||||
source: 'https://learn.microsoft.com/x', lastUpdated: '2026-06' };
|
||||
assert.equal(
|
||||
stampVerifiedMeta(base, { pass: true }, '2026-06-29').verified_by,
|
||||
'judge-v3.1',
|
||||
);
|
||||
});
|
||||
|
||||
test('stampVerifiedMeta throws on a malformed judgeVersion label (no silent fallback)', () => {
|
||||
const base = { title: 'T', status: 'GA', category: 'c',
|
||||
source: 'https://learn.microsoft.com/x', lastUpdated: '2026-06' };
|
||||
for (const bad of ['3.2.1', '', 'v3', '3.2 x']) {
|
||||
assert.throws(
|
||||
() => stampVerifiedMeta(base, { pass: true, judgeVersion: bad }, '2026-06-29'),
|
||||
/judge version|malformed/i,
|
||||
`expected throw for judgeVersion=${JSON.stringify(bad)}`,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
test('stampVerifiedMeta → composeKbFile round-trips a minor-bearing judge version (end-to-end)', () => {
|
||||
const base = { title: 'Azure AI Search', status: 'GA', category: 'rag-architecture',
|
||||
source: 'https://learn.microsoft.com/azure/search/x', lastUpdated: '2026-06' };
|
||||
const stamped = stampVerifiedMeta(base, { pass: true, judgeVersion: '3.2' }, '2026-06-29');
|
||||
const file = composeKbFile(stamped, '## Introduksjon\n\nKort.\n');
|
||||
assert.equal(parseVerifiedByHeader(file), 'judge-v3.2');
|
||||
});
|
||||
|
||||
test('stampVerifiedMeta supports a human verification path', () => {
|
||||
const base = { title: 'T', status: 'GA', category: 'c',
|
||||
source: 'https://learn.microsoft.com/x', lastUpdated: '2026-06' };
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue