chore: fix prettier formatting, regenerate phase9 manifest, add missing config files (.env.example, .node-version, .nvmrc, CI workflow)
CI / test (push) Failing after 28s
CI / test (push) Failing after 28s
This commit is contained in:
@@ -4,8 +4,20 @@ import path from 'node:path';
|
||||
|
||||
const root = path.resolve(import.meta.dirname, '..');
|
||||
const manifestPath = path.join(root, 'PHASE_16_PACKAGE_MANIFEST_v1.0.json');
|
||||
const excludedDirectories = new Set(['.git', '.next', 'node_modules', 'coverage', 'playwright-report', 'test-results', '__pycache__']);
|
||||
const excludedFiles = new Set(['PHASE_16_PACKAGE_MANIFEST_v1.0.json', 'package-lock.json', 'tsconfig.tsbuildinfo']);
|
||||
const excludedDirectories = new Set([
|
||||
'.git',
|
||||
'.next',
|
||||
'node_modules',
|
||||
'coverage',
|
||||
'playwright-report',
|
||||
'test-results',
|
||||
'__pycache__',
|
||||
]);
|
||||
const excludedFiles = new Set([
|
||||
'PHASE_16_PACKAGE_MANIFEST_v1.0.json',
|
||||
'package-lock.json',
|
||||
'tsconfig.tsbuildinfo',
|
||||
]);
|
||||
|
||||
async function walk(directory) {
|
||||
const files = [];
|
||||
@@ -26,7 +38,11 @@ const entries = [];
|
||||
for (const file of files) {
|
||||
const contents = await readFile(file.fullPath);
|
||||
const details = await stat(file.fullPath);
|
||||
entries.push({ path: file.relative, bytes: details.size, sha256: createHash('sha256').update(contents).digest('hex') });
|
||||
entries.push({
|
||||
path: file.relative,
|
||||
bytes: details.size,
|
||||
sha256: createHash('sha256').update(contents).digest('hex'),
|
||||
});
|
||||
}
|
||||
const manifest = {
|
||||
schema_version: '1.0',
|
||||
|
||||
@@ -77,7 +77,7 @@ const integrationVisual = await readFile(
|
||||
path.join(repositoryRoot, 'tests/visual/integrations.visual.spec.ts'),
|
||||
'utf8',
|
||||
);
|
||||
if (!componentVisual.includes("hpc.theme.preference")) {
|
||||
if (!componentVisual.includes('hpc.theme.preference')) {
|
||||
failures.push('Component visual test does not use the canonical theme storage key.');
|
||||
}
|
||||
if (componentVisual.includes('rdg-theme-preference')) {
|
||||
|
||||
@@ -3,7 +3,15 @@ import path from 'node:path';
|
||||
import { fail, pass, repositoryRoot, sha256 } from './lib.mjs';
|
||||
|
||||
const manifestName = 'PHASE_16_PACKAGE_MANIFEST_v1.0.json';
|
||||
const excludedDirectories = new Set(['.git', '.next', 'node_modules', 'coverage', 'playwright-report', 'test-results', '__pycache__']);
|
||||
const excludedDirectories = new Set([
|
||||
'.git',
|
||||
'.next',
|
||||
'node_modules',
|
||||
'coverage',
|
||||
'playwright-report',
|
||||
'test-results',
|
||||
'__pycache__',
|
||||
]);
|
||||
const excludedFiles = new Set([manifestName, 'package-lock.json', 'tsconfig.tsbuildinfo']);
|
||||
|
||||
async function walk(directory) {
|
||||
@@ -22,25 +30,41 @@ async function walk(directory) {
|
||||
|
||||
const manifest = JSON.parse(await readFile(path.join(repositoryRoot, manifestName), 'utf8'));
|
||||
const failures = [];
|
||||
if (manifest.project !== 'RentalDriveGo' || manifest.phase !== 16) failures.push('Phase 16 manifest identity is invalid.');
|
||||
if (manifest.release_status !== 'Launch aborted before deployment') failures.push('Manifest release status is not the recorded Phase 16 decision.');
|
||||
if (!Array.isArray(manifest.files) || manifest.file_count !== manifest.files.length) failures.push('Manifest file count is invalid.');
|
||||
if (manifest.project !== 'RentalDriveGo' || manifest.phase !== 16)
|
||||
failures.push('Phase 16 manifest identity is invalid.');
|
||||
if (manifest.release_status !== 'Launch aborted before deployment')
|
||||
failures.push('Manifest release status is not the recorded Phase 16 decision.');
|
||||
if (!Array.isArray(manifest.files) || manifest.file_count !== manifest.files.length)
|
||||
failures.push('Manifest file count is invalid.');
|
||||
const listed = new Set();
|
||||
for (const entry of manifest.files ?? []) {
|
||||
if (typeof entry.path !== 'string' || path.isAbsolute(entry.path) || entry.path.split('/').includes('..')) {
|
||||
failures.push(`Unsafe manifest path: ${String(entry.path)}`); continue;
|
||||
if (
|
||||
typeof entry.path !== 'string' ||
|
||||
path.isAbsolute(entry.path) ||
|
||||
entry.path.split('/').includes('..')
|
||||
) {
|
||||
failures.push(`Unsafe manifest path: ${String(entry.path)}`);
|
||||
continue;
|
||||
}
|
||||
if (listed.has(entry.path)) {
|
||||
failures.push(`${entry.path}: duplicate manifest entry.`);
|
||||
continue;
|
||||
}
|
||||
if (listed.has(entry.path)) { failures.push(`${entry.path}: duplicate manifest entry.`); continue; }
|
||||
listed.add(entry.path);
|
||||
try {
|
||||
const filePath = path.join(repositoryRoot, entry.path);
|
||||
const details = await stat(filePath);
|
||||
if (!details.isFile() || details.size !== entry.bytes) failures.push(`${entry.path}: file metadata changed.`);
|
||||
if (!details.isFile() || details.size !== entry.bytes)
|
||||
failures.push(`${entry.path}: file metadata changed.`);
|
||||
if ((await sha256(filePath)) !== entry.sha256) failures.push(`${entry.path}: SHA-256 changed.`);
|
||||
} catch { failures.push(`${entry.path}: file is missing.`); }
|
||||
} catch {
|
||||
failures.push(`${entry.path}: file is missing.`);
|
||||
}
|
||||
}
|
||||
const actual = new Set(await walk(repositoryRoot));
|
||||
for (const file of actual) if (!listed.has(file)) failures.push(`${file}: unlisted repository file.`);
|
||||
for (const file of listed) if (!actual.has(file)) failures.push(`${file}: listed but excluded or absent.`);
|
||||
for (const file of actual)
|
||||
if (!listed.has(file)) failures.push(`${file}: unlisted repository file.`);
|
||||
for (const file of listed)
|
||||
if (!actual.has(file)) failures.push(`${file}: listed but excluded or absent.`);
|
||||
if (failures.length) fail(failures);
|
||||
else pass(`Phase 16 package manifest verifies ${listed.size} repository files with no extras.`);
|
||||
|
||||
@@ -16,16 +16,51 @@ const required = [
|
||||
];
|
||||
const failures = [];
|
||||
for (const file of required) {
|
||||
try { await access(path.join(repositoryRoot, file)); }
|
||||
catch { failures.push(`Missing required Phase 16 artifact: ${file}`); }
|
||||
try {
|
||||
await access(path.join(repositoryRoot, file));
|
||||
} catch {
|
||||
failures.push(`Missing required Phase 16 artifact: ${file}`);
|
||||
}
|
||||
}
|
||||
const status = await readFile(path.join(repositoryRoot, 'docs/phase16/FINAL_LAUNCH_STATUS_v1.0.md'), 'utf8');
|
||||
const auth = await readFile(path.join(repositoryRoot, 'docs/phase16/LAUNCH_AUTHORIZATION_RECORD_v1.0.md'), 'utf8');
|
||||
const flags = await readFile(path.join(repositoryRoot, 'docs/phase16/FEATURE_FLAG_RELEASE_STATE_v1.0.csv'), 'utf8');
|
||||
const secrets = await readFile(path.join(repositoryRoot, 'docs/phase16/SECRET_PROVISIONING_RECORD_v1.0.csv'), 'utf8');
|
||||
if (!status.includes('Launch aborted before deployment')) failures.push('Final launch status is missing or inconsistent.');
|
||||
if (!auth.includes('**Rejected**')) failures.push('Launch authorization is not explicitly rejected.');
|
||||
if (/\btrue\b/.test(flags.split('\n').slice(1).map((line) => line.split(',')[1]).join('\n'))) failures.push('A Phase 16 production feature flag is enabled.');
|
||||
if (!secrets.includes('No production credentials exist') && !secrets.includes('No credential supplied')) failures.push('Secret provisioning record does not explicitly preserve missing credentials as blockers.');
|
||||
const status = await readFile(
|
||||
path.join(repositoryRoot, 'docs/phase16/FINAL_LAUNCH_STATUS_v1.0.md'),
|
||||
'utf8',
|
||||
);
|
||||
const auth = await readFile(
|
||||
path.join(repositoryRoot, 'docs/phase16/LAUNCH_AUTHORIZATION_RECORD_v1.0.md'),
|
||||
'utf8',
|
||||
);
|
||||
const flags = await readFile(
|
||||
path.join(repositoryRoot, 'docs/phase16/FEATURE_FLAG_RELEASE_STATE_v1.0.csv'),
|
||||
'utf8',
|
||||
);
|
||||
const secrets = await readFile(
|
||||
path.join(repositoryRoot, 'docs/phase16/SECRET_PROVISIONING_RECORD_v1.0.csv'),
|
||||
'utf8',
|
||||
);
|
||||
if (!status.includes('Launch aborted before deployment'))
|
||||
failures.push('Final launch status is missing or inconsistent.');
|
||||
if (!auth.includes('**Rejected**'))
|
||||
failures.push('Launch authorization is not explicitly rejected.');
|
||||
if (
|
||||
/\btrue\b/.test(
|
||||
flags
|
||||
.split('\n')
|
||||
.slice(1)
|
||||
.map((line) => line.split(',')[1])
|
||||
.join('\n'),
|
||||
)
|
||||
)
|
||||
failures.push('A Phase 16 production feature flag is enabled.');
|
||||
if (
|
||||
!secrets.includes('No production credentials exist') &&
|
||||
!secrets.includes('No credential supplied')
|
||||
)
|
||||
failures.push(
|
||||
'Secret provisioning record does not explicitly preserve missing credentials as blockers.',
|
||||
);
|
||||
if (failures.length) fail(failures);
|
||||
else pass('Phase 16 no-go governance, disabled feature state, blocked approvals, and evidence package are explicit and internally consistent.');
|
||||
else
|
||||
pass(
|
||||
'Phase 16 no-go governance, disabled feature state, blocked approvals, and evidence package are explicit and internally consistent.',
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user