const REQUIRED_RELEASE_GATES = [ { id: "diff-check", label: "Git diff check", command: "git diff --check", }, { id: "vendor-sync", label: "Vendor sync guard", command: "wasm-port/tools/verify_vendor_sync.sh", expectedOutput: "vendor sync up to date", }, { id: "standalone-cnc-semantics", label: "Standalone CNC semantics guard", command: "wasm-port/tools/verify_no_standalone_cnc_semantics.sh", expectedOutput: "standalone CNC semantics guard complete", }, { id: "interp-wasm", label: "Interpreter WASM Node smoke", command: "SKIP_INTERP_BUILD=1 wasm-port/tests/wasm/node/verify_interp_wasm.sh", expectedOutput: "interp_wasm_node_smoke=ok", }, { id: "sim-config-inventory", label: "Sim config inventory WASM Node smoke", command: "SKIP_INTERP_BUILD=1 wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh", expectedOutput: "sim_configs_wasm_node_inventory_unexpected_fail=0", }, { id: "ini-panel-browser", label: "INI panel browser smoke", command: "SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_ini_panel_browser.sh", expectedOutput: "browser_ini_shell_integration_workflow_smoke=ok", }, { id: "opfs-session-browser", label: "OPFS session browser workflow smoke", command: "SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_opfs_session_workflow_browser.sh", expectedOutput: "browser_opfs_session_workflow_smoke=ok", }, { id: "release-artifact-url-browser", label: "Release artifact URL browser workflow smoke", command: "SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_release_artifact_url_workflow_browser.sh", expectedOutput: "browser_release_artifact_url_workflow_smoke=ok", }, { id: "project-batch-acceptance", label: "Project batch acceptance workflow smoke", command: "wasm-port/tests/sdk/node/verify_project_batch_acceptance_workflow.sh", expectedOutput: "project_batch_acceptance_workflow_node_smoke=ok", }, { id: "ui-node-smokes", label: "UI Node smokes", command: "wasm-port/tests/ui/node/verify_ui_node_smokes.sh", expectedOutput: "ui_node_smokes=ok", }, { id: "host-smokes", label: "Host aggregate smoke", command: "wasm-port/tests/host/verify_host_smokes.sh", expectedOutput: "host_wasm_opfs_browser_smokes=ok", }, { id: "project-release-gate", label: "Project release gate", command: "wasm-port/tests/host/verify_project_release_gate.sh", expectedOutput: "project_release_gate=ok", }, ]; const DEFAULT_SIM_CONFIG_INVENTORY_BASELINE = { executed: 28, passed: 28, skipped: 131, unexpectedFail: 0, }; const BLOCKED_RUNTIME_FAMILIES = [ "L4-USER-M-PROCESS", "L4-TOOL-DB", "L4-PYTHON-REMAP", ]; const PROJECT_RELEASE_READINESS_ARTIFACT_API = "project-release-readiness-report"; const PROJECT_RELEASE_READINESS_ARTIFACT_VERSION = 1; function outputContains(observedOutputs, expectedOutput) { return observedOutputs.some((output) => String(output).includes(expectedOutput)); } function readGatePassed({ gate, gateResults, observedOutputs }) { if (gateResults?.[gate.id] === true) { return true; } if (gate.expectedOutput && outputContains(observedOutputs, gate.expectedOutput)) { return true; } return false; } function objectOrEmpty(value) { return value && typeof value === "object" ? value : {}; } function arrayOrEmpty(value) { return Array.isArray(value) ? value : []; } function normalizeObservedOutput(value) { if (Array.isArray(value)) { return value.map((output) => String(output)); } if (typeof value === "string") { return [value]; } return []; } function normalizeAxisScreenshotArtifacts(value) { return arrayOrEmpty(value) .map((artifact) => objectOrEmpty(artifact)) .filter(({ apiName }) => apiName === "real-browser-simulation-axis-screenshot-artifact") .map((artifact) => ({ apiName: artifact.apiName, artifactVersion: artifact.artifactVersion ?? 1, viewportName: artifact.viewportName ?? "unknown", windowSize: artifact.windowSize ?? "unknown", query: artifact.query ?? "", screenshotBytes: Number.isFinite(artifact.screenshotBytes) ? artifact.screenshotBytes : 0, validatedBy: artifact.validatedBy ?? "unknown", fullDiagnosticsApiName: artifact.fullDiagnosticsApiName ?? null, fullDiagnosticsPath: artifact.fullDiagnosticsPath ?? null, previewRenderer: artifact.previewRenderer ?? null, previewViewMode: artifact.previewViewMode ?? null, threeReady: artifact.threeReady === true, threePathPoints: Number.isFinite(artifact.threePathPoints) ? artifact.threePathPoints : 0, screenshotPath: artifact.screenshotPath ?? null, diagnosticsPath: artifact.diagnosticsPath ?? null, })); } function createAxisScreenshotArtifactSummary(axisScreenshotArtifacts = []) { const artifacts = normalizeAxisScreenshotArtifacts(axisScreenshotArtifacts); const expectedViewports = ["desktop-preview", "desktop-dro", "mobile-preview", "mobile-mdi"]; const viewportNames = artifacts.map(({ viewportName }) => viewportName); const missingViewports = expectedViewports.filter((name) => !viewportNames.includes(name)); const undersizedViewports = artifacts .filter(({ screenshotBytes }) => screenshotBytes < 10000) .map(({ viewportName }) => viewportName); const ready = artifacts.length === 0 || (missingViewports.length === 0 && undersizedViewports.length === 0); return { apiName: "project-release-axis-screenshot-artifact-summary", summaryVersion: 1, phase: ready ? "ready" : "blocked", ready, expectedViewports, artifactCount: artifacts.length, viewportNames, missingViewports, undersizedViewports, totalScreenshotBytes: artifacts.reduce((sum, { screenshotBytes }) => sum + screenshotBytes, 0), artifacts, }; } export function createProjectReleaseGateManifest() { return { apiName: "project-release-gate-manifest", manifestVersion: 1, gateCount: REQUIRED_RELEASE_GATES.length, gateIds: REQUIRED_RELEASE_GATES.map(({ id }) => id), gates: REQUIRED_RELEASE_GATES.map((gate) => ({ id: gate.id, label: gate.label, command: gate.command, expectedOutput: gate.expectedOutput ?? null, })), }; } export function createProjectReleaseGateExecutionManifest({ gateResults = {}, observedOutputs = [], gateObservedOutputs = {}, manifest = createProjectReleaseGateManifest(), } = {}) { const globalOutputs = normalizeObservedOutput(observedOutputs); const gates = arrayOrEmpty(manifest?.gates); const rows = gates.map((gate) => { const scopedOutputs = [ ...normalizeObservedOutput(gateObservedOutputs?.[gate.id]), ...globalOutputs, ]; const matchedOutput = gate.expectedOutput ? scopedOutputs.find((output) => output.includes(gate.expectedOutput)) ?? null : null; const explicitPassed = gateResults?.[gate.id] === true; const passed = explicitPassed || matchedOutput !== null; return { id: gate.id, label: gate.label, command: gate.command, expectedOutput: gate.expectedOutput ?? null, observedOutput: matchedOutput, observedOutputCount: scopedOutputs.length, status: passed ? "passed" : "unknown", passed, evidence: matchedOutput !== null ? "expected-output" : (explicitPassed ? "explicit-result" : "missing"), }; }); return { apiName: "project-release-gate-execution-manifest", manifestVersion: 1, phase: rows.every(({ passed }) => passed) ? "ready" : "waiting", ready: rows.every(({ passed }) => passed), gateCount: rows.length, passedCount: rows.filter(({ passed }) => passed).length, unknownCount: rows.filter(({ passed }) => !passed).length, missingGateIds: rows.filter(({ passed }) => !passed).map(({ id }) => id), manifest, rows, }; } export function createProjectReleaseGateExecutionSummaryViewModel( executionManifest = createProjectReleaseGateExecutionManifest(), ) { const rows = arrayOrEmpty(executionManifest?.rows); const ready = executionManifest?.ready === true; const missingGateIds = arrayOrEmpty(executionManifest?.missingGateIds); const statusText = ready ? "Ready" : "Waiting"; const detailText = ready ? "All release gate evidence present" : (missingGateIds.length > 0 ? missingGateIds.join(", ") : "release gate evidence missing"); const evidenceCounts = rows.reduce((counts, row) => ({ ...counts, [row.evidence ?? "missing"]: (counts[row.evidence ?? "missing"] ?? 0) + 1, }), {}); return { apiName: "project-release-gate-execution-summary-view-model", viewModelVersion: 1, phase: ready ? "ready" : "waiting", ready, title: "Project release gate execution", statusText, detailText, statusLine: `${statusText}: ${detailText}`, gateCount: executionManifest?.gateCount ?? rows.length, passedCount: executionManifest?.passedCount ?? rows.filter(({ passed }) => passed === true).length, unknownCount: executionManifest?.unknownCount ?? rows.filter(({ passed }) => passed !== true).length, missingGateIds, nextGateId: missingGateIds[0] ?? null, evidenceCounts, rows: [ { id: "gate-results", label: "Release gate evidence", value: `${executionManifest?.passedCount ?? 0}/${executionManifest?.gateCount ?? rows.length} passed`, }, { id: "unknown-gates", label: "Unknown gates", value: `${executionManifest?.unknownCount ?? 0}`, }, { id: "next-gate", label: "Next gate", value: missingGateIds[0] ?? "none", }, { id: "expected-output-evidence", label: "Expected-output evidence", value: `${evidenceCounts["expected-output"] ?? 0}`, }, { id: "explicit-result-evidence", label: "Explicit-result evidence", value: `${evidenceCounts["explicit-result"] ?? 0}`, }, { id: "missing", label: "Missing gate evidence", value: missingGateIds.length > 0 ? missingGateIds.join(", ") : "none", }, ], gateRows: rows.map((row) => ({ id: row.id, label: row.label, command: row.command, expectedOutput: row.expectedOutput ?? null, observedOutput: row.observedOutput ?? null, evidence: row.evidence ?? "missing", status: row.status ?? (row.passed === true ? "passed" : "unknown"), passed: row.passed === true, })), }; } export function createProjectReleaseGateResultMatrix({ gateResults = {}, observedOutputs = [], manifest = createProjectReleaseGateManifest(), } = {}) { const outputs = Array.isArray(observedOutputs) ? observedOutputs : []; const gates = Array.isArray(manifest?.gates) ? manifest.gates : []; const rows = gates.map((gate) => { const passed = readGatePassed({ gate, gateResults, observedOutputs: outputs }); return { id: gate.id, label: gate.label, command: gate.command, expectedOutput: gate.expectedOutput ?? null, status: passed ? "passed" : "unknown", passed, }; }); return { apiName: "project-release-gate-result-matrix", matrixVersion: 1, phase: rows.every(({ passed }) => passed) ? "ready" : "waiting", ready: rows.every(({ passed }) => passed), gateCount: rows.length, passedCount: rows.filter(({ passed }) => passed).length, unknownCount: rows.filter(({ passed }) => !passed).length, missingGateIds: rows.filter(({ passed }) => !passed).map(({ id }) => id), manifest, rows, }; } export function createProjectReleaseGateActionPlan({ gateResultMatrix = null, gateResults = {}, observedOutputs = [], manifest = createProjectReleaseGateManifest(), } = {}) { const matrix = gateResultMatrix && typeof gateResultMatrix === "object" ? gateResultMatrix : createProjectReleaseGateResultMatrix({ gateResults, observedOutputs, manifest }); const rows = Array.isArray(matrix.rows) ? matrix.rows : []; const pendingRows = rows.filter(({ passed }) => passed !== true); const completedRows = rows.filter(({ passed }) => passed === true); const commands = pendingRows.map((gate, index) => ({ step: index + 1, gateId: gate.id, label: gate.label, command: gate.command, expectedOutput: gate.expectedOutput ?? null, })); const nextGate = commands[0] ?? null; const shellScript = commands.length > 0 ? [ "#!/usr/bin/env bash", "set -euo pipefail", "", ...commands.map(({ command }) => command), "", ].join("\n") : "# Project release gates are already marked passed.\n"; return { apiName: "project-release-gate-action-plan", planVersion: 1, phase: commands.length === 0 ? "ready" : "waiting", ready: commands.length === 0, gateCount: rows.length, completedCount: completedRows.length, pendingCount: pendingRows.length, completedGateIds: completedRows.map(({ id }) => id), pendingGateIds: pendingRows.map(({ id }) => id), nextGateId: nextGate?.gateId ?? null, nextCommand: nextGate?.command ?? null, commands, shellScript, rows: commands.map(({ gateId, label, command, expectedOutput }) => ({ id: gateId, label, value: command, expectedOutput, })), }; } const BATCH_ACCEPTANCE_CAPABILITY_TYPES = [ "api", "workflow", "gate", "browser", ]; function normalizeBatchCapabilities(capabilities) { return arrayOrEmpty(capabilities) .map((capability) => objectOrEmpty(capability)) .filter(({ id, type }) => typeof id === "string" && typeof type === "string") .map((capability) => ({ id: capability.id, type: capability.type, label: capability.label ?? capability.id, evidence: capability.evidence ?? null, command: capability.command ?? null, })); } export function createProjectBatchAcceptanceCapabilityMatrix({ capabilities = [], } = {}) { const normalizedCapabilities = normalizeBatchCapabilities(capabilities); const acceptedCapabilities = normalizedCapabilities.filter(({ type }) => BATCH_ACCEPTANCE_CAPABILITY_TYPES.includes(type) ); const rejectedCapabilities = normalizedCapabilities.filter(({ type }) => !BATCH_ACCEPTANCE_CAPABILITY_TYPES.includes(type) ); const capabilityTypes = [...new Set(acceptedCapabilities.map(({ type }) => type))]; const countsByType = BATCH_ACCEPTANCE_CAPABILITY_TYPES.reduce((counts, type) => ({ ...counts, [type]: acceptedCapabilities.filter((capability) => capability.type === type).length, }), {}); const missing = acceptedCapabilities.length > 0 ? [] : ["batch-capability"]; return { apiName: "project-batch-acceptance-capability-matrix", matrixVersion: 1, phase: missing.length === 0 ? "ready" : "blocked", ready: missing.length === 0, acceptedCapabilityTypes: BATCH_ACCEPTANCE_CAPABILITY_TYPES, capabilityTypes, capabilityCount: acceptedCapabilities.length, acceptedCount: acceptedCapabilities.length, rejectedCount: rejectedCapabilities.length, countsByType, capabilities: acceptedCapabilities, rejectedCapabilities, missing, rows: [ { id: "accepted-capabilities", label: "Accepted capabilities", value: `${acceptedCapabilities.length}`, }, { id: "accepted-types", label: "Accepted capability types", value: capabilityTypes.join(", ") || "none", }, { id: "rejected-capabilities", label: "Rejected capabilities", value: `${rejectedCapabilities.length}`, }, { id: "missing", label: "Missing capability evidence", value: missing.length > 0 ? missing.join(", ") : "none", }, ], }; } export function createProjectBatchAcceptanceWorkflow({ batchId = "current-batch", capabilities = [], gateResults = {}, observedOutputs = [], } = {}) { const capabilityMatrix = createProjectBatchAcceptanceCapabilityMatrix({ capabilities }); const gateManifest = createProjectReleaseGateManifest(); const gateResultMatrix = createProjectReleaseGateResultMatrix({ gateResults, observedOutputs, manifest: gateManifest, }); const batchGatePassed = gateResultMatrix.rows .find(({ id }) => id === "project-batch-acceptance")?.passed === true; const ready = capabilityMatrix.ready && batchGatePassed; const missing = [ ...capabilityMatrix.missing, ...(batchGatePassed ? [] : ["project-batch-acceptance"]), ]; return { apiName: "project-batch-acceptance-workflow", workflowVersion: 1, phase: ready ? "ready" : "blocked", ready, batchId, acceptedCapabilityTypes: capabilityMatrix.acceptedCapabilityTypes, capabilityTypes: capabilityMatrix.capabilityTypes, capabilityCount: capabilityMatrix.capabilityCount, capabilityMatrix, capabilities: capabilityMatrix.capabilities, rejectedCapabilities: capabilityMatrix.rejectedCapabilities, gateResultMatrix, missing, rows: [ { id: "batch", label: "Batch", value: batchId, }, { id: "capabilities", label: "Accepted capabilities", value: capabilityMatrix.capabilityTypes.join(", ") || "none", }, { id: "project-batch-acceptance", label: "Batch acceptance gate", value: batchGatePassed ? "passed" : "unknown", }, { id: "missing", label: "Missing acceptance evidence", value: missing.length > 0 ? missing.join(", ") : "none", }, ], }; } export function createProjectBatchAcceptanceSummaryViewModel( workflow = createProjectBatchAcceptanceWorkflow(), ) { const missing = arrayOrEmpty(workflow?.missing); const ready = workflow?.ready === true; const statusText = ready ? "Ready" : "Blocked"; const detailText = ready ? "Batch acceptance evidence complete" : (missing.length > 0 ? missing.join(", ") : "batch acceptance evidence missing"); return { apiName: "project-batch-acceptance-summary-view-model", viewModelVersion: 1, phase: ready ? "ready" : "blocked", ready, title: "Project batch acceptance", statusText, detailText, statusLine: `${statusText}: ${detailText}`, rows: [ { id: "batch", label: "Batch", value: workflow?.batchId ?? "current-batch", }, { id: "capability-count", label: "Accepted capability count", value: `${workflow?.capabilityCount ?? 0}`, }, { id: "capability-types", label: "Accepted capability types", value: arrayOrEmpty(workflow?.capabilityTypes).join(", ") || "none", }, { id: "missing", label: "Missing acceptance evidence", value: missing.length > 0 ? missing.join(", ") : "none", }, ], }; } export function createProjectBatchAcceptanceActionPlan( workflow = createProjectBatchAcceptanceWorkflow(), ) { const missing = arrayOrEmpty(workflow?.missing); const ready = workflow?.ready === true; const commands = ready ? [] : [ ...(missing.includes("project-batch-acceptance") ? [{ step: 1, id: "project-batch-acceptance", label: "Run project batch acceptance workflow smoke", command: "wasm-port/tests/sdk/node/verify_project_batch_acceptance_workflow.sh", expectedOutput: "project_batch_acceptance_workflow_node_smoke=ok", }] : []), ]; const shellScript = commands.length > 0 ? [ "#!/usr/bin/env bash", "set -euo pipefail", "", ...commands.map(({ command }) => command), "", ].join("\n") : (ready ? "# Project batch acceptance workflow is already ready.\n" : "# Project batch acceptance workflow requires at least one API, workflow, gate, or browser capability.\n"); return { apiName: "project-batch-acceptance-action-plan", planVersion: 1, phase: ready ? "ready" : "blocked", ready, missing, commandCount: commands.length, nextActionId: commands[0]?.id ?? (missing[0] ?? null), nextCommand: commands[0]?.command ?? null, commands, shellScript, rows: [ { id: "batch-acceptance", label: "Batch acceptance", value: ready ? "ready" : "blocked", }, { id: "missing", label: "Missing acceptance evidence", value: missing.length > 0 ? missing.join(", ") : "none", }, { id: "next-command", label: "Next command", value: commands[0]?.command ?? "none", }, ], }; } export function createProjectBatchAcceptanceChecklist({ workflow = createProjectBatchAcceptanceWorkflow(), actionPlan = createProjectBatchAcceptanceActionPlan(workflow), } = {}) { const capabilityMatrix = objectOrEmpty(workflow?.capabilityMatrix); const ready = workflow?.ready === true; const items = [ { id: "accepted-capability", label: "At least one API, workflow, gate, or browser capability", status: capabilityMatrix.ready === true ? "pass" : "blocked", passed: capabilityMatrix.ready === true, detail: arrayOrEmpty(workflow?.capabilityTypes).join(", ") || "none", }, { id: "batch-acceptance-gate", label: "Batch acceptance gate evidence", status: !arrayOrEmpty(workflow?.missing).includes("project-batch-acceptance") ? "pass" : "blocked", passed: !arrayOrEmpty(workflow?.missing).includes("project-batch-acceptance"), detail: workflow?.gateResultMatrix?.rows ?.find?.(({ id }) => id === "project-batch-acceptance")?.status ?? "unknown", }, { id: "next-action", label: "Next acceptance action", status: actionPlan?.nextActionId ? "blocked" : "pass", passed: !actionPlan?.nextActionId, detail: actionPlan?.nextCommand ?? "none", }, ]; return { apiName: "project-batch-acceptance-checklist", checklistVersion: 1, phase: ready ? "ready" : "blocked", ready, batchId: workflow?.batchId ?? "current-batch", passedCount: items.filter(({ passed }) => passed).length, blockedCount: items.filter(({ passed }) => !passed).length, items, rows: items.map(({ id, label, status, detail }) => ({ id, label, value: `${status}: ${detail}`, })), }; } export function createProjectBatchAcceptanceReport({ batchId = "current-batch", capabilities = [], gateResults = {}, observedOutputs = [], } = {}) { const capabilityMatrix = createProjectBatchAcceptanceCapabilityMatrix({ capabilities }); const workflow = createProjectBatchAcceptanceWorkflow({ batchId, capabilities, gateResults, observedOutputs, }); const summaryViewModel = createProjectBatchAcceptanceSummaryViewModel(workflow); const actionPlan = createProjectBatchAcceptanceActionPlan(workflow); const checklist = createProjectBatchAcceptanceChecklist({ workflow, actionPlan }); return { apiName: "project-batch-acceptance-report", reportVersion: 1, phase: workflow.phase, ready: workflow.ready, batchId: workflow.batchId, missing: [...workflow.missing], capabilityMatrix, workflow, summaryViewModel, actionPlan, checklist, rows: [ { id: "summary", label: "Batch acceptance summary", value: summaryViewModel.statusLine, }, { id: "capabilities", label: "Accepted capabilities", value: capabilityMatrix.capabilityTypes.join(", ") || "none", }, { id: "checklist", label: "Checklist", value: `${checklist.passedCount}/${checklist.items.length} passed`, }, { id: "next-command", label: "Next command", value: actionPlan.nextCommand ?? "none", }, ], }; } export function createProjectBatchAcceptanceReportValidation(report = {}) { const reportObject = objectOrEmpty(report); const capabilityMatrix = objectOrEmpty(reportObject.capabilityMatrix); const workflow = objectOrEmpty(reportObject.workflow); const summaryViewModel = objectOrEmpty(reportObject.summaryViewModel); const actionPlan = objectOrEmpty(reportObject.actionPlan); const checklist = objectOrEmpty(reportObject.checklist); const rows = arrayOrEmpty(reportObject.rows); const missing = [ ...(reportObject.apiName === "project-batch-acceptance-report" ? [] : ["apiName"]), ...(reportObject.reportVersion === 1 ? [] : ["reportVersion"]), ...(reportObject.phase === "ready" ? [] : ["phase"]), ...(reportObject.ready === true ? [] : ["ready"]), ...(Array.isArray(reportObject.missing) && reportObject.missing.length === 0 ? [] : ["missing"]), ...(capabilityMatrix.apiName === "project-batch-acceptance-capability-matrix" && capabilityMatrix.ready === true ? [] : ["capabilityMatrix"]), ...(workflow.apiName === "project-batch-acceptance-workflow" && workflow.ready === true ? [] : ["workflow"]), ...(summaryViewModel.apiName === "project-batch-acceptance-summary-view-model" && summaryViewModel.ready === true ? [] : ["summaryViewModel"]), ...(actionPlan.apiName === "project-batch-acceptance-action-plan" && actionPlan.ready === true ? [] : ["actionPlan"]), ...(checklist.apiName === "project-batch-acceptance-checklist" && checklist.ready === true ? [] : ["checklist"]), ...(rows.find(({ id, value }) => id === "checklist" && value === "3/3 passed") ? [] : ["rows.checklist"]), ]; return { apiName: "project-batch-acceptance-report-validation", validationVersion: 1, phase: missing.length === 0 ? "ready" : "blocked", ready: missing.length === 0, missing, batchId: reportObject.batchId ?? null, reportApiName: reportObject.apiName ?? null, reportVersion: reportObject.reportVersion ?? null, rows: [ { id: "report", label: "Batch acceptance report", value: reportObject.apiName ?? "missing", }, { id: "capability-matrix", label: "Capability matrix", value: capabilityMatrix.ready === true ? "ready" : "missing", }, { id: "workflow", label: "Batch workflow", value: workflow.ready === true ? "ready" : "missing", }, { id: "checklist", label: "Checklist", value: checklist.ready === true ? "ready" : "missing", }, { id: "missing", label: "Missing report evidence", value: missing.length > 0 ? missing.join(", ") : "none", }, ], }; } export function createProjectBatchAcceptanceReportValidationSummaryViewModel( validation = createProjectBatchAcceptanceReportValidation(), ) { const missing = arrayOrEmpty(validation?.missing); const ready = validation?.ready === true; const statusText = ready ? "Ready" : "Blocked"; const detailText = ready ? "Batch acceptance report evidence complete" : (missing.length > 0 ? missing.join(", ") : "batch acceptance report evidence missing"); return { apiName: "project-batch-acceptance-report-validation-summary-view-model", viewModelVersion: 1, phase: ready ? "ready" : "blocked", ready, title: "Project batch acceptance report validation", statusText, detailText, statusLine: `${statusText}: ${detailText}`, rows: [ { id: "report", label: "Batch acceptance report", value: validation?.reportApiName ?? "missing", }, { id: "batch", label: "Batch", value: validation?.batchId ?? "missing", }, { id: "validation", label: "Report validation", value: ready ? "ready" : "blocked", }, { id: "missing", label: "Missing report evidence", value: missing.length > 0 ? missing.join(", ") : "none", }, ], }; } export function createProjectBatchAcceptanceReportValidationActionPlan( validation = createProjectBatchAcceptanceReportValidation(), ) { const missing = arrayOrEmpty(validation?.missing); const ready = validation?.ready === true; const commands = ready ? [] : [ { step: 1, id: "project-batch-acceptance", label: "Run project batch acceptance workflow and artifact gate", command: "wasm-port/tests/sdk/node/verify_project_batch_acceptance_workflow.sh", expectedOutput: "project_batch_acceptance_artifact_node_smoke=ok", }, ]; const shellScript = commands.length > 0 ? [ "#!/usr/bin/env bash", "set -euo pipefail", "", ...commands.map(({ command }) => command), "", ].join("\n") : "# Project batch acceptance report validation is already ready.\n"; return { apiName: "project-batch-acceptance-report-validation-action-plan", planVersion: 1, phase: ready ? "ready" : "blocked", ready, missing, missingCount: missing.length, commandCount: commands.length, nextActionId: commands[0]?.id ?? null, nextCommand: commands[0]?.command ?? null, commands, shellScript, rows: [ { id: "report-validation", label: "Report validation", value: ready ? "ready" : "blocked", }, { id: "missing", label: "Missing report evidence", value: missing.length > 0 ? missing.join(", ") : "none", }, { id: "next-command", label: "Next command", value: commands[0]?.command ?? "none", }, ], }; } export function createProjectBatchAcceptanceReportJsonWorkflow({ reportJson = "" } = {}) { try { const report = JSON.parse(String(reportJson)); const validation = createProjectBatchAcceptanceReportValidation(report); return { apiName: "project-batch-acceptance-report-json-workflow", workflowVersion: 1, phase: validation.ready ? "ready" : "blocked", ready: validation.ready, parsed: true, parseError: null, report, validation, missing: validation.missing, rows: [ { id: "parse", label: "Batch report JSON parse", value: "ready", }, { id: "validation", label: "Batch report validation", value: validation.ready ? "ready" : "blocked", }, { id: "missing", label: "Missing report evidence", value: validation.missing.length > 0 ? validation.missing.join(", ") : "none", }, ], }; } catch (error) { const validation = createProjectBatchAcceptanceReportValidation({}); return { apiName: "project-batch-acceptance-report-json-workflow", workflowVersion: 1, phase: "blocked", ready: false, parsed: false, parseError: error?.message ?? "batch acceptance report JSON parse error", report: null, validation, missing: ["report-json", ...validation.missing], rows: [ { id: "parse", label: "Batch report JSON parse", value: "blocked", }, { id: "validation", label: "Batch report validation", value: "blocked", }, { id: "missing", label: "Missing report evidence", value: ["report-json", ...validation.missing].join(", "), }, ], }; } } export async function loadProjectBatchAcceptanceReportUrlWorkflow({ reportUrl = "", fetchRef = globalThis.fetch, } = {}) { if (!reportUrl || typeof fetchRef !== "function") { const jsonWorkflow = createProjectBatchAcceptanceReportJsonWorkflow({ reportJson: "" }); const missing = [ ...(reportUrl ? [] : ["report-url"]), ...(typeof fetchRef === "function" ? [] : ["fetch"]), ...jsonWorkflow.missing, ]; return { apiName: "project-batch-acceptance-report-url-workflow", workflowVersion: 1, phase: "blocked", ready: false, reportUrl, fetched: false, httpStatus: null, fetchError: null, jsonWorkflow, validation: jsonWorkflow.validation, missing, rows: [ { id: "fetch", label: "Batch report URL fetch", value: "blocked", }, { id: "json-workflow", label: "Batch report JSON workflow", value: "blocked", }, { id: "missing", label: "Missing report evidence", value: missing.join(", "), }, ], }; } try { const response = await fetchRef(reportUrl); const httpStatus = response?.status ?? null; if (!response?.ok) { const jsonWorkflow = createProjectBatchAcceptanceReportJsonWorkflow({ reportJson: "" }); const fetchError = `batch report URL fetch failed${httpStatus ? `: ${httpStatus}` : ""}`; return { apiName: "project-batch-acceptance-report-url-workflow", workflowVersion: 1, phase: "blocked", ready: false, reportUrl, fetched: false, httpStatus, fetchError, jsonWorkflow, validation: jsonWorkflow.validation, missing: ["report-fetch", ...jsonWorkflow.missing], rows: [ { id: "fetch", label: "Batch report URL fetch", value: "blocked", }, { id: "json-workflow", label: "Batch report JSON workflow", value: "blocked", }, { id: "missing", label: "Missing report evidence", value: ["report-fetch", ...jsonWorkflow.missing].join(", "), }, ], }; } const reportJson = await response.text(); const jsonWorkflow = createProjectBatchAcceptanceReportJsonWorkflow({ reportJson }); return { apiName: "project-batch-acceptance-report-url-workflow", workflowVersion: 1, phase: jsonWorkflow.ready === true ? "ready" : "blocked", ready: jsonWorkflow.ready === true, reportUrl, fetched: true, httpStatus, fetchError: null, jsonWorkflow, validation: jsonWorkflow.validation, missing: jsonWorkflow.missing, rows: [ { id: "fetch", label: "Batch report URL fetch", value: "ready", }, { id: "json-workflow", label: "Batch report JSON workflow", value: jsonWorkflow.ready === true ? "ready" : "blocked", }, { id: "missing", label: "Missing report evidence", value: jsonWorkflow.missing.length > 0 ? jsonWorkflow.missing.join(", ") : "none", }, ], }; } catch (error) { const jsonWorkflow = createProjectBatchAcceptanceReportJsonWorkflow({ reportJson: "" }); return { apiName: "project-batch-acceptance-report-url-workflow", workflowVersion: 1, phase: "blocked", ready: false, reportUrl, fetched: false, httpStatus: null, fetchError: error?.message ?? "batch report URL fetch error", jsonWorkflow, validation: jsonWorkflow.validation, missing: ["report-fetch", ...jsonWorkflow.missing], rows: [ { id: "fetch", label: "Batch report URL fetch", value: "blocked", }, { id: "json-workflow", label: "Batch report JSON workflow", value: "blocked", }, { id: "missing", label: "Missing report evidence", value: ["report-fetch", ...jsonWorkflow.missing].join(", "), }, ], }; } } export function createProjectBatchAcceptanceReportUrlWorkflowSummaryViewModel( workflow = {}, ) { const missing = arrayOrEmpty(workflow?.missing); const ready = workflow?.ready === true; const statusText = ready ? "Ready" : "Blocked"; const detailText = ready ? "Batch report URL workflow complete" : (missing.length > 0 ? missing.join(", ") : workflow?.fetchError ?? "batch report URL workflow blocked"); return { apiName: "project-batch-acceptance-report-url-workflow-summary-view-model", viewModelVersion: 1, phase: ready ? "ready" : "blocked", ready, title: "Project batch acceptance report URL workflow", statusText, detailText, statusLine: `${statusText}: ${detailText}`, rows: [ { id: "report-url", label: "Report URL", value: workflow?.reportUrl || "missing", }, { id: "fetch", label: "Fetch", value: workflow?.fetched === true ? `ready${workflow?.httpStatus ? ` (${workflow.httpStatus})` : ""}` : "blocked", }, { id: "json-workflow", label: "JSON workflow", value: workflow?.jsonWorkflow?.ready === true ? "ready" : "blocked", }, { id: "report-validation", label: "Report validation", value: workflow?.validation?.ready === true ? "ready" : "blocked", }, { id: "missing", label: "Missing workflow evidence", value: missing.length > 0 ? missing.join(", ") : "none", }, ], }; } export function createProjectBatchAcceptanceReportUrlWorkflowActionPlan( workflow = {}, ) { const missing = arrayOrEmpty(workflow?.missing); const ready = workflow?.ready === true; const inputActions = [ ...(missing.includes("report-url") ? [{ id: "provide-report-url", label: "Provide project batch acceptance report URL", kind: "input", command: null, expectedOutput: null, }] : []), ...(missing.includes("fetch") ? [{ id: "provide-fetch", label: "Provide fetch implementation", kind: "input", command: null, expectedOutput: null, }] : []), ...(missing.includes("report-fetch") ? [{ id: "verify-report-url-fetch", label: "Verify project batch acceptance report URL fetch", kind: "fetch", command: null, expectedOutput: null, }] : []), ]; const commandActions = ready ? [] : [ { id: "project-batch-acceptance", label: "Run project batch acceptance workflow smoke", kind: "command", command: "wasm-port/tests/sdk/node/verify_project_batch_acceptance_workflow.sh", expectedOutput: "project_batch_acceptance_workflow_node_smoke=ok", }, ]; const actions = ready ? [] : [...inputActions, ...commandActions].map((action, index) => ({ step: index + 1, ...action, })); const commands = actions.filter(({ command }) => typeof command === "string" && command.length > 0); const shellScript = commands.length > 0 ? [ "#!/usr/bin/env bash", "set -euo pipefail", "", ...commands.map(({ command }) => command), "", ].join("\n") : (ready ? "# Project batch acceptance report URL workflow is already ready.\n" : "# Project batch acceptance report URL workflow requires non-shell input.\n"); return { apiName: "project-batch-acceptance-report-url-workflow-action-plan", planVersion: 1, phase: ready ? "ready" : "blocked", ready, missing, actionCount: actions.length, commandCount: commands.length, nextActionId: actions[0]?.id ?? null, nextCommand: commands[0]?.command ?? null, actions, commands, shellScript, rows: actions.map(({ id, label, kind, command, expectedOutput }) => ({ id, label, kind, value: command ?? kind, expectedOutput, })), }; } export function createProjectReleaseReadinessReport({ gateResults = {}, observedOutputs = [], simConfigInventory = DEFAULT_SIM_CONFIG_INVENTORY_BASELINE, blockedRuntimeFamilies = BLOCKED_RUNTIME_FAMILIES, promotedRuntimeFamilies = [], axisScreenshotArtifacts = [], } = {}) { const gateManifest = createProjectReleaseGateManifest(); const gateExecutionManifest = createProjectReleaseGateExecutionManifest({ gateResults, observedOutputs, manifest: gateManifest, }); const gateExecutionSummaryViewModel = createProjectReleaseGateExecutionSummaryViewModel( gateExecutionManifest, ); const gateResultMatrix = createProjectReleaseGateResultMatrix({ gateResults, observedOutputs, manifest: gateManifest, }); const gateActionPlan = createProjectReleaseGateActionPlan({ gateResultMatrix, manifest: gateManifest, }); const gateRows = gateResultMatrix.rows; const releaseGatePassed = gateRows.find(({ id }) => id === "project-release-gate")?.passed === true; const inventory = { ...DEFAULT_SIM_CONFIG_INVENTORY_BASELINE, ...(simConfigInventory && typeof simConfigInventory === "object" ? simConfigInventory : {}), }; const blockedFamilies = [...blockedRuntimeFamilies]; const promotedFamilies = [...promotedRuntimeFamilies]; const promotedBlockedFamilies = promotedFamilies.filter((family) => blockedFamilies.includes(family)); const inventoryReady = inventory.unexpectedFail === 0; const blockedRuntimeReady = promotedBlockedFamilies.length === 0; const axisScreenshotArtifactSummary = createAxisScreenshotArtifactSummary(axisScreenshotArtifacts); const axisScreenshotArtifactsReady = axisScreenshotArtifactSummary.ready; const ready = releaseGatePassed && inventoryReady && blockedRuntimeReady && axisScreenshotArtifactsReady; const missing = [ ...(releaseGatePassed ? [] : ["project-release-gate"]), ...(inventoryReady ? [] : ["sim-config-inventory"]), ...(blockedRuntimeReady ? [] : ["blocked-runtime-families"]), ...(axisScreenshotArtifactsReady ? [] : ["axis-screenshot-artifacts"]), ]; return { apiName: PROJECT_RELEASE_READINESS_ARTIFACT_API, reportVersion: PROJECT_RELEASE_READINESS_ARTIFACT_VERSION, phase: ready ? "ready" : "waiting", ready, missing, releaseGate: { command: "wasm-port/tests/host/verify_project_release_gate.sh", passed: releaseGatePassed, expectedOutput: "project_release_gate=ok", }, simConfigInventory: inventory, blockedRuntimeFamilies: blockedFamilies, promotedRuntimeFamilies: promotedFamilies, promotedBlockedFamilies, axisScreenshotArtifactSummary, gateManifest, gateExecutionManifest, gateExecutionSummaryViewModel, gateResultMatrix, gateActionPlan, gates: gateRows, rows: [ { id: "project-release-gate", label: "Project release gate", value: releaseGatePassed ? "passed" : "unknown", }, { id: "sim-config-inventory", label: "Sim config inventory", value: `unexpected_fail=${inventory.unexpectedFail}`, }, { id: "blocked-runtime-families", label: "Blocked runtime families", value: blockedRuntimeReady ? blockedFamilies.join(", ") : `promoted: ${promotedBlockedFamilies.join(", ")}`, }, { id: "axis-screenshot-artifacts", label: "AXIS screenshot artifacts", value: axisScreenshotArtifactSummary.artifactCount > 0 ? `${axisScreenshotArtifactSummary.artifactCount}/${axisScreenshotArtifactSummary.expectedViewports.length} captured` : "not captured", }, ], }; } export function createProjectReleaseReadinessSummaryViewModel( report = createProjectReleaseReadinessReport(), ) { const missing = Array.isArray(report?.missing) ? report.missing : []; const gateResultMatrix = objectOrEmpty(report?.gateResultMatrix); const simConfigInventory = objectOrEmpty(report?.simConfigInventory); const axisScreenshotArtifactSummary = objectOrEmpty(report?.axisScreenshotArtifactSummary); const blockedRuntimeFamilies = arrayOrEmpty(report?.blockedRuntimeFamilies); const promotedBlockedFamilies = arrayOrEmpty(report?.promotedBlockedFamilies); const ready = report?.ready === true; const statusText = ready ? "Ready" : "Waiting"; const detailText = ready ? "No blocking reasons" : (missing.length > 0 ? missing.join(", ") : "release evidence missing"); return { apiName: "project-release-readiness-summary-view-model", viewModelVersion: 1, phase: ready ? "ready" : "waiting", ready, title: "Project release readiness", statusText, detailText, statusLine: `${statusText}: ${detailText}`, rows: [ { id: "gate-results", label: "Release gates", value: `${gateResultMatrix.passedCount ?? 0}/${gateResultMatrix.gateCount ?? 0} passed`, }, { id: "unknown-gates", label: "Unknown gates", value: `${gateResultMatrix.unknownCount ?? 0}`, }, { id: "sim-config-inventory", label: "Sim config inventory", value: `unexpected_fail=${simConfigInventory.unexpectedFail ?? "unknown"}`, }, { id: "blocked-runtime-families", label: "Blocked runtime families", value: promotedBlockedFamilies.length > 0 ? `promoted: ${promotedBlockedFamilies.join(", ")}` : (blockedRuntimeFamilies.join(", ") || "none"), }, { id: "axis-screenshot-artifacts", label: "AXIS screenshot artifacts", value: Number(axisScreenshotArtifactSummary.artifactCount ?? 0) > 0 ? `${axisScreenshotArtifactSummary.artifactCount}/${arrayOrEmpty(axisScreenshotArtifactSummary.expectedViewports).length} captured` : "not captured", }, { id: "missing", label: "Missing readiness", value: missing.length > 0 ? missing.join(", ") : "none", }, ], }; } export function createProjectReleaseReadinessArtifactValidationSummaryViewModel( validation = createProjectReleaseReadinessArtifactValidation(), ) { const missing = arrayOrEmpty(validation?.missing); const ready = validation?.ready === true; const statusText = ready ? "Ready" : "Blocked"; const detailText = ready ? "Artifact evidence complete" : (missing.length > 0 ? missing.join(", ") : "artifact evidence missing"); return { apiName: "project-release-readiness-artifact-validation-summary-view-model", viewModelVersion: 1, phase: ready ? "ready" : "blocked", ready, title: "Project release artifact validation", statusText, detailText, statusLine: `${statusText}: ${detailText}`, rows: [ { id: "artifact", label: "Readiness artifact", value: validation?.artifactApiName ?? "missing", }, { id: "gate-count", label: "Gate count", value: `${validation?.gateCount ?? 0}/${validation?.expectedGateCount ?? 0}`, }, { id: "gate-manifest", label: "Gate manifest", value: validation?.gateManifestReady === true ? "ready" : "missing", }, { id: "gate-execution-manifest", label: "Gate execution manifest", value: validation?.gateExecutionManifestReady === true ? "ready" : "missing", }, { id: "gate-execution-summary", label: "Gate execution summary", value: validation?.gateExecutionSummaryReady === true ? "ready" : "missing", }, { id: "gate-result-matrix", label: "Gate result matrix", value: validation?.gateResultMatrixReady === true ? "ready" : "missing", }, { id: "gate-action-plan", label: "Gate action plan", value: validation?.gateActionPlanReady === true ? "ready" : "missing", }, { id: "missing", label: "Missing artifact evidence", value: missing.length > 0 ? missing.join(", ") : "none", }, ], }; } export function createProjectReleaseReadinessArtifactValidationActionPlan( validation = createProjectReleaseReadinessArtifactValidation(), ) { const missing = arrayOrEmpty(validation?.missing); const ready = validation?.ready === true; const commands = ready ? [] : [ { step: 1, id: "project-release-gate", label: "Run project release gate", command: "wasm-port/tests/host/verify_project_release_gate.sh", expectedOutput: "project_release_gate=ok", }, { step: 2, id: "artifact-validation", label: "Validate project release readiness artifact", command: "wasm-port/tests/host/verify_project_release_readiness_artifact.sh build/project-release-readiness.json", expectedOutput: "project_release_readiness_artifact_node_smoke=ok", }, ]; const shellScript = commands.length > 0 ? [ "#!/usr/bin/env bash", "set -euo pipefail", "", ...commands.map(({ command }) => command), "", ].join("\n") : "# Project release readiness artifact validation is already ready.\n"; return { apiName: "project-release-readiness-artifact-validation-action-plan", planVersion: 1, phase: ready ? "ready" : "blocked", ready, missing, missingCount: missing.length, commandCount: commands.length, nextActionId: commands[0]?.id ?? null, nextCommand: commands[0]?.command ?? null, commands, shellScript, rows: [ { id: "artifact-validation", label: "Artifact validation", value: ready ? "ready" : "blocked", }, { id: "missing", label: "Missing artifact evidence", value: missing.length > 0 ? missing.join(", ") : "none", }, { id: "next-command", label: "Next command", value: commands[0]?.command ?? "none", }, ], }; } export function parseProjectReleaseReadinessArtifactJson(text) { return JSON.parse(String(text)); } export function createProjectReleaseReadinessArtifactJsonWorkflow({ artifactJson = "" } = {}) { try { const artifact = parseProjectReleaseReadinessArtifactJson(artifactJson); const validation = createProjectReleaseReadinessArtifactValidation(artifact); const summaryViewModel = createProjectReleaseReadinessArtifactValidationSummaryViewModel(validation); const actionPlan = createProjectReleaseReadinessArtifactValidationActionPlan(validation); return { apiName: "project-release-readiness-artifact-json-workflow", workflowVersion: 1, phase: validation.ready === true ? "ready" : "blocked", ready: validation.ready === true, parsed: true, parseError: null, artifactApiName: validation.artifactApiName, validation, summaryViewModel, actionPlan, missing: validation.missing, rows: [ { id: "parse", label: "Artifact JSON parse", value: "ready", }, { id: "validation", label: "Artifact validation", value: validation.ready === true ? "ready" : "blocked", }, { id: "next-command", label: "Next command", value: actionPlan.nextCommand ?? "none", }, ], }; } catch (error) { const validation = createProjectReleaseReadinessArtifactValidation({}); const summaryViewModel = createProjectReleaseReadinessArtifactValidationSummaryViewModel(validation); const actionPlan = createProjectReleaseReadinessArtifactValidationActionPlan(validation); const parseError = error?.message ?? "artifact JSON parse error"; return { apiName: "project-release-readiness-artifact-json-workflow", workflowVersion: 1, phase: "blocked", ready: false, parsed: false, parseError, artifactApiName: null, validation, summaryViewModel, actionPlan, missing: ["artifact-json", ...validation.missing], rows: [ { id: "parse", label: "Artifact JSON parse", value: "blocked", }, { id: "validation", label: "Artifact validation", value: "blocked", }, { id: "next-command", label: "Next command", value: actionPlan.nextCommand ?? "none", }, ], }; } } export async function loadProjectReleaseReadinessArtifactUrlWorkflow({ artifactUrl = "", fetchRef = globalThis.fetch, } = {}) { if (!artifactUrl || typeof fetchRef !== "function") { const jsonWorkflow = createProjectReleaseReadinessArtifactJsonWorkflow({ artifactJson: "" }); const missing = [ ...(artifactUrl ? [] : ["artifact-url"]), ...(typeof fetchRef === "function" ? [] : ["fetch"]), ...jsonWorkflow.missing, ]; return { apiName: "project-release-readiness-artifact-url-workflow", workflowVersion: 1, phase: "blocked", ready: false, artifactUrl, fetched: false, httpStatus: null, fetchError: null, jsonWorkflow, validation: jsonWorkflow.validation, summaryViewModel: jsonWorkflow.summaryViewModel, actionPlan: jsonWorkflow.actionPlan, missing, rows: [ { id: "fetch", label: "Artifact URL fetch", value: "blocked", }, { id: "json-workflow", label: "Artifact JSON workflow", value: "blocked", }, { id: "next-command", label: "Next command", value: jsonWorkflow.actionPlan.nextCommand ?? "none", }, ], }; } try { const response = await fetchRef(artifactUrl); const httpStatus = response?.status ?? null; if (!response?.ok) { const jsonWorkflow = createProjectReleaseReadinessArtifactJsonWorkflow({ artifactJson: "" }); const fetchError = `artifact URL fetch failed${httpStatus ? `: ${httpStatus}` : ""}`; return { apiName: "project-release-readiness-artifact-url-workflow", workflowVersion: 1, phase: "blocked", ready: false, artifactUrl, fetched: false, httpStatus, fetchError, jsonWorkflow, validation: jsonWorkflow.validation, summaryViewModel: jsonWorkflow.summaryViewModel, actionPlan: jsonWorkflow.actionPlan, missing: ["artifact-fetch", ...jsonWorkflow.missing], rows: [ { id: "fetch", label: "Artifact URL fetch", value: "blocked", }, { id: "json-workflow", label: "Artifact JSON workflow", value: "blocked", }, { id: "next-command", label: "Next command", value: jsonWorkflow.actionPlan.nextCommand ?? "none", }, ], }; } const artifactJson = await response.text(); const jsonWorkflow = createProjectReleaseReadinessArtifactJsonWorkflow({ artifactJson }); return { apiName: "project-release-readiness-artifact-url-workflow", workflowVersion: 1, phase: jsonWorkflow.ready === true ? "ready" : "blocked", ready: jsonWorkflow.ready === true, artifactUrl, fetched: true, httpStatus, fetchError: null, jsonWorkflow, validation: jsonWorkflow.validation, summaryViewModel: jsonWorkflow.summaryViewModel, actionPlan: jsonWorkflow.actionPlan, missing: jsonWorkflow.missing, rows: [ { id: "fetch", label: "Artifact URL fetch", value: "ready", }, { id: "json-workflow", label: "Artifact JSON workflow", value: jsonWorkflow.ready === true ? "ready" : "blocked", }, { id: "next-command", label: "Next command", value: jsonWorkflow.actionPlan.nextCommand ?? "none", }, ], }; } catch (error) { const jsonWorkflow = createProjectReleaseReadinessArtifactJsonWorkflow({ artifactJson: "" }); return { apiName: "project-release-readiness-artifact-url-workflow", workflowVersion: 1, phase: "blocked", ready: false, artifactUrl, fetched: false, httpStatus: null, fetchError: error?.message ?? "artifact URL fetch error", jsonWorkflow, validation: jsonWorkflow.validation, summaryViewModel: jsonWorkflow.summaryViewModel, actionPlan: jsonWorkflow.actionPlan, missing: ["artifact-fetch", ...jsonWorkflow.missing], rows: [ { id: "fetch", label: "Artifact URL fetch", value: "blocked", }, { id: "json-workflow", label: "Artifact JSON workflow", value: "blocked", }, { id: "next-command", label: "Next command", value: jsonWorkflow.actionPlan.nextCommand ?? "none", }, ], }; } } export function createProjectReleaseReadinessArtifactUrlWorkflowSummaryViewModel( workflow = {}, ) { const missing = arrayOrEmpty(workflow?.missing); const ready = workflow?.ready === true; const statusText = ready ? "Ready" : "Blocked"; const detailText = ready ? "Artifact URL workflow complete" : (missing.length > 0 ? missing.join(", ") : workflow?.fetchError ?? "artifact URL workflow blocked"); return { apiName: "project-release-readiness-artifact-url-workflow-summary-view-model", viewModelVersion: 1, phase: ready ? "ready" : "blocked", ready, title: "Project release artifact URL workflow", statusText, detailText, statusLine: `${statusText}: ${detailText}`, rows: [ { id: "artifact-url", label: "Artifact URL", value: workflow?.artifactUrl || "missing", }, { id: "fetch", label: "Fetch", value: workflow?.fetched === true ? `ready${workflow?.httpStatus ? ` (${workflow.httpStatus})` : ""}` : "blocked", }, { id: "json-workflow", label: "JSON workflow", value: workflow?.jsonWorkflow?.ready === true ? "ready" : "blocked", }, { id: "artifact-validation", label: "Artifact validation", value: workflow?.validation?.ready === true ? "ready" : "blocked", }, { id: "next-command", label: "Next command", value: workflow?.actionPlan?.nextCommand ?? "none", }, { id: "missing", label: "Missing workflow evidence", value: missing.length > 0 ? missing.join(", ") : "none", }, ], }; } export function createProjectReleaseReadinessArtifactUrlWorkflowActionPlan( workflow = {}, ) { const missing = arrayOrEmpty(workflow?.missing); const ready = workflow?.ready === true; const validationCommands = arrayOrEmpty(workflow?.actionPlan?.commands); const inputActions = [ ...(missing.includes("artifact-url") ? [{ id: "provide-artifact-url", label: "Provide release readiness artifact URL", kind: "input", command: null, expectedOutput: null, }] : []), ...(missing.includes("fetch") ? [{ id: "provide-fetch", label: "Provide fetch implementation", kind: "input", command: null, expectedOutput: null, }] : []), ...(missing.includes("artifact-fetch") ? [{ id: "verify-artifact-url-fetch", label: "Verify release readiness artifact URL fetch", kind: "fetch", command: null, expectedOutput: null, }] : []), ]; const commandActions = validationCommands.map((command) => ({ id: command.id ?? command.gateId ?? `command-${command.step}`, label: command.label, kind: "command", command: command.command, expectedOutput: command.expectedOutput ?? null, })); const actions = ready ? [] : [...inputActions, ...commandActions].map((action, index) => ({ step: index + 1, ...action, })); const commands = actions.filter(({ command }) => typeof command === "string" && command.length > 0); const shellScript = commands.length > 0 ? [ "#!/usr/bin/env bash", "set -euo pipefail", "", ...commands.map(({ command }) => command), "", ].join("\n") : (ready ? "# Project release readiness artifact URL workflow is already ready.\n" : "# Project release readiness artifact URL workflow requires non-shell input.\n"); return { apiName: "project-release-readiness-artifact-url-workflow-action-plan", planVersion: 1, phase: ready ? "ready" : "blocked", ready, missing, actionCount: actions.length, commandCount: commands.length, nextActionId: actions[0]?.id ?? null, nextCommand: commands[0]?.command ?? null, actions, commands, shellScript, rows: actions.map(({ id, label, kind, command, expectedOutput }) => ({ id, label, kind, value: command ?? kind, expectedOutput, })), }; } export function createProjectReleaseReadinessArtifactValidation(artifact = {}) { const artifactObject = objectOrEmpty(artifact); const releaseGate = objectOrEmpty(artifactObject.releaseGate); const simConfigInventory = objectOrEmpty(artifactObject.simConfigInventory); const gateManifest = objectOrEmpty(artifactObject.gateManifest); const gateExecutionManifest = objectOrEmpty(artifactObject.gateExecutionManifest); const gateExecutionSummaryViewModel = objectOrEmpty(artifactObject.gateExecutionSummaryViewModel); const gateResultMatrix = objectOrEmpty(artifactObject.gateResultMatrix); const gateActionPlan = objectOrEmpty(artifactObject.gateActionPlan); const axisScreenshotArtifactSummary = objectOrEmpty(artifactObject.axisScreenshotArtifactSummary); const gates = arrayOrEmpty(artifactObject.gates); const manifestGateIds = arrayOrEmpty(gateManifest.gateIds); const executionRows = arrayOrEmpty(gateExecutionManifest.rows); const matrixRows = arrayOrEmpty(gateResultMatrix.rows); const actionPlanCommands = arrayOrEmpty(gateActionPlan.commands); const rows = arrayOrEmpty(artifactObject.rows); const expectedGateIds = createProjectReleaseGateManifest().gateIds; const gateCountReady = gates.length === REQUIRED_RELEASE_GATES.length; const gatesPassed = gateCountReady && gates.every(({ passed }) => passed === true); const manifestReady = gateManifest.apiName === "project-release-gate-manifest" && gateManifest.manifestVersion === 1 && gateManifest.gateCount === REQUIRED_RELEASE_GATES.length && manifestGateIds.join(",") === expectedGateIds.join(","); const matrixReady = gateResultMatrix.apiName === "project-release-gate-result-matrix" && gateResultMatrix.matrixVersion === 1 && gateResultMatrix.ready === true && gateResultMatrix.passedCount === REQUIRED_RELEASE_GATES.length && gateResultMatrix.unknownCount === 0 && matrixRows.length === REQUIRED_RELEASE_GATES.length; const executionManifestReady = gateExecutionManifest.apiName === "project-release-gate-execution-manifest" && gateExecutionManifest.manifestVersion === 1 && gateExecutionManifest.ready === true && gateExecutionManifest.passedCount === REQUIRED_RELEASE_GATES.length && gateExecutionManifest.unknownCount === 0 && executionRows.length === REQUIRED_RELEASE_GATES.length && executionRows.every(({ command, status }) => typeof command === "string" && status === "passed"); const executionSummaryReady = gateExecutionSummaryViewModel.apiName === "project-release-gate-execution-summary-view-model" && gateExecutionSummaryViewModel.viewModelVersion === 1 && gateExecutionSummaryViewModel.ready === true && gateExecutionSummaryViewModel.passedCount === REQUIRED_RELEASE_GATES.length && gateExecutionSummaryViewModel.unknownCount === 0 && gateExecutionSummaryViewModel.nextGateId === null && arrayOrEmpty(gateExecutionSummaryViewModel.gateRows).length === REQUIRED_RELEASE_GATES.length; const actionPlanReady = gateActionPlan.apiName === "project-release-gate-action-plan" && gateActionPlan.planVersion === 1 && gateActionPlan.ready === true && gateActionPlan.pendingCount === 0 && gateActionPlan.nextCommand === null && actionPlanCommands.length === 0; const axisScreenshotArtifacts = arrayOrEmpty(axisScreenshotArtifactSummary.artifacts); const axisScreenshotSummaryReady = !axisScreenshotArtifactSummary.apiName || axisScreenshotArtifacts.length === 0 || ( axisScreenshotArtifactSummary.apiName === "project-release-axis-screenshot-artifact-summary" && axisScreenshotArtifactSummary.summaryVersion === 1 && axisScreenshotArtifactSummary.ready === true && axisScreenshotArtifacts.length === 4 && arrayOrEmpty(axisScreenshotArtifactSummary.missingViewports).length === 0 && arrayOrEmpty(axisScreenshotArtifactSummary.undersizedViewports).length === 0 && axisScreenshotArtifacts.every((artifact) => artifact.apiName === "real-browser-simulation-axis-screenshot-artifact" && artifact.screenshotBytes >= 10000 && artifact.validatedBy === "browser_real_simulation_page_smoke" && ( artifact.fullDiagnosticsPath === null || ( artifact.fullDiagnosticsApiName === "real-browser-simulation-diagnostics-artifact" && artifact.previewRenderer === "threejs" && artifact.threePathPoints > 0 ) ) ) ); const missing = [ ...(artifactObject.apiName === PROJECT_RELEASE_READINESS_ARTIFACT_API ? [] : ["apiName"]), ...(artifactObject.reportVersion === PROJECT_RELEASE_READINESS_ARTIFACT_VERSION ? [] : ["reportVersion"]), ...(artifactObject.phase === "ready" ? [] : ["phase"]), ...(artifactObject.ready === true ? [] : ["ready"]), ...(Array.isArray(artifactObject.missing) && artifactObject.missing.length === 0 ? [] : ["missing"]), ...(releaseGate.command === "wasm-port/tests/host/verify_project_release_gate.sh" ? [] : ["releaseGate.command"]), ...(releaseGate.passed === true ? [] : ["releaseGate.passed"]), ...(releaseGate.expectedOutput === "project_release_gate=ok" ? [] : ["releaseGate.expectedOutput"]), ...(simConfigInventory.executed === 28 ? [] : ["simConfigInventory.executed"]), ...(simConfigInventory.passed === 28 ? [] : ["simConfigInventory.passed"]), ...(simConfigInventory.skipped === 131 ? [] : ["simConfigInventory.skipped"]), ...(simConfigInventory.unexpectedFail === 0 ? [] : ["simConfigInventory.unexpectedFail"]), ...(arrayOrEmpty(artifactObject.blockedRuntimeFamilies).join(",") === BLOCKED_RUNTIME_FAMILIES.join(",") ? [] : ["blockedRuntimeFamilies"]), ...(arrayOrEmpty(artifactObject.promotedBlockedFamilies).length === 0 ? [] : ["promotedBlockedFamilies"]), ...(manifestReady ? [] : ["gateManifest"]), ...(executionManifestReady ? [] : ["gateExecutionManifest"]), ...(executionSummaryReady ? [] : ["gateExecutionSummaryViewModel"]), ...(matrixReady ? [] : ["gateResultMatrix"]), ...(actionPlanReady ? [] : ["gateActionPlan"]), ...(axisScreenshotSummaryReady ? [] : ["axisScreenshotArtifactSummary"]), ...(gateCountReady ? [] : ["gates.length"]), ...(gatesPassed ? [] : ["gates.passed"]), ...(rows.find(({ id, value }) => id === "project-release-gate" && value === "passed") ? [] : ["rows.project-release-gate"]), ]; return { apiName: "project-release-readiness-artifact-validation", validationVersion: 1, phase: missing.length === 0 ? "ready" : "blocked", ready: missing.length === 0, missing, artifactApiName: artifactObject.apiName ?? null, artifactVersion: artifactObject.reportVersion ?? null, gateCount: gates.length, expectedGateCount: REQUIRED_RELEASE_GATES.length, expectedGateIds, gateManifestReady: manifestReady, gateExecutionManifestReady: executionManifestReady, gateExecutionSummaryReady: executionSummaryReady, gateResultMatrixReady: matrixReady, gateActionPlanReady: actionPlanReady, axisScreenshotArtifactSummaryReady: axisScreenshotSummaryReady, axisScreenshotArtifactCount: axisScreenshotArtifacts.length, blockedRuntimeFamilies: arrayOrEmpty(artifactObject.blockedRuntimeFamilies), rows: [ { id: "artifact", label: "Readiness artifact", value: artifactObject.apiName ?? "missing", }, { id: "project-release-gate", label: "Project release gate", value: releaseGate.passed === true ? "passed" : "missing", }, { id: "gate-manifest", label: "Gate manifest", value: manifestReady ? "ready" : "missing", }, { id: "gate-execution-manifest", label: "Gate execution manifest", value: executionManifestReady ? "ready" : "missing", }, { id: "gate-execution-summary", label: "Gate execution summary", value: executionSummaryReady ? "ready" : "missing", }, { id: "gate-result-matrix", label: "Gate result matrix", value: matrixReady ? "ready" : "missing", }, { id: "gate-action-plan", label: "Gate action plan", value: actionPlanReady ? "ready" : "missing", }, { id: "axis-screenshot-artifacts", label: "AXIS screenshot artifacts", value: axisScreenshotArtifacts.length > 0 ? `${axisScreenshotArtifacts.length} captured` : "not captured", }, { id: "validation", label: "Artifact validation", value: missing.length === 0 ? "ready" : missing.join(", "), }, ], }; }