85 lines
5.8 KiB
JavaScript
85 lines
5.8 KiB
JavaScript
import crypto from "node:crypto";
|
|
import fs from "node:fs";
|
|
import path from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
|
|
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");
|
|
const readJson = (relative) => JSON.parse(fs.readFileSync(path.join(root, relative), "utf8"));
|
|
const sha = (relative) => crypto.createHash("sha256").update(fs.readFileSync(path.join(root, relative))).digest("hex");
|
|
const report = readJson("tests/golden/WBV2-P1424-001/product-implementation-report.json");
|
|
const catalog = readJson("web/app/src/capabilities/blender-feature-route-catalog.json");
|
|
const smokePlan = readJson("tests/golden/WBV2/feature-smoke-plan.json");
|
|
const smokeReport = readJson("tests/golden/WBV2/feature-smoke-report.json");
|
|
const smokeFix = readJson("tests/golden/WBV2/feature-smoke-fix-report.json");
|
|
const wasmManifest = readJson("web/app/public/wasm-module-manifest.json");
|
|
|
|
const countBy = (items, selector) => Object.fromEntries([...new Set(items.map(selector))].sort().map((key) => [key, items.filter((item) => selector(item) === key).length]));
|
|
const unverified = catalog.entries.filter((entry) => entry.evidenceStatus === "UNVERIFIED");
|
|
const failedSmoke = smokeReport.cases.filter((entry) => entry.status !== "PASS");
|
|
const focusedPasses = new Set(smokeFix.fixes.filter((entry) => entry.status === "PASS").map((entry) => entry.featureId));
|
|
const unresolvedSmoke = failedSmoke.filter((entry) => !focusedPasses.has(entry.featureId));
|
|
const implementationResults = report.implementationTasks.map((task) => ({
|
|
id: task.id,
|
|
module: task.module,
|
|
status: task.status,
|
|
resultPath: task.resultPath ?? null,
|
|
resultPresent: task.resultPath ? fs.existsSync(path.join(root, task.resultPath)) : false,
|
|
}));
|
|
const missingResults = implementationResults.filter((entry) => !entry.resultPresent);
|
|
|
|
const audit = {
|
|
schemaVersion: 1,
|
|
auditId: "WBV2-FEATURE-COMPLETENESS-001",
|
|
operation: "DETAILED_MIGRATION_COMPLETENESS_AUDIT",
|
|
status: missingResults.length || unresolvedSmoke.length ? "PARTIAL_BLOCKED" : "IMPLEMENTATION_COMPLETE",
|
|
implementation: {
|
|
workflowModules: { total: report.workflowModules.length, implemented: report.workflowModules.filter((item) => item.status === "implemented").length },
|
|
productTasks: { total: report.implementationTasks.length, implemented: report.implementationTasks.filter((item) => item.status === "implemented").length, missingResultFiles: missingResults },
|
|
},
|
|
routeCoverage: {
|
|
unitCount: catalog.unitCount,
|
|
classifiedCount: catalog.classifiedCount,
|
|
unclassifiedCount: catalog.unclassifiedCount,
|
|
counts: catalog.counts,
|
|
unavailable: catalog.entries.filter((entry) => entry.route === "UNAVAILABLE").map((entry) => entry.id),
|
|
byKind: countBy(catalog.entries, (entry) => entry.kind),
|
|
byFamily: countBy(catalog.entries, (entry) => entry.family),
|
|
},
|
|
behavioralEvidence: {
|
|
status: catalog.behavioralEvidence.status,
|
|
verifiedUnits: catalog.behavioralEvidence.verifiedUnits,
|
|
unverifiedUnits: catalog.behavioralEvidence.unverifiedUnits,
|
|
unverifiedByKind: countBy(unverified, (entry) => entry.kind),
|
|
unverifiedByFamily: countBy(unverified, (entry) => entry.family),
|
|
rule: "route assignment is not behavioral completion",
|
|
},
|
|
smoke: {
|
|
planId: smokePlan.planId,
|
|
totalFeatures: smokePlan.cases.length,
|
|
attempts: smokeReport.cases.reduce((sum, entry) => sum + Number(entry.attempts ?? 0), 0),
|
|
passed: smokeReport.cases.filter((entry) => entry.status === "PASS").length,
|
|
resolved: smokeReport.cases.filter((entry) => entry.status === "PASS").length + focusedPasses.size,
|
|
initialFailed: failedSmoke,
|
|
unresolved: unresolvedSmoke,
|
|
focusedFix: smokeFix.fixes,
|
|
retries: smokeFix.retries,
|
|
},
|
|
wasmLifecycle: wasmManifest.modules.map((module) => ({ id: module.id, lifecycle: module.lifecycle, preload: module.preload, single: module.resources?.single?.fileName ?? null, pthread: module.resources?.pthread?.fileName ?? null })),
|
|
actualMissingOrBlocked: [
|
|
{ id: "IO_IMPORT_WASM_ARTIFACT", status: "RESOLVED", description: "importScene/exportScene is compiled into the source-matched WebEngine WASM and passed native ABI plus focused Chromium checks.", evidence: "tests/golden/WBV2/feature-smoke-fix-report.json" },
|
|
{ id: "BEHAVIORAL_EVIDENCE_OVERLAY", status: "DEFERRED", description: "2720 classified source units still lack exact behavioral receipts; they remain routed, not claimed verified.", evidence: "web/app/src/capabilities/blender-feature-route-catalog.json" },
|
|
],
|
|
nextWork: {
|
|
priority: ["schedule batched evidence for unverified operators/RNA/macros/modifier units"],
|
|
forbidden: ["do not rerun passed feature smoke cases", "do not mark route assignment as behavioral verification", "do not replace WASM with a simulated JS implementation"],
|
|
},
|
|
inputs: {
|
|
productReportSha256: sha("tests/golden/WBV2-P1424-001/product-implementation-report.json"),
|
|
routeCatalogSha256: sha("web/app/src/capabilities/blender-feature-route-catalog.json"),
|
|
smokeReportSha256: sha("tests/golden/WBV2/feature-smoke-report.json"),
|
|
},
|
|
};
|
|
const outputPath = path.join(root, "tests/golden/WBV2/feature-completeness-audit.json");
|
|
fs.writeFileSync(outputPath, `${JSON.stringify(audit, null, 2)}\n`);
|
|
process.stdout.write(`wbv2-feature-completeness-audit status=${audit.status} modules=${audit.implementation.workflowModules.implemented}/${audit.implementation.workflowModules.total} tasks=${audit.implementation.productTasks.implemented}/${audit.implementation.productTasks.total} routes=${audit.routeCoverage.classifiedCount}/${audit.routeCoverage.unitCount} verified=${audit.behavioralEvidence.verifiedUnits} unverified=${audit.behavioralEvidence.unverifiedUnits} smokeResolved=${audit.smoke.resolved}/${audit.smoke.totalFeatures} blocked=${audit.actualMissingOrBlocked.filter((item) => item.status === "BLOCKED").length}\n`);
|