import { access, readFile } from "node:fs/promises"; import { createHash } from "node:crypto"; const failures = []; const check = (condition, message) => { if (!condition) failures.push(message); }; const loadJson = async (path) => JSON.parse(await readFile(path, "utf8")); const sha256 = (bytes) => createHash("sha256").update(bytes).digest("hex"); const exists = async (path) => { try { await access(path); return true; } catch { return false; } }; const packageJson = await loadJson("package.json"); for (const script of ["build", "test", "check", "dev", "fixtures:manifest", "baseline:check"]) { check(typeof packageJson.scripts?.[script] === "string", `package.json is missing script: ${script}`); } check(packageJson.type === "module", "package.json must use native ESM"); check(packageJson.devDependencies?.typescript === "5.9.3", "TypeScript version must remain pinned at 5.9.3 for M0"); const manifest = await loadJson("fixtures/manifest.json"); check(manifest.manifestVersion === 1, "fixture manifest version must be 1"); check(manifest.sampleCount === 33, `fixture manifest must contain 33 pairs, got ${manifest.sampleCount}`); check(manifest.samples?.length === 33, "fixture manifest sample array must contain 33 entries"); const ids = new Set(); for (const sample of manifest.samples ?? []) { check(!ids.has(sample.id), `duplicate fixture id: ${sample.id}`); ids.add(sample.id); check(sample.sourceReadOnly === true, `fixture is not read-only: ${sample.id}`); check(sample.rawPath.startsWith("research/"), `raw fixture escapes research root: ${sample.id}`); check(sample.decodedPath.startsWith("research/"), `decoded fixture escapes research root: ${sample.id}`); check(sample.rawPath !== sample.decodedPath, `raw/decoded fixture paths must differ: ${sample.id}`); const rawBytes = await readFile(sample.rawPath); const decodedBytes = await readFile(sample.decodedPath); check(rawBytes.byteLength === sample.rawBytes, `raw byte count drifted: ${sample.id}`); check(decodedBytes.byteLength === sample.decodedBytes, `decoded byte count drifted: ${sample.id}`); check(sha256(rawBytes) === sample.rawSha256, `raw SHA-256 drifted: ${sample.id}`); check(sha256(decodedBytes) === sample.decodedSha256, `decoded SHA-256 drifted: ${sample.id}`); } const researchBaseline = await loadJson("config/research-baseline.json"); check(researchBaseline.readOnly === true, "research baseline must be read-only"); check(researchBaseline.observedInventory.samplePairCount === manifest.sampleCount, "research and fixture sample counts differ"); check(researchBaseline.observedInventory.majorSectionKinds === 8, "M0 section-kind baseline must remain 8"); check(researchBaseline.observedInventory.braceTokenHeadKinds === 214, "M0 token-head baseline must remain 214"); for (const sourcePath of Object.values(researchBaseline.source)) check(await exists(sourcePath), `missing research baseline source: ${sourcePath}`); const schema = await loadJson("schema/jianpu.schema.json"); check(schema.$schema?.includes("2020-12"), "project schema must declare JSON Schema 2020-12"); check(schema.properties?.format?.const === "jianpu-web", "project schema format must be jianpu-web"); check(schema.properties?.schemaVersion?.const === 1, "project schema version must be 1"); for (const definition of ["source", "score", "section", "measure", "event", "unknownBlock", "manualLayout"]) { check(Boolean(schema.$defs?.[definition]), `project schema missing definition: ${definition}`); } const requirements = await loadJson("config/requirements.json"); check(requirements.entryCount === requirements.entries?.length, "requirements entryCount is not reproducible"); const requirementIds = new Set((requirements.entries ?? []).map((entry) => entry.id)); for (const requiredId of ["F-001", "E-001", "T-001", "V-001", "L-001", "A-001", "U-001", "Q-001", "A-008"]) { check(requirementIds.has(requiredId), `requirements traceability is missing ${requiredId}`); } check(requirementIds.size === requirements.entries?.length, "requirements IDs must be unique"); check(requirements.entries.every((entry) => Array.isArray(entry.stages) && Array.isArray(entry.accRefs)), "requirements entries need stage and ACC references"); check(requirements.entries.find((entry) => entry.id === "A-008")?.scope === "out-of-scope", "P2 A-008 must remain explicitly out of scope"); check(Array.isArray(requirements.sources) && requirements.sources.length >= 5, "requirements source documents are incomplete"); for (const source of requirements.sources ?? []) check(await exists(source.path), `missing requirements source document: ${source.path}`); check(Array.isArray(requirements.capabilityDimensions) && requirements.capabilityDimensions.includes("evidence"), "requirements capability dimensions are incomplete"); const licenses = await loadJson("config/resource-licenses.json"); check(Array.isArray(licenses.entries) && licenses.entries.length >= 4, "resource license list is incomplete"); check(licenses.entries.every((entry) => entry.source && entry.license && entry.status), "resource license entries need source, license and status"); check(licenses.entries.find((entry) => entry.id === "research.jpw7-private-assets")?.distribution === "forbidden", "private research assets must be forbidden for distribution"); const writeSet = await loadJson("config/original-format-write-set.json"); check(writeSet.policy === "allow-list-only", "original-format writer must be allow-list-only"); check(writeSet.defaultWrite === "blocked", "original-format writer default must be blocked"); check(writeSet.externalReplayRequired === true, "original-format external replay gate is required"); check(writeSet.unknownContent === "preserve-and-block-unsafe-export", "unknown original content policy is unsafe-export blocking"); check(Array.isArray(writeSet.entries) && writeSet.entries.length >= 5, "original-format write set is incomplete"); const environment = await loadJson("config/acceptance-environment.json"); check(environment.baselineId === "jpw7-web-m0", "acceptance environment baseline ID drifted"); check(environment.primaryAcceptanceProfile.machine === "Windows 11 x64 desktop", "primary acceptance machine must be fixed"); check(environment.primaryAcceptanceProfile.browserProfiles.length === 3, "acceptance browser profile list must contain three profiles"); check(environment.primaryAcceptanceProfile.paperProfile.size === "A4", "acceptance paper must be A4"); check(environment.primaryAcceptanceProfile.paperProfile.printScale === "100%", "acceptance print scale must be 100%"); check(environment.status.includes("measured run evidence"), "acceptance environment must require measured run evidence"); if (failures.length) { console.error(JSON.stringify({ ok: false, failures }, null, 2)); process.exitCode = 1; } else { console.log(JSON.stringify({ ok: true, check: "M0 baseline", samplePairs: manifest.sampleCount, requirementEntries: requirements.entryCount, schemaVersion: schema.properties.schemaVersion.const, originalFormatPolicy: writeSet.policy, acceptanceProfile: environment.baselineId }, null, 2)); }