test(pdf): apply owner-approved revised S01 baseline metric

This commit is contained in:
cesnimda
2026-10-30 08:21:01 +01:00
parent 9267cab854
commit 0bacb30e20
5 changed files with 116 additions and 5 deletions
@@ -19,6 +19,36 @@ export function verifyBytes(bytes, expected) {
demand(Buffer.isBuffer(bytes) && /^[a-f0-9]{64}$/.test(expected) && digest(bytes) === expected, 'Missing or changed evidence');
}
export function qualifyRevisedS01(oracle, baseline, measurements) {
const unique = rows => {
demand(Array.isArray(rows) && rows.length > 0 && new Set(rows.map(row => row.fixture)).size === rows.length, 'S01 missing or duplicate fixtures');
return new Map(rows.map(row => [row.fixture, row]));
};
const originals = unique(oracle.fixtures), coordinates = unique(baseline.rows), documents = unique(measurements.fixtures);
demand(oracle.total === originals.size && originals.size === coordinates.size && originals.size === documents.size, 'S01 corpus membership differs');
const tolerance = oracle.tolerances;
demand(tolerance.positionMm === 2 && tolerance.fontPt === .5 && tolerance.pageDimensionMm === .5 && tolerance.rgbComponent === 1 / 255, 'S01 original tolerances changed');
const finite = value => demand(typeof value === 'number' && Number.isFinite(value), 'S01 nonfinite coordinate');
const fixtures = [...originals].map(([fixture, original]) => {
const row = coordinates.get(fixture), document = documents.get(fixture);
demand(row && document && document.settings.designVersion === 2 && /^[a-f0-9]{64}$/.test(row.pdfSha256) && row.pdfSha256 === document.pdfSha256, 'S01 source/version binding differs');
finite(row.domBaselinePx); finite(row.pdfBaselinePx);
const baselineDeltaMm = Math.abs(row.domBaselinePx - row.pdfBaselinePx) * 25.4 / 96;
// Only the S01 vertical cell-top comparison is superseded by the owner's baseline metric.
const failures = original.failures.filter(value => !value.startsWith('S01: position delta '));
demand(original.comparisons.filter(value => value.token === 'S01').length === 1, 'S01 marker missing or duplicated');
for (const comparison of original.comparisons) {
for (const key of ['deltaXmm', 'deltaYmm', 'fontDeltaPt']) { finite(comparison[key]); demand(comparison[key] >= 0, 'S01 invalid difference'); }
if (comparison.colorDelta !== null) { finite(comparison.colorDelta); demand(comparison.colorDelta >= 0, 'S01 invalid color difference'); }
const vertical = comparison.token === 'S01' ? baselineDeltaMm : comparison.deltaYmm;
if (comparison.previewPage !== comparison.pdfPage || Math.max(comparison.deltaXmm, vertical) > 2 || comparison.fontDeltaPt > .5 || (comparison.colorDelta !== null && comparison.colorDelta > 1 / 255))
failures.push(`${comparison.token}: retained position/page/font/color check failed`);
}
return { fixture, pdfSha256: row.pdfSha256, baselineDeltaMm, pass: failures.length === 0, failures };
});
return { metric: 'S01_Y_BASELINE_TO_BASELINE', positionToleranceMm: 2, total: fixtures.length, passed: fixtures.filter(row => row.pass).length, fixtures };
}
export function measureNative(format, text) {
demand(Buffer.byteLength(text, 'utf8') <= 8 * 1024 * 1024, 'Native report exceeds its input bound');
const result = { passed: 0, failed: 0, skipped: 0, retries: 0, unexecuted: 0, names: [] };
@@ -177,7 +207,7 @@ export function validatePackage(base = directory) {
verifyBytes(fs.readFileSync(file), expected);
}
const verifierFiles = JSON.parse(artifact(evidence.verifierSourceArtifactId).toString('utf8'));
const expectedVerifierFiles = ['verify.mjs', 'verify.test.mjs', 'package.test.mjs', 'verify_git.py', 'HANDOFF_SCHEMA.json'].map(name => 'docs/automation/handoffs/pre-phase10/' + name).sort();
const expectedVerifierFiles = ['verify.mjs', 'verify.test.mjs', 'package.test.mjs', 'verify_git.py', 'HANDOFF_SCHEMA.json', 'S01_METRIC_APPROVAL.json'].map(name => 'docs/automation/handoffs/pre-phase10/' + name).sort();
demand(JSON.stringify(Object.keys(verifierFiles).sort()) === JSON.stringify(expectedVerifierFiles), 'Missing verifier source membership');
for (const [name, expected] of Object.entries(verifierFiles)) {
verifyBytes(fs.readFileSync(path.join(root, name)), expected);
@@ -203,14 +233,24 @@ export function validatePackage(base = directory) {
demand(probe.cases?.length === 3 && probe.cases.every(item => item.status === 'REJECTED_OR_FAILED'), 'This snapshot lacks its recorded negative CV-model evidence');
const oracles = evidence.reports.filter(item => ['windows-template-oracle', 'linux-template-oracle'].includes(item.id));
demand(oracles.length === 2 && oracles.every(item => item.status === 'PARTIAL' && item.exitCode !== 0), 'Original PDF failures were concealed');
demand(decisions.get('Automated PDF Fidelity') === 'FAIL' && decisions.get('PDF Spacing') === 'PARTIAL', 'Revised PDF failures were promoted');
const approval = JSON.parse(artifact('s01-metric-approval').toString('utf8'));
verifyBytes(artifact('s01-metric-approval'), digest(readSafe('S01_METRIC_APPROVAL.json')));
demand(approval.candidateCommit === 'ba04d817fed26d659bfa2a56b089d6cac851dc34' && evidence.testedCommit === approval.candidateCommit && approval.metric === 'S01_Y_BASELINE_TO_BASELINE' && approval.positionToleranceMm === 2 && approval.designVersion === 2 && approval.ownerReply === 'Approve the revised S01 metric only', 'S01 owner approval scope differs');
const revisedS01 = {};
for (const platform of ['windows', 'linux']) {
const parse = id => JSON.parse(artifact(id).toString('utf8'));
const result = qualifyRevisedS01(parse(platform + '-template-oracle-native'), parse('p10-' + platform + '-classification'), parse(platform + '-template-measurements'));
demand(result.total === 36 && result.passed === 36, 'Approved revised S01 fidelity failed');
revisedS01[platform] = { passed: result.passed, total: result.total };
}
demand(decisions.get('Automated PDF Fidelity') === 'PASS' && decisions.get('PDF Spacing') === 'PARTIAL', 'Scoped PDF or human spacing decision differs');
const stages = documents.GATES.stages;
demand(stages.map(item => item.sequence).join(',') === '5,6,7,8,9,10,11' && stages[0].manualApprovals.includes('phase10-development-entry') && stages[0].manualApprovals.includes('candidate-specific-cleanup-scope') && stages.every(item => item.permissibleConditionalDeferrals.length === 0), 'Source-derived stage/approval conditions changed');
const observedLocalCommit = execFileSync('rtk', ['proxy', 'git', 'rev-parse', 'HEAD'], { cwd: root, encoding: 'utf8' }).trim();
const observedWorkingTreeClean = !execFileSync('rtk', ['proxy', 'git', 'status', '--porcelain'], { cwd: root, encoding: 'utf8' }).trim();
const branch = execFileSync('rtk', ['proxy', 'git', 'branch', '--show-current'], { cwd: root, encoding: 'utf8' }).trim();
demand(branch === declared.sourceBranch, 'Wrong checkout branch');
return { integrity: 'PASS', testedCommit: evidence.testedCommit, observedLocalCommit, observedWorkingTreeClean, reports: evidence.reports.length, artifacts: evidence.artifacts.length, phase10: 'BLOCKED', ...requireProtectedEntry() };
return { integrity: 'PASS', testedCommit: evidence.testedCommit, observedLocalCommit, observedWorkingTreeClean, reports: evidence.reports.length, artifacts: evidence.artifacts.length, revisedS01, phase10: 'BLOCKED', ...requireProtectedEntry() };
}
if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {