chore: initialize POM 2 repo (docs guards green; release gates recorded NOT RUN pending install)
This commit is contained in:
117
scripts/release-manifest.mjs
Normal file
117
scripts/release-manifest.mjs
Normal file
@@ -0,0 +1,117 @@
|
||||
import { createHash } from "node:crypto";
|
||||
import { existsSync, readFileSync, readdirSync, statSync, writeFileSync } from "node:fs";
|
||||
import { join, relative, resolve } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const root = resolve(fileURLToPath(new URL("..", import.meta.url)));
|
||||
const packageJson = JSON.parse(readFileSync(join(root, "package.json"), "utf8"));
|
||||
|
||||
function walk(path) {
|
||||
if (!existsSync(path)) throw new Error(`Missing packaged path: ${relative(root, path)}`);
|
||||
if (statSync(path).isFile()) return [path];
|
||||
return readdirSync(path, { withFileTypes: true }).flatMap((entry) => walk(join(path, entry.name)));
|
||||
}
|
||||
|
||||
const candidates = packageJson.files
|
||||
.filter((item) => item !== "RELEASE.json")
|
||||
.flatMap((item) => walk(join(root, item)));
|
||||
candidates.push(join(root, "package.json"));
|
||||
|
||||
const sourceInventory = [...new Set(candidates)]
|
||||
.sort()
|
||||
.map((path) => {
|
||||
const data = readFileSync(path);
|
||||
return {
|
||||
path: relative(root, path).replaceAll("\\", "/"),
|
||||
bytes: data.length,
|
||||
sha256: createHash("sha256").update(data).digest("hex"),
|
||||
};
|
||||
});
|
||||
|
||||
function readGate(file, gate) {
|
||||
const path = join(root, file);
|
||||
if (!existsSync(path)) return { status: "NOT RUN", reason: `Missing evidence file: ${file}` };
|
||||
try {
|
||||
const data = JSON.parse(readFileSync(path, "utf8"));
|
||||
const passed = data.passed === true || (data.summary && data.summary.passed === true);
|
||||
return {
|
||||
status: passed ? "PASS" : "FAIL",
|
||||
timestamp: data.timestamp || null,
|
||||
scope: data.scope || gate,
|
||||
command: data.command || null,
|
||||
details: data,
|
||||
};
|
||||
} catch {
|
||||
return { status: "NOT RUN", reason: `Unparsable evidence: ${file}` };
|
||||
}
|
||||
}
|
||||
|
||||
const gates = {
|
||||
typescript: readGate("00_admin/validation/typecheck.json", "tsc --noEmit"),
|
||||
behavioralTests: readGate("00_admin/validation/tests.json", "npm test"),
|
||||
runtimeSmoke: readGate("00_admin/validation/smoke.json", "npm run smoke"),
|
||||
structuralVerification: readGate("00_admin/validation/structural.json", "node scripts/verify.mjs"),
|
||||
themeSchemaValidation: readGate("00_admin/validation/theme.json", "theme schema validation"),
|
||||
npmPackDryRun: readGate("00_admin/validation/pack.json", "npm pack --dry-run"),
|
||||
};
|
||||
|
||||
const releaseDate = new Date().toISOString().split("T")[0];
|
||||
|
||||
const release = {
|
||||
name: "POM — Produce Magnum Opus",
|
||||
package: packageJson.name,
|
||||
version: packageJson.version,
|
||||
schemaVersion: "2.0.0",
|
||||
releaseDate,
|
||||
status: "release-candidate",
|
||||
summary: "OMP-native production kernel with evidence-gated stages, native task/IRC delegation, optional Swarm DAGs, Obsidian/Quartz knowledge projection, cinematic TUI hooks, and verified archive delivery.",
|
||||
compatibility: {
|
||||
bun: packageJson.engines.bun,
|
||||
ompPackages: ">=16.4.6 <17",
|
||||
module: "ESM",
|
||||
},
|
||||
inventory: {
|
||||
stages: 9,
|
||||
customTools: 7,
|
||||
agents: 14,
|
||||
prompts: 13,
|
||||
rules: 3,
|
||||
themes: 2,
|
||||
behavioralTests: 9,
|
||||
sourceFilesHashed: sourceInventory.length,
|
||||
sourceBytesHashed: sourceInventory.reduce((sum, item) => sum + item.bytes, 0),
|
||||
selfHashPolicy: "RELEASE.json is excluded from its own source inventory; final package and archive hashes are recorded externally.",
|
||||
},
|
||||
verification: {
|
||||
typescript: gates.typescript,
|
||||
behavioralTests: gates.behavioralTests,
|
||||
runtimeSmoke: gates.runtimeSmoke,
|
||||
structuralVerification: gates.structuralVerification,
|
||||
themeSchemaValidation: gates.themeSchemaValidation,
|
||||
npmPackDryRun: gates.npmPackDryRun,
|
||||
productionDependencyAudit: { status: "NOT RUN", reason: "Requires network access to npm registry" },
|
||||
fullDevelopmentTreeAudit: { status: "NOT RUN", reason: "Requires network access to npm registry" },
|
||||
ompPluginDoctor: { status: "NOT RUN", reason: "Requires Bun + OMP installation" },
|
||||
interactiveTuiSmoke: { status: "NOT RUN", reason: "Requires a live OMP terminal host" },
|
||||
},
|
||||
releaseGates: [
|
||||
"Run npm run verify",
|
||||
"Run npm run doctor in a Bun + OMP installation",
|
||||
"Open /pom and verify the overlay, HUD, status, cards, and /pom theme install",
|
||||
"Create a smoke project, validate stage 0, and verify a checkpoint archive",
|
||||
],
|
||||
sourceInventory,
|
||||
};
|
||||
|
||||
const rendered = `${JSON.stringify(release, null, 2)}\n`;
|
||||
const output = join(root, "RELEASE.json");
|
||||
if (process.argv.includes("--check")) {
|
||||
if (!existsSync(output) || readFileSync(output, "utf8") !== rendered) {
|
||||
console.error("RELEASE.json is stale; run npm run release:manifest");
|
||||
process.exit(1);
|
||||
}
|
||||
console.log("POM release manifest PASS");
|
||||
} else {
|
||||
writeFileSync(output, rendered);
|
||||
console.log(`Wrote RELEASE.json · ${sourceInventory.length} files · ${release.inventory.sourceBytesHashed} bytes`);
|
||||
}
|
||||
Reference in New Issue
Block a user