fix(playable): close release validation lifecycle gaps
This commit is contained in:
@@ -56,10 +56,16 @@ function argsOf(argv) {
|
||||
}
|
||||
|
||||
function readJson(file) {
|
||||
try { return JSON.parse(fs.readFileSync(file, 'utf8')); }
|
||||
try {
|
||||
const value = JSON.parse(fs.readFileSync(file, 'utf8'));
|
||||
if (!isObject(value)) return { __read_error: `${path.basename(file)}: root must be an object` };
|
||||
return value;
|
||||
}
|
||||
catch (error) { return { __read_error: `${path.basename(file)}: ${error.code || 'invalid JSON'}` }; }
|
||||
}
|
||||
|
||||
function isObject(value) { return value !== null && typeof value === 'object' && !Array.isArray(value); }
|
||||
|
||||
function secretLiterals() {
|
||||
return ['MT_ACCOUNT', 'MT_PASSWORD'].map((name) => process.env[name] || '').filter((v) => v.length >= SECRET_MIN_SCAN);
|
||||
}
|
||||
@@ -160,7 +166,8 @@ function validateReport(report, options, errors) {
|
||||
for (const line of lines) {
|
||||
let event;
|
||||
try { event = JSON.parse(line); } catch { badEvents += 1; continue; }
|
||||
if (!EVENT_KEYS.every((key) => Object.hasOwn(event, key)) || event.run_id !== options.run_id
|
||||
if (!isObject(event) || !EVENT_KEYS.every((key) => Object.hasOwn(event, key)) || event.run_id !== options.run_id
|
||||
|| !Number.isSafeInteger(event.monotonic_us) || event.monotonic_us < 0
|
||||
|| typeof event.payload !== 'object' || event.payload === null || Array.isArray(event.payload)
|
||||
|| !Number.isInteger(event.connection_epoch) || event.monotonic_us < lastUs) badEvents += 1;
|
||||
else lastUs = event.monotonic_us;
|
||||
@@ -273,6 +280,16 @@ function sealRelease(dir) {
|
||||
if (report.run_id !== run.run_id) errors.fail.push(`${label}: report run_id mismatch`);
|
||||
if (report.suite !== run.suite) errors.fail.push(`${label}: report suite mismatch`);
|
||||
if (report.exit_gate?.checked !== true || report.exit_gate?.process_code !== 0 || report.exit_gate?.timed_out !== false) errors.fail.push(`${label}: report not sealed by a clean exit gate`);
|
||||
// The client exit gate runs before the external RSS seal. An interrupted
|
||||
// or crashed memory sealer must not leave a releasable intermediate PASS.
|
||||
if (run.suite === 'soak' && report.status === 'PASS') {
|
||||
const memoryCases = Array.isArray(report.runner_cases)
|
||||
? report.runner_cases.filter((item) => item?.id === 'STB-MEMORY-01') : [];
|
||||
if (memoryCases.length !== 1 || memoryCases[0].status !== 'PASS'
|
||||
|| report.memory?.verdict?.status !== 'PASS') {
|
||||
errors.fail.push(`${label}: STB-MEMORY-01 external memory seal missing or not PASS`);
|
||||
}
|
||||
}
|
||||
checkBuild(report.build, manifest.candidate, `${label}: `, errors);
|
||||
if (report.status === 'PASS') passedBySuite[run.suite] = (passedBySuite[run.suite] || 0) + 1;
|
||||
else if (report.status === 'BLOCKED') {
|
||||
|
||||
Reference in New Issue
Block a user