feat(automation): enforce assessment scope and document activation blockers
This commit is contained in:
@@ -0,0 +1,61 @@
|
||||
// Trusted operator preparation. Validates published native evidence; runs no product suite.
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import crypto from 'node:crypto';
|
||||
import zlib from 'node:zlib';
|
||||
import { pathToFileURL } from 'node:url';
|
||||
import { execFileSync } from 'node:child_process';
|
||||
|
||||
const [repository, destination] = process.argv.slice(2);
|
||||
if (!repository || !destination) throw new Error('Usage: node prepare_assessment.mjs <published-handoff-checkout> <new-output-directory>');
|
||||
const root = fs.realpathSync(repository), output = path.resolve(destination);
|
||||
if (fs.existsSync(output)) throw new Error('Use a new receipt directory; preserve previous qualification');
|
||||
const git = (...args) => execFileSync('rtk', ['proxy','git','-C',root,...args], {encoding:'utf8'}).trim();
|
||||
const handoff = '5cb903b989742b7a96cb22ddf4c0e24993b18980';
|
||||
const baseline = 'fc5b9d377199ce51f480a920a3772108094929e3';
|
||||
if (git('rev-parse','HEAD') !== handoff || git('status','--porcelain')) throw new Error('Require the exact clean published handoff checkout');
|
||||
const remote = git('ls-remote','--exit-code','origin','refs/heads/test/pre-phase10-ux-acceptance');
|
||||
if (remote.split(/\s/)[0] !== handoff) throw new Error('Handoff publication differs');
|
||||
if (git('rev-parse',handoff+'^') !== baseline) throw new Error('Baseline relationship differs');
|
||||
const relative = 'docs/automation/handoffs/pre-phase10';
|
||||
const directory = path.join(root,relative);
|
||||
const {validatePackage, measureNative} = await import(pathToFileURL(path.join(directory,'verify.mjs')).href);
|
||||
const verified = validatePackage();
|
||||
if (verified.integrity !== 'PASS' || !verified.observedWorkingTreeClean) throw new Error('Handoff integrity failed');
|
||||
const evidence = JSON.parse(fs.readFileSync(path.join(directory,'TEST_EVIDENCE.json')));
|
||||
const artifact = id => {
|
||||
const row=evidence.artifacts.find(item=>item.id===id);
|
||||
if (!row) throw new Error('Missing native artifact');
|
||||
const raw=fs.readFileSync(path.join(directory,row.path));
|
||||
return row.path.endsWith('.gz') ? zlib.gunzipSync(raw) : raw;
|
||||
};
|
||||
const hash = raw => crypto.createHash('sha256').update(raw).digest('hex');
|
||||
const files = {};
|
||||
// Freeze all tracked non-envelope sources, including tests/config/security/preservation documentation.
|
||||
for (const name of git('ls-files','-z').split('\0').filter(Boolean)) {
|
||||
if (!name.startsWith(relative+'/')) files[name]=hash(fs.readFileSync(path.join(root,name)));
|
||||
}
|
||||
const nativeReports = evidence.reports.map(report=>{
|
||||
let observation=JSON.parse(artifact(report.processArtifactId));
|
||||
const parts=report.processPointer.slice(1).split('/').map(value=>value.replaceAll('~1','/').replaceAll('~0','~'));
|
||||
for (const key of parts.slice(0,-1)) observation=observation[key];
|
||||
return {...report, measured:report.nativeFormat ? measureNative(report.nativeFormat,artifact(report.artifactId).toString('utf8')) : null,
|
||||
nativeProcess:observation};
|
||||
});
|
||||
fs.mkdirSync(output,{recursive:true});
|
||||
const evidenceFiles={};
|
||||
for (const name of git('ls-files',relative).split(/\r?\n/).filter(Boolean)) {
|
||||
const local=path.relative(directory,path.join(root,name));
|
||||
const target=path.join(output,'handoff',local);
|
||||
fs.mkdirSync(path.dirname(target),{recursive:true});
|
||||
fs.copyFileSync(path.join(root,name),target);
|
||||
evidenceFiles['handoff/'+local.split(path.sep).join('/')]=hash(fs.readFileSync(target));
|
||||
}
|
||||
const receipt={schemaVersion:1,scope:'P10-00-ASSESSMENT-ONLY',integrity:'PASS',
|
||||
generatedUtc:new Date().toISOString(),baselineCommit:baseline,handoffCommit:handoff,
|
||||
testedCommit:evidence.testedCommit,files,evidenceFiles,nativeReports,
|
||||
retainedDecisions:JSON.parse(fs.readFileSync(path.join(directory,'ACCEPTANCE.json'))).verdicts,
|
||||
revisedS01:verified.revisedS01,verifierObservation:verified,
|
||||
limits:'Source-bound reuse for unchanged read-only assessment. Original failures and conditional/blocked decisions retained. No fresh product, security, model, human or production acceptance.'};
|
||||
fs.writeFileSync(path.join(output,'receipt.json'),JSON.stringify(receipt,null,2)+'\n');
|
||||
console.log(JSON.stringify({integrity:'PASS',receipt:path.join(output,'receipt.json'),sha256:hash(fs.readFileSync(path.join(output,'receipt.json'))),frozenSources:Object.keys(files).length,nativeReports:nativeReports.length,productSuitesExecuted:0},null,2));
|
||||
Reference in New Issue
Block a user