Files
jobtrackingapp/job-tracker-ui/scripts/verify-a3-preservation.mjs
T

38 lines
2.7 KiB
JavaScript

// Read-only source census. Route presence does not establish workflow or security acceptance.
import fs from 'node:fs';
import path from 'node:path';
import crypto from 'node:crypto';
import assert from 'node:assert/strict';
import { execFileSync } from 'node:child_process';
import ts from 'typescript';
const root = path.resolve(import.meta.dirname, '../..');
const registerPath = 'docs/pre-phase10-ux/preservation.json';
const registerBytes = fs.readFileSync(path.join(root, registerPath));
const register = JSON.parse(registerBytes);
const base = 'd3d338376444b8abdcaf7200f682faa3daf1ad10';
const digest = bytes => crypto.createHash('sha256').update(bytes.toString('utf8').replace(/\r\n/g, '\n')).digest('hex');
assert.equal(digest(registerBytes), digest(execFileSync('git', ['show', `${base}:${registerPath}`], { cwd: root })), 'Original preservation register must remain unchanged');
assert.equal(register.groups.length, 57); assert.equal(register.routes.length, 34);
const source = fs.readFileSync(path.join(root, 'job-tracker-ui/src/App.tsx'), 'utf8');
const ast = ts.createSourceFile('App.tsx', source, ts.ScriptTarget.Latest, true, ts.ScriptKind.TSX);
const declared = new Set();
function visit(node) {
if (ts.isJsxAttribute(node) && node.name.getText(ast) === 'path' && node.initializer && ts.isStringLiteral(node.initializer)) declared.add(node.initializer.text);
if (ts.isPropertyAssignment(node) && node.name.getText(ast) === 'path' && ts.isStringLiteral(node.initializer)) declared.add(node.initializer.text);
ts.forEachChild(node, visit);
}
visit(ast);
const missing = register.routes.filter(item => !declared.has(item.route));
assert.deepEqual(missing, [], 'All inherited route patterns must remain registered');
const result = {
base, policy: 'Source membership and unchanged criteria only; no acceptance promotion.',
sourceSha256: digest(source), registerSha256: digest(registerBytes),
groups: register.groups.map(({ id, currentStatus, requiredClosure, sourceCriterion }) => ({ id, currentStatus, requiredClosure, sourceCriterion })),
routes: register.routes.map(({ route, owners, requiredClosure }) => ({ route, owners, requiredClosure, sourceMembership: 'PRESENT' })),
limits: ['57 historical group statuses and original criteria retained, not promoted.', 'Not browser/auth/provider/native/deployed or whole-capability acceptance. CalendarPage retains the original reminder route and owner internally.'],
};
const out = path.join(root, 'docs/ui-modernisation/evidence/cross-app');
fs.mkdirSync(out, { recursive: true });
fs.writeFileSync(path.join(out, 'PRESERVATION_CENSUS.json'), JSON.stringify(result, null, 2));
console.log('PASS:57 unchanged preservation groups,34 inherited route patterns present in the TypeScript AST.');