结论:53 个 Python-remap inventory rows 已完成 row proof、browser proof 与 inventory baseline promotion,当前 inventory baseline 为 PASS=82 / SKIP=77 / unexpected_fail=0。
4070 lines
161 KiB
JavaScript
4070 lines
161 KiB
JavaScript
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: "tool-db-process-proof",
|
|
label: "Tool DB process protocol proof",
|
|
command: "wasm-port/tests/host/verify_tool_db_process_proof.sh",
|
|
expectedOutput: "tool_db_process_proof=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: 82,
|
|
passed: 82,
|
|
skipped: 77,
|
|
unexpectedFail: 0,
|
|
};
|
|
|
|
const DEFAULT_SIM_CONFIG_INVENTORY_ARTIFACT_HASHES = {
|
|
"wasm-port/build/wasm/sim-configs-inventory/boundary-summary.tsv":
|
|
"6cc15052cad03d04eff4506cd0a9d651cd45a2bd5da4d9f38633e9535826b9f3",
|
|
"wasm-port/build/wasm/sim-configs-inventory/ini-boundary-summary.tsv":
|
|
"17bff95b57c55ff4201a23c7286e733550e4df513470c66dd5f24329e7781a35",
|
|
};
|
|
|
|
function isBoundaryEvidenceBaselineFresh(boundaryEvidence = {}) {
|
|
const baseline = objectOrEmpty(boundaryEvidence.baselineSummary);
|
|
const hashes = objectOrEmpty(boundaryEvidence.sourceArtifactHashes);
|
|
return baseline.executed === DEFAULT_SIM_CONFIG_INVENTORY_BASELINE.executed &&
|
|
baseline.passed === DEFAULT_SIM_CONFIG_INVENTORY_BASELINE.passed &&
|
|
baseline.skipped === DEFAULT_SIM_CONFIG_INVENTORY_BASELINE.skipped &&
|
|
baseline.unexpectedFail === DEFAULT_SIM_CONFIG_INVENTORY_BASELINE.unexpectedFail &&
|
|
Object.entries(DEFAULT_SIM_CONFIG_INVENTORY_ARTIFACT_HASHES)
|
|
.every(([artifactPath, expectedHash]) => hashes[artifactPath] === expectedHash);
|
|
}
|
|
|
|
function createPromotionFamilySummaryText(candidateReport = {}) {
|
|
const families = arrayOrEmpty(objectOrEmpty(candidateReport).familyRows);
|
|
if (families.length === 0) {
|
|
return "not provided";
|
|
}
|
|
return families.map((family) => {
|
|
const label = family.id ?? "unknown";
|
|
const completeCount = family.completeCount ?? 0;
|
|
const candidateCount = family.candidateCount ?? 0;
|
|
const state = family.complete === true && family.explicitBrowserDiagnosticsReady === true ? "ready" : "blocked";
|
|
return `${label}: ${completeCount}/${candidateCount} ${state}`;
|
|
}).join("; ");
|
|
}
|
|
|
|
function createPromotionFamilySummaryRows(candidateReport = {}) {
|
|
return arrayOrEmpty(objectOrEmpty(candidateReport).familyRows).map((family) => {
|
|
const sourceFileCount = arrayOrEmpty(family.sourceFiles).length;
|
|
const candidateCount = family.candidateCount ?? 0;
|
|
const completeCount = family.completeCount ?? 0;
|
|
const explicitBrowserDiagnosticsReady = family.explicitBrowserDiagnosticsReady === true;
|
|
const ready = family.complete === true && explicitBrowserDiagnosticsReady;
|
|
return {
|
|
id: family.id ?? "unknown",
|
|
label: family.id ?? "Unknown promotion family",
|
|
value: `${completeCount}/${candidateCount} ${ready ? "ready" : "blocked"}; sources=${sourceFileCount}; diagnostics=${explicitBrowserDiagnosticsReady ? "ready" : "blocked"}`,
|
|
sourceFiles: arrayOrEmpty(family.sourceFiles),
|
|
candidateCount,
|
|
completeCount,
|
|
sourceFileCount,
|
|
explicitBrowserDiagnosticsReady,
|
|
ready,
|
|
};
|
|
});
|
|
}
|
|
|
|
function createPromotionCandidateArtifactSummary({
|
|
candidateReport = {},
|
|
evidenceExpansionReport = {},
|
|
inventory = DEFAULT_SIM_CONFIG_INVENTORY_BASELINE,
|
|
blockedFamilies = BLOCKED_RUNTIME_FAMILIES,
|
|
promotedBlockedFamilies = [],
|
|
promotionCandidateRows = [],
|
|
evidenceExpansionRows = [],
|
|
} = {}) {
|
|
const rows = arrayOrEmpty(objectOrEmpty(candidateReport).rows);
|
|
const candidateArtifactRows = arrayOrEmpty(promotionCandidateRows);
|
|
const evidenceExpansionArtifactRows = arrayOrEmpty(evidenceExpansionRows);
|
|
const evidenceReadyCount = rows.filter((row) =>
|
|
row.currentNodeInventoryStatus === "PASS" &&
|
|
row.blockedKind === "-" &&
|
|
row.currentMatrixBrowserStatus === "explicit-browser-diagnostics" &&
|
|
row.targetBrowserEvidence === "explicit-browser-diagnostics"
|
|
).length;
|
|
const artifactEvidenceReadyRows = candidateArtifactRows
|
|
.filter((row) => row.candidate_kind === "evidence-ready");
|
|
const artifactInventoryReadyRows = candidateArtifactRows
|
|
.filter((row) => row.candidate_kind === "inventory-ready");
|
|
const artifactEvidenceReadyCount = artifactEvidenceReadyRows.length;
|
|
const inventoryReadyCount = artifactInventoryReadyRows.length > 0
|
|
? artifactInventoryReadyRows.length
|
|
: 2;
|
|
const evidenceExpansionCandidateCount = evidenceExpansionArtifactRows.length > 0
|
|
? evidenceExpansionArtifactRows.length
|
|
: 14;
|
|
const promotionAllowedCount = candidateArtifactRows.length > 0
|
|
? candidateArtifactRows.filter((row) => String(row.promotion_allowed ?? row.promotionAllowed) === "1").length
|
|
: 0;
|
|
const hardBlockRuntimeFamilyRows = artifactInventoryReadyRows
|
|
.filter((row) => blockedFamilies.includes(row.skip_kind ?? row.blocked_kind ?? row.blockedKind))
|
|
.map((row, index) => ({
|
|
candidateId: row.candidate_id ?? row.path ?? "unknown",
|
|
path: row.path ?? "unknown",
|
|
skipKind: row.skip_kind ?? row.blocked_kind ?? row.blockedKind ?? "unknown",
|
|
promotionAllowed: String(row.promotion_allowed ?? row.promotionAllowed ?? "0") === "1",
|
|
blockReason: row.block_reason ?? row.blockReason ?? "-",
|
|
recommendedNextCommand: row.recommended_next_command ?? row.recommendedNextCommand ?? "-",
|
|
detailId: `hard-block-runtime-detail-${index + 1}`,
|
|
}));
|
|
const resolvedEvidenceReadyCount = artifactEvidenceReadyCount > 0 ? artifactEvidenceReadyCount : evidenceReadyCount;
|
|
const hardBlockRuntimePromotionAllowedCount = hardBlockRuntimeFamilyRows
|
|
.filter((row) => row.promotionAllowed)
|
|
.length;
|
|
const hardBlockRuntimeFamilySummaryRows = blockedFamilies.map((family) => {
|
|
const familyRows = hardBlockRuntimeFamilyRows.filter((row) => row.skipKind === family);
|
|
const promotionAllowedRows = familyRows.filter((row) => row.promotionAllowed);
|
|
return {
|
|
family,
|
|
candidateCount: familyRows.length,
|
|
promotionAllowedCount: promotionAllowedRows.length,
|
|
locked: promotionAllowedRows.length === 0,
|
|
lockedPaths: familyRows.map((row) => row.path),
|
|
firstPath: familyRows[0]?.path ?? null,
|
|
firstBlockReason: familyRows[0]?.blockReason ?? null,
|
|
nextProof: familyRows[0]?.recommendedNextCommand ?? null,
|
|
};
|
|
});
|
|
const hardBlockRuntimeLockSummary = createHardBlockRuntimeLockSummary(
|
|
blockedFamilies,
|
|
promotedBlockedFamilies,
|
|
);
|
|
const baselineText = [
|
|
`executed=${inventory.executed ?? "unknown"}`,
|
|
`passed=${inventory.passed ?? "unknown"}`,
|
|
`skipped=${inventory.skipped ?? "unknown"}`,
|
|
`unexpected_fail=${inventory.unexpectedFail ?? "unknown"}`,
|
|
].join(" ");
|
|
|
|
return {
|
|
apiName: "project-release-promotion-candidate-artifact-summary",
|
|
summaryVersion: 1,
|
|
phase: "ready",
|
|
ready: true,
|
|
artifactPath: "wasm-port/build/wasm/sim-configs-inventory/promotion-candidates.tsv",
|
|
evidenceExpansionArtifactPath: "wasm-port/build/wasm/sim-configs-inventory/evidence-expansion-candidates.tsv",
|
|
layerCount: 2,
|
|
evidenceReadyCount: resolvedEvidenceReadyCount,
|
|
inventoryReadyCount,
|
|
evidenceExpansionCandidateCount,
|
|
totalCandidateCount: resolvedEvidenceReadyCount + inventoryReadyCount,
|
|
promotionAllowedCount,
|
|
hardBlockRuntimeLock: hardBlockRuntimeLockSummary.lockValue,
|
|
hardBlockRuntimePromotionAllowedCount,
|
|
inventoryBaseline: baselineText,
|
|
artifactRows: {
|
|
promotionCandidates: candidateArtifactRows.length > 0
|
|
? candidateArtifactRows.length
|
|
: resolvedEvidenceReadyCount + inventoryReadyCount,
|
|
evidenceExpansion: evidenceExpansionArtifactRows.length > 0
|
|
? evidenceExpansionArtifactRows.length
|
|
: evidenceExpansionCandidateCount,
|
|
},
|
|
hardBlockRuntimeFamilyRows,
|
|
hardBlockRuntimeFamilyDetailRows: hardBlockRuntimeFamilyRows.map((row) => ({
|
|
id: row.detailId,
|
|
family: row.skipKind,
|
|
path: row.path,
|
|
blockReason: row.blockReason,
|
|
nextProof: row.recommendedNextCommand,
|
|
promotionAllowed: row.promotionAllowed,
|
|
})),
|
|
hardBlockRuntimeFamilySummaryRows,
|
|
layers: [
|
|
{
|
|
id: "evidence-ready",
|
|
candidateCount: resolvedEvidenceReadyCount,
|
|
promotionAllowedCount: 0,
|
|
virtualHalEvidenceReady: true,
|
|
baselineChanging: false,
|
|
},
|
|
{
|
|
id: "inventory-ready",
|
|
candidateCount: inventoryReadyCount,
|
|
promotionAllowedCount,
|
|
virtualHalEvidenceReady: false,
|
|
baselineChanging: promotionAllowedCount > 0,
|
|
},
|
|
],
|
|
evidenceExpansion: {
|
|
id: "browser-diagnostics-expansion",
|
|
candidateCount: evidenceExpansionCandidateCount,
|
|
artifactRowCount: evidenceExpansionArtifactRows.length > 0
|
|
? evidenceExpansionArtifactRows.length
|
|
: evidenceExpansionCandidateCount,
|
|
promotionAllowedCount: 0,
|
|
baselineChanging: false,
|
|
nextEvidence: "browser-diagnostics-binding",
|
|
},
|
|
};
|
|
}
|
|
|
|
function createHardBlockRuntimeLockSummary(blockedFamilies = [], promotedBlockedFamilies = []) {
|
|
const blockedCount = arrayOrEmpty(blockedFamilies).length;
|
|
const promotedCount = arrayOrEmpty(promotedBlockedFamilies).length;
|
|
return {
|
|
lockValue: promotedCount === 0 ? `locked (${blockedCount} families)` : `violated (${promotedCount} promoted)`,
|
|
promotedCountValue: `${promotedCount} promoted`,
|
|
};
|
|
}
|
|
|
|
function createToolDbProcessProofSummary(toolDbProcessProof = {}) {
|
|
const proof = objectOrEmpty(toolDbProcessProof);
|
|
const nativeProtocolReady = proof.nativeProtocolReady === true;
|
|
const wasmProtocolReady = proof.wasmProtocolReady !== false;
|
|
const browserProtocolReady = proof.browserProtocolReady !== false;
|
|
const opfsPersistenceReady = proof.opfsPersistenceReady !== false;
|
|
const tblFallbackSufficient = proof.tblFallbackSufficient === true;
|
|
const promotionAllowed = proof.promotionAllowed === true;
|
|
const ready = wasmProtocolReady &&
|
|
browserProtocolReady &&
|
|
opfsPersistenceReady &&
|
|
tblFallbackSufficient === false &&
|
|
promotionAllowed === false;
|
|
|
|
return {
|
|
apiName: "tool-db-process-proof-summary",
|
|
summaryVersion: 1,
|
|
phase: ready ? "ready" : "blocked",
|
|
ready,
|
|
boundaryClass: "L4-TOOL-DB",
|
|
path: proof.path ?? "axis/db_demo/base.ngc",
|
|
dbProgramPath: proof.dbProgramPath ?? "./db_nonran.py",
|
|
runtimeMode: proof.runtimeMode ?? "browser-python-wasm-worker",
|
|
nativeProtocolReady,
|
|
nativeRuntimeRequiredForPromotion: true,
|
|
wasmProtocolReady,
|
|
browserProtocolReady,
|
|
opfsPersistenceReady,
|
|
tblFallbackSufficient,
|
|
promotionAllowed,
|
|
executionEnabled: proof.executionEnabled === true,
|
|
requiredCommands: [
|
|
"wasm-port/tests/sdk/node/verify_tool_db_process_port.sh",
|
|
"wasm-port/tests/opfs/node/verify_tool_db_store.sh",
|
|
"wasm-port/tests/wasm/node/verify_tool_db_process_port_wasm.sh",
|
|
"SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_tool_db_process_browser.sh",
|
|
],
|
|
};
|
|
}
|
|
|
|
function createPythonRemapRuntimeProofSummary(pythonRemapRuntimeProof = {}) {
|
|
const proof = objectOrEmpty(pythonRemapRuntimeProof);
|
|
const nativeLifecycleReady = proof.nativeLifecycleReady === true;
|
|
const wasmLifecycleReady = proof.wasmLifecycleReady !== false;
|
|
const browserLifecycleReady = proof.browserLifecycleReady !== false;
|
|
const promotionAllowed = proof.promotionAllowed === true;
|
|
const executionEnabled = proof.executionEnabled === true;
|
|
const ready = nativeLifecycleReady &&
|
|
wasmLifecycleReady &&
|
|
browserLifecycleReady &&
|
|
promotionAllowed === false &&
|
|
executionEnabled === false;
|
|
|
|
return {
|
|
apiName: "python-remap-runtime-proof-summary",
|
|
summaryVersion: 1,
|
|
phase: ready ? "ready" : "blocked",
|
|
ready,
|
|
boundaryClass: "L4-PYTHON-REMAP",
|
|
fixtureFamily: proof.fixtureFamily ?? "axis/remap/stop-lookahead/nc_files",
|
|
fixtureId: proof.fixtureId ?? "stop_lookahead_python_runtime_lifecycle",
|
|
iniPath: proof.iniPath ?? "axis/remap/stop-lookahead/demo.ini",
|
|
pythonPathPrepend: proof.pythonPathPrepend ?? "python",
|
|
topLevelPath: proof.topLevelPath ?? "python/toplevel.py",
|
|
callableName: proof.callableName ?? "queuebuster",
|
|
runtimeMode: proof.runtimeMode ?? "browser-python-wasm-worker",
|
|
nativeProbeStatus: proof.nativeProbeStatus ?? (nativeLifecycleReady ? "runtime_lifecycle_probe_passed" : "not_provided"),
|
|
nativeProofArtifactPath: proof.nativeProofArtifactPath ?? "wasm-port/build/native/native-runtime-probe-summary.tsv",
|
|
nativeProofStdoutLog: proof.nativeProofStdoutLog ?? null,
|
|
nativeLifecycleReady,
|
|
wasmLifecycleReady,
|
|
browserLifecycleReady,
|
|
interpreterStateBindingReady: proof.interpreterStateBindingReady !== false,
|
|
generatorLifecycleReady: proof.generatorLifecycleReady !== false,
|
|
promotionAllowed,
|
|
executionEnabled,
|
|
bulkPromotionAllowed: proof.bulkPromotionAllowed === true,
|
|
manualLockUpdateRequired: proof.manualLockUpdateRequired !== false,
|
|
requiredCommands: [
|
|
"ENABLE_PYTHON_REMAP_RUNTIME_PROBE=1 wasm-port/tests/native/probe_python_remap_runtime.sh",
|
|
"wasm-port/tests/sdk/node/verify_python_remap_runtime_port.sh",
|
|
"SKIP_INTERP_BUILD=1 wasm-port/tests/wasm/node/verify_python_remap_runtime_port_wasm.sh",
|
|
"SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_python_remap_runtime_browser.sh",
|
|
],
|
|
};
|
|
}
|
|
|
|
const BLOCKED_RUNTIME_FAMILIES = [
|
|
"L4-USER-M-PROCESS",
|
|
"L4-PYTHON-REMAP",
|
|
];
|
|
|
|
const PROJECT_RELEASE_READINESS_ARTIFACT_API = "project-release-readiness-report";
|
|
const PROJECT_RELEASE_READINESS_ARTIFACT_VERSION = 1;
|
|
|
|
const REQUIRED_PROMOTION_CANDIDATES = [
|
|
{
|
|
id: "qtdragon-multi-joint-on-abort",
|
|
sourceFiles: [
|
|
"linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/qtdragon_xyyz.ini",
|
|
"linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/on_abort.ngc",
|
|
],
|
|
},
|
|
{
|
|
id: "qtdragon-xyz-on-abort",
|
|
sourceFiles: [
|
|
"linuxcnc/configs/sim/qtdragon/qtdragon_xyz/qtdragon_inch.ini",
|
|
"linuxcnc/configs/sim/qtdragon/qtdragon_xyz/on_abort.ngc",
|
|
],
|
|
},
|
|
{
|
|
id: "qtdragon-xyz45-on-abort",
|
|
sourceFiles: [
|
|
"linuxcnc/configs/sim/qtdragon/qtdragon_xyz45/qtdragon_xyza.ini",
|
|
"linuxcnc/configs/sim/qtdragon/qtdragon_xyz45/on_abort.ngc",
|
|
],
|
|
},
|
|
{
|
|
id: "qtdragon-hd-xyz-on-abort",
|
|
sourceFiles: [
|
|
"linuxcnc/configs/sim/qtdragon_hd/qtdragon_hd_xyz/qtdragon_hd_vertical.ini",
|
|
"linuxcnc/configs/sim/qtdragon_hd/qtdragon_hd_xyz/on_abort.ngc",
|
|
],
|
|
},
|
|
{
|
|
id: "qtdragon-hd-z-compensation-on-abort",
|
|
sourceFiles: [
|
|
"linuxcnc/configs/sim/qtdragon_hd/qtdragon_hd_z_compensation/qtdragon_hd_z_compensation.ini",
|
|
"linuxcnc/configs/sim/qtdragon_hd/qtdragon_hd_z_compensation/on_abort.ngc",
|
|
],
|
|
},
|
|
{
|
|
id: "qtvcp-screens-qtdragon-on-abort",
|
|
sourceFiles: [
|
|
"linuxcnc/configs/sim/qtvcp_screens/qtdragon/qtdragon_mpg.ini",
|
|
"linuxcnc/configs/sim/qtvcp_screens/qtdragon/on_abort.ngc",
|
|
],
|
|
},
|
|
{
|
|
id: "puma-seam-weld",
|
|
sourceFiles: [
|
|
"linuxcnc/configs/sim/axis/vismach/puma/puma.ini",
|
|
"linuxcnc/configs/sim/axis/vismach/puma/puma_seam_weld.ngc",
|
|
"linuxcnc/configs/sim/axis/vismach/puma/remap_subs/428remap.ngc",
|
|
],
|
|
},
|
|
{
|
|
id: "rose-engine-rcone-demo",
|
|
sourceFiles: [
|
|
"linuxcnc/configs/sim/axis/rose_engine/rose_engine.ini",
|
|
"linuxcnc/configs/sim/axis/rose_engine/rcone_demo.ngc",
|
|
],
|
|
},
|
|
];
|
|
|
|
const REQUIRED_EVIDENCE_EXPANSION_CANDIDATES = [
|
|
{
|
|
id: "woodpecker-on-abort",
|
|
sourceFiles: [
|
|
"linuxcnc/configs/sim/woodpecker/woodpecker.ini",
|
|
"linuxcnc/configs/sim/woodpecker/on_abort.ngc",
|
|
"linuxcnc/configs/sim/woodpecker/tool.tbl",
|
|
],
|
|
},
|
|
{
|
|
id: "puma-cube",
|
|
sourceFiles: [
|
|
"linuxcnc/configs/sim/axis/vismach/puma/puma_cube.ini",
|
|
"linuxcnc/configs/sim/axis/vismach/puma/puma_cube.ngc",
|
|
"linuxcnc/configs/sim/axis/vismach/puma/remap_subs/428remap.ngc",
|
|
],
|
|
},
|
|
{
|
|
id: "melfa-example",
|
|
sourceFiles: [
|
|
"linuxcnc/configs/sim/axis/vismach/melfa-sim/melfa.ini",
|
|
"linuxcnc/configs/sim/axis/vismach/melfa-sim/example.ngc",
|
|
"linuxcnc/configs/sim/axis/vismach/melfa-sim/remap_subs/428remap.ngc",
|
|
],
|
|
},
|
|
];
|
|
|
|
const REQUIRED_MACRO_LOAD_FIXTURES = [
|
|
{
|
|
id: "rose-engine-rcone-macro-load",
|
|
fixturePath: "linuxcnc/configs/sim/axis/rose_engine/rcone.ngc",
|
|
declarationEvidence: {
|
|
sourceFile: "linuxcnc/configs/sim/axis/rose_engine/rose_engine.ini",
|
|
key: "NGCGUI_SUBFILE",
|
|
value: "rcone.ngc",
|
|
line: 34,
|
|
},
|
|
sourceFiles: [
|
|
"linuxcnc/configs/sim/axis/rose_engine/rose_engine.ini",
|
|
"linuxcnc/configs/sim/axis/rose_engine/rcone.ngc",
|
|
"linuxcnc/configs/sim/axis/rose_engine/rcone_demo.ngc",
|
|
],
|
|
},
|
|
{
|
|
id: "external-offsets-queuebuster-macro-load",
|
|
fixturePath: "linuxcnc/configs/sim/axis/external_offsets/queuebuster.ngc",
|
|
declarationEvidence: {
|
|
sourceFile: "linuxcnc/configs/sim/axis/external_offsets/eoffsets.ini",
|
|
key: "NGCGUI_SUBFILE",
|
|
value: "queuebuster.ngc",
|
|
line: 40,
|
|
},
|
|
sourceFiles: [
|
|
"linuxcnc/configs/sim/axis/external_offsets/eoffsets.ini",
|
|
"linuxcnc/configs/sim/axis/external_offsets/eoffsets.ngc",
|
|
],
|
|
},
|
|
];
|
|
|
|
const REQUIRED_BLOCKED_MACRO_LOAD_FIXTURES = [
|
|
{
|
|
id: "silverdragon-tool-sensor-python-ui-boundary",
|
|
fixturePath: "linuxcnc/configs/sim/gscreen/silverdragon/macros/tool_sensor.ngc",
|
|
blockedKind: "PYTHON-UI-PROCESS",
|
|
},
|
|
{
|
|
id: "gmoccapy-on-abort-python-remap-boundary",
|
|
fixturePath: "linuxcnc/configs/sim/gmoccapy/macros/on_abort.ngc",
|
|
blockedKind: "L4-PYTHON-REMAP",
|
|
},
|
|
];
|
|
|
|
const REQUIRED_MACRO_LOAD_AUDIT_CANDIDATES = [
|
|
{
|
|
id: "gscreen-industrial-lathe-wear-toolchange-audit",
|
|
fixturePath: "linuxcnc/configs/sim/gscreen/industrial_lathe_wear/toolchange.ngc",
|
|
auditStatus: "blocked-missing-owning-ini",
|
|
},
|
|
{
|
|
id: "qtvcp-industrial-lathe-wear-toolchange-audit",
|
|
fixturePath: "linuxcnc/configs/sim/qtvcp_screens/industrial_lathe_wear/toolchange.ngc",
|
|
auditStatus: "blocked-missing-owning-ini",
|
|
},
|
|
];
|
|
|
|
function isVirtualHalSimConfigSourceCoverageReady(report) {
|
|
const coverage = objectOrEmpty(report);
|
|
return coverage.apiName === "linuxcnc-wasm-virtual-hal-sim-config-source-coverage-report" &&
|
|
coverage.complete === true &&
|
|
coverage.webSimulationSatisfied === true &&
|
|
arrayOrEmpty(coverage.missingTargets).length === 0 &&
|
|
arrayOrEmpty(coverage.missingCapabilities).length === 0 &&
|
|
arrayOrEmpty(coverage.sourceFiles).includes("linuxcnc/configs/sim/axis/foam/axis_foam.ini") &&
|
|
arrayOrEmpty(coverage.sourceFiles).includes("linuxcnc/configs/sim/qtdragon/qtdragon_xyz/on_abort.ngc") &&
|
|
arrayOrEmpty(coverage.sourceFiles).includes("linuxcnc/configs/sim/qtdragon/qtdragon_xyz45/qtdragon_xyza.ini") &&
|
|
arrayOrEmpty(coverage.sourceFiles).includes(
|
|
"linuxcnc/configs/sim/qtdragon_hd/qtdragon_hd_z_compensation/qtdragon_hd_z_compensation.ini",
|
|
);
|
|
}
|
|
|
|
function hasRequiredPromotionCandidates(rows) {
|
|
return REQUIRED_PROMOTION_CANDIDATES.every((candidate) =>
|
|
rows.some((row) =>
|
|
row.id === candidate.id &&
|
|
row.complete === true &&
|
|
row.currentNodeInventoryStatus === "PASS" &&
|
|
row.blockedKind === "-" &&
|
|
row.targetBrowserEvidence === "explicit-browser-diagnostics" &&
|
|
candidate.sourceFiles.every((file) => arrayOrEmpty(row.sourceFiles).includes(file))
|
|
)
|
|
);
|
|
}
|
|
|
|
function isVirtualHalSimConfigMacroLoadFixtureReportReady(report) {
|
|
const fixtureReport = objectOrEmpty(report);
|
|
const rows = arrayOrEmpty(fixtureReport.rows);
|
|
return fixtureReport.apiName === "linuxcnc-wasm-virtual-hal-sim-config-macro-load-fixture-report" &&
|
|
fixtureReport.complete === true &&
|
|
fixtureReport.webSimulationSatisfied === true &&
|
|
fixtureReport.inventoryBaselineUnchanged === true &&
|
|
arrayOrEmpty(fixtureReport.missingFixtures).length === 0 &&
|
|
arrayOrEmpty(fixtureReport.blockedFixtureIds).length === 0 &&
|
|
arrayOrEmpty(fixtureReport.declarationEvidenceViolations).length === 0 &&
|
|
arrayOrEmpty(fixtureReport.standaloneMainViolations).length === 0 &&
|
|
arrayOrEmpty(fixtureReport.blockedFixturePromotionViolations).length === 0 &&
|
|
arrayOrEmpty(fixtureReport.blockedBoundaryEvidenceViolations).length === 0 &&
|
|
arrayOrEmpty(fixtureReport.missingAuditRows).length === 0 &&
|
|
arrayOrEmpty(fixtureReport.auditPromotionViolations).length === 0 &&
|
|
arrayOrEmpty(fixtureReport.auditBoundaryEvidenceViolations).length === 0 &&
|
|
arrayOrEmpty(fixtureReport.auditPromotionAllowedViolations).length === 0 &&
|
|
arrayOrEmpty(fixtureReport.missingManifestFiles).length === 0 &&
|
|
REQUIRED_MACRO_LOAD_FIXTURES.every((fixture) =>
|
|
rows.some((row) =>
|
|
row.id === fixture.id &&
|
|
row.complete === true &&
|
|
row.nonMainFixture === true &&
|
|
row.targetBrowserEvidence === "non-main-fixture-diagnostics" &&
|
|
row.fixturePath === fixture.fixturePath &&
|
|
(
|
|
!fixture.declarationEvidence ||
|
|
(
|
|
row.declarationEvidenceReady === true &&
|
|
objectOrEmpty(row.declarationEvidence).sourceFile === fixture.declarationEvidence.sourceFile &&
|
|
objectOrEmpty(row.declarationEvidence).key === fixture.declarationEvidence.key &&
|
|
objectOrEmpty(row.declarationEvidence).value === fixture.declarationEvidence.value &&
|
|
objectOrEmpty(row.declarationEvidence).line === fixture.declarationEvidence.line
|
|
)
|
|
) &&
|
|
fixture.sourceFiles.every((file) => arrayOrEmpty(row.sourceFiles).includes(file))
|
|
)
|
|
) &&
|
|
REQUIRED_BLOCKED_MACRO_LOAD_FIXTURES.every((fixture) =>
|
|
arrayOrEmpty(fixtureReport.blockedRows).some((row) =>
|
|
row.id === fixture.id &&
|
|
row.fixturePath === fixture.fixturePath &&
|
|
row.blockedKind === fixture.blockedKind &&
|
|
row.excludedFromPositiveFixtures === true &&
|
|
row.boundaryEvidenceReady === true &&
|
|
isBoundaryEvidenceBaselineFresh(row.boundaryEvidence) &&
|
|
objectOrEmpty(row.boundaryEvidence?.boundarySummary).recommendedBlocked === "UNAVAILABLE" &&
|
|
String(objectOrEmpty(row.boundaryEvidence?.boundarySummary).dependencies ?? "").includes("missing_vendored_ini:") &&
|
|
objectOrEmpty(row.boundaryEvidence?.iniBoundarySummary).vendored === 0 &&
|
|
objectOrEmpty(row.boundaryEvidence?.iniBoundarySummary).reportAvailable === 0 &&
|
|
row.complete === true
|
|
)
|
|
) &&
|
|
REQUIRED_MACRO_LOAD_AUDIT_CANDIDATES.every((candidate) =>
|
|
arrayOrEmpty(fixtureReport.auditRows).some((row) =>
|
|
row.id === candidate.id &&
|
|
row.fixturePath === candidate.fixturePath &&
|
|
row.auditStatus === candidate.auditStatus &&
|
|
row.promotionAllowed === false &&
|
|
row.excludedFromPositiveFixtures === true &&
|
|
row.boundaryEvidenceReady === true &&
|
|
isBoundaryEvidenceBaselineFresh(row.boundaryEvidence) &&
|
|
objectOrEmpty(row.boundaryEvidence?.boundarySummary).recommendedBlocked === "UNAVAILABLE" &&
|
|
String(objectOrEmpty(row.boundaryEvidence?.boundarySummary).dependencies ?? "").includes("missing_vendored_ini:") &&
|
|
objectOrEmpty(row.boundaryEvidence?.iniBoundarySummary).vendored === 0 &&
|
|
objectOrEmpty(row.boundaryEvidence?.iniBoundarySummary).reportAvailable === 0 &&
|
|
row.complete === true
|
|
)
|
|
);
|
|
}
|
|
|
|
function isVirtualHalMotionControllerMatrixReady(report) {
|
|
const matrix = objectOrEmpty(report);
|
|
return matrix.apiName === "linuxcnc-wasm-virtual-hal-motion-controller-matrix-report" &&
|
|
matrix.complete === true &&
|
|
matrix.webSimulationSatisfied === true &&
|
|
matrix.manifestChecked === true &&
|
|
arrayOrEmpty(matrix.missingFixtures).length === 0 &&
|
|
arrayOrEmpty(matrix.missingSimConfigTargets).length === 0 &&
|
|
arrayOrEmpty(matrix.missingManifestFiles).length === 0 &&
|
|
arrayOrEmpty(matrix.requiredPins).includes("motion.distance-to-go") &&
|
|
arrayOrEmpty(matrix.requiredPins).includes("motion.in-position") &&
|
|
arrayOrEmpty(matrix.sourceFiles).includes("linuxcnc/src/emc/motion/motion.c") &&
|
|
arrayOrEmpty(matrix.sourceFiles).includes("linuxcnc/src/emc/motion/axis.c") &&
|
|
arrayOrEmpty(matrix.simConfigTargets).includes("external-offsets") &&
|
|
arrayOrEmpty(matrix.simConfigTargets).includes("qtdragon-on-abort") &&
|
|
arrayOrEmpty(matrix.simConfigTargets).includes("vismach-remap-sims") &&
|
|
arrayOrEmpty(matrix.simConfigSourceFiles).includes("linuxcnc/configs/sim/axis/external_offsets/dynamic_offsets.ini") &&
|
|
arrayOrEmpty(matrix.simConfigSourceFiles).includes("linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/qtdragon_xyyz.ini") &&
|
|
arrayOrEmpty(matrix.simConfigSourceFiles).includes("linuxcnc/configs/sim/axis/vismach/puma/puma.ini");
|
|
}
|
|
|
|
function isVirtualHalSimConfigPromotionCandidateReportReady(report) {
|
|
const candidateReport = objectOrEmpty(report);
|
|
const rows = arrayOrEmpty(candidateReport.rows);
|
|
return candidateReport.apiName === "linuxcnc-wasm-virtual-hal-sim-config-promotion-candidate-report" &&
|
|
candidateReport.complete === true &&
|
|
candidateReport.webSimulationSatisfied === true &&
|
|
candidateReport.inventoryBaselineUnchanged === true &&
|
|
arrayOrEmpty(candidateReport.missingCandidates).length === 0 &&
|
|
arrayOrEmpty(candidateReport.blockedCandidateIds).length === 0 &&
|
|
arrayOrEmpty(candidateReport.missingManifestFiles).length === 0 &&
|
|
arrayOrEmpty(candidateReport.missingRequiredReports).length === 0 &&
|
|
hasRequiredPromotionCandidates(rows);
|
|
}
|
|
|
|
function isVirtualHalSimConfigEvidenceExpansionReportReady(report) {
|
|
const expansionReport = objectOrEmpty(report);
|
|
const rows = arrayOrEmpty(expansionReport.rows);
|
|
return expansionReport.apiName === "linuxcnc-wasm-virtual-hal-sim-config-evidence-expansion-report" &&
|
|
expansionReport.complete === true &&
|
|
expansionReport.webSimulationSatisfied === true &&
|
|
expansionReport.inventoryBaselineUnchanged === true &&
|
|
expansionReport.promotionAllowed === false &&
|
|
expansionReport.candidateCount === 3 &&
|
|
expansionReport.readyCandidateCount === 3 &&
|
|
arrayOrEmpty(expansionReport.missingCandidates).length === 0 &&
|
|
arrayOrEmpty(expansionReport.promotionAllowedViolations).length === 0 &&
|
|
arrayOrEmpty(expansionReport.blockedCandidateIds).length === 0 &&
|
|
arrayOrEmpty(expansionReport.missingManifestFiles).length === 0 &&
|
|
arrayOrEmpty(expansionReport.missingRequiredReports).length === 0 &&
|
|
REQUIRED_EVIDENCE_EXPANSION_CANDIDATES.every((candidate) =>
|
|
rows.some((row) =>
|
|
row.id === candidate.id &&
|
|
row.complete === true &&
|
|
row.currentNodeInventoryStatus === "PASS" &&
|
|
row.currentMatrixBrowserStatus === "REP" &&
|
|
row.targetBrowserEvidence === "browser-diagnostics-expansion" &&
|
|
row.blockedKind === "-" &&
|
|
row.promotionAllowed === false &&
|
|
candidate.sourceFiles.every((file) => arrayOrEmpty(row.sourceFiles).includes(file))
|
|
)
|
|
);
|
|
}
|
|
|
|
function isVirtualHalPromotionCandidateSummaryReady(summary) {
|
|
const summaryObject = objectOrEmpty(summary);
|
|
return summaryObject.apiName === "real-browser-simulation-promotion-candidate-summary" &&
|
|
summaryObject.ready === true &&
|
|
summaryObject.phase === "ready" &&
|
|
summaryObject.candidateCount === 8 &&
|
|
summaryObject.readyCandidateCount === 8 &&
|
|
summaryObject.familyCount === 3 &&
|
|
summaryObject.sourceFileCount === 17 &&
|
|
summaryObject.preferredCandidateId === "qtdragon-multi-joint-on-abort" &&
|
|
summaryObject.preferredIniPath === "linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/qtdragon_xyyz.ini" &&
|
|
summaryObject.preferredGcodePath === "linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/on_abort.ngc" &&
|
|
summaryObject.explicitBrowserDiagnosticsCount === 8 &&
|
|
summaryObject.inventoryBaseline === "executed=82 passed=82 skipped=77 unexpected_fail=0" &&
|
|
arrayOrEmpty(summaryObject.blockedCandidateIds).length === 0;
|
|
}
|
|
|
|
export function createProjectReleaseBrowserDiagnosticsArtifactValidation(artifact = {}) {
|
|
const artifactObject = objectOrEmpty(artifact);
|
|
const sourceCompliance = objectOrEmpty(artifactObject.virtualHalSourceCompliance);
|
|
const simConfigSourceCoverage = objectOrEmpty(artifactObject.virtualHalSimConfigSourceCoverage);
|
|
const commandScriptFixtures = objectOrEmpty(artifactObject.virtualHalCommandScriptFixtures);
|
|
const motionControllerMatrix = objectOrEmpty(artifactObject.virtualHalMotionControllerMatrix);
|
|
const promotionCandidates = objectOrEmpty(artifactObject.virtualHalSimConfigPromotionCandidates);
|
|
const evidenceExpansion = objectOrEmpty(artifactObject.virtualHalSimConfigEvidenceExpansion);
|
|
const promotionCandidateSummary = objectOrEmpty(artifactObject.virtualHalPromotionCandidateSummary);
|
|
const macroLoadFixtures = objectOrEmpty(artifactObject.virtualHalSimConfigMacroLoadFixtures);
|
|
const sessionDiagnostics = objectOrEmpty(artifactObject.virtualHalSessionDiagnostics);
|
|
const replacement = objectOrEmpty(artifactObject.virtualHalSimulationReplacement);
|
|
const virtualHal = objectOrEmpty(artifactObject.virtualHal);
|
|
const sourceComplianceReady = sourceCompliance.complete === true &&
|
|
sourceCompliance.webSimulationSatisfied === true &&
|
|
arrayOrEmpty(sourceCompliance.sourceFiles).includes("linuxcnc/src/hal/utils/halcmd_commands.cc");
|
|
const simConfigSourceCoverageReady = isVirtualHalSimConfigSourceCoverageReady(simConfigSourceCoverage);
|
|
const commandScriptFixturesReady = commandScriptFixtures.complete === true &&
|
|
commandScriptFixtures.webSimulationSatisfied === true &&
|
|
arrayOrEmpty(commandScriptFixtures.coveredActions).includes("loadusr") &&
|
|
arrayOrEmpty(commandScriptFixtures.coveredActions).includes("stop") &&
|
|
arrayOrEmpty(commandScriptFixtures.sourceFiles).includes("linuxcnc/src/hal/utils/halcmd_commands.cc");
|
|
const motionControllerMatrixReady = isVirtualHalMotionControllerMatrixReady(motionControllerMatrix);
|
|
const hasPromotionCandidates = artifactObject.virtualHalSimConfigPromotionCandidates !== undefined;
|
|
const promotionCandidatesReady = !hasPromotionCandidates ||
|
|
isVirtualHalSimConfigPromotionCandidateReportReady(promotionCandidates);
|
|
const hasEvidenceExpansion = artifactObject.virtualHalSimConfigEvidenceExpansion !== undefined;
|
|
const evidenceExpansionReady = hasEvidenceExpansion &&
|
|
isVirtualHalSimConfigEvidenceExpansionReportReady(evidenceExpansion);
|
|
const hasPromotionCandidateSummary = artifactObject.virtualHalPromotionCandidateSummary !== undefined;
|
|
const promotionCandidateSummaryReady = hasPromotionCandidateSummary &&
|
|
isVirtualHalPromotionCandidateSummaryReady(promotionCandidateSummary);
|
|
const hasMacroLoadFixtures = artifactObject.virtualHalSimConfigMacroLoadFixtures !== undefined;
|
|
const macroLoadFixturesReady = hasMacroLoadFixtures &&
|
|
isVirtualHalSimConfigMacroLoadFixtureReportReady(macroLoadFixtures);
|
|
const replacementReady = replacement.ready === true &&
|
|
replacement.replacesHostRuntimeForSimulation === true &&
|
|
objectOrEmpty(replacement.sourceCompliance).webSimulationSatisfied === true &&
|
|
objectOrEmpty(replacement.motionControllerMatrix).complete === true;
|
|
const virtualHalReady = virtualHal.source === "browser-virtual-hal";
|
|
const hasSessionDiagnostics = artifactObject.virtualHalSessionDiagnostics !== undefined;
|
|
const sessionDiagnosticsReady = !hasSessionDiagnostics || (
|
|
sessionDiagnostics.apiName === "real-browser-simulation-virtual-hal-session-diagnostics" &&
|
|
sessionDiagnostics.ready === true &&
|
|
sessionDiagnostics.phase === "release-ready" &&
|
|
objectOrEmpty(sessionDiagnostics.validation).ready === true &&
|
|
objectOrEmpty(sessionDiagnostics.diagnosticsArtifact).apiName === "real-browser-simulation-diagnostics-artifact" &&
|
|
objectOrEmpty(sessionDiagnostics.diagnosticsArtifact?.virtualHalSourceCompliance).complete === true &&
|
|
objectOrEmpty(sessionDiagnostics.diagnosticsArtifact?.virtualHalMotionControllerMatrix).manifestChecked === true
|
|
);
|
|
const missing = [
|
|
...(artifactObject.apiName === "real-browser-simulation-diagnostics-artifact" ? [] : ["apiName"]),
|
|
...(virtualHalReady ? [] : ["virtualHal"]),
|
|
...(replacementReady ? [] : ["virtualHalSimulationReplacement"]),
|
|
...(sourceComplianceReady ? [] : ["virtualHalSourceCompliance"]),
|
|
...(simConfigSourceCoverageReady ? [] : ["virtualHalSimConfigSourceCoverage"]),
|
|
...(promotionCandidatesReady ? [] : ["virtualHalSimConfigPromotionCandidates"]),
|
|
...(evidenceExpansionReady ? [] : ["virtualHalSimConfigEvidenceExpansion"]),
|
|
...(promotionCandidateSummaryReady ? [] : ["virtualHalPromotionCandidateSummary"]),
|
|
...(macroLoadFixturesReady ? [] : ["virtualHalSimConfigMacroLoadFixtures"]),
|
|
...(commandScriptFixturesReady ? [] : ["virtualHalCommandScriptFixtures"]),
|
|
...(motionControllerMatrixReady ? [] : ["virtualHalMotionControllerMatrix"]),
|
|
...(sessionDiagnosticsReady ? [] : ["virtualHalSessionDiagnostics"]),
|
|
];
|
|
return {
|
|
apiName: "project-release-browser-diagnostics-artifact-validation",
|
|
validationVersion: 1,
|
|
phase: missing.length === 0 ? "ready" : "blocked",
|
|
ready: missing.length === 0,
|
|
missing,
|
|
artifactApiName: artifactObject.apiName ?? null,
|
|
virtualHalReady,
|
|
replacementReady,
|
|
sourceComplianceReady,
|
|
simConfigSourceCoverageReady,
|
|
promotionCandidatesReady,
|
|
hasPromotionCandidates,
|
|
evidenceExpansionReady,
|
|
hasEvidenceExpansion,
|
|
promotionCandidateSummaryReady,
|
|
hasPromotionCandidateSummary,
|
|
macroLoadFixturesReady,
|
|
hasMacroLoadFixtures,
|
|
commandScriptFixturesReady,
|
|
motionControllerMatrixReady,
|
|
sessionDiagnosticsReady,
|
|
hasSessionDiagnostics,
|
|
sourceFiles: [...new Set([
|
|
...arrayOrEmpty(sourceCompliance.sourceFiles),
|
|
...arrayOrEmpty(simConfigSourceCoverage.sourceFiles),
|
|
...arrayOrEmpty(promotionCandidates.sourceFiles),
|
|
...arrayOrEmpty(evidenceExpansion.sourceFiles),
|
|
...arrayOrEmpty(macroLoadFixtures.sourceFiles),
|
|
...arrayOrEmpty(commandScriptFixtures.sourceFiles),
|
|
...arrayOrEmpty(motionControllerMatrix.sourceFiles),
|
|
...arrayOrEmpty(motionControllerMatrix.simConfigSourceFiles),
|
|
])],
|
|
rows: [
|
|
{
|
|
id: "artifact",
|
|
label: "Browser diagnostics artifact",
|
|
value: artifactObject.apiName ?? "missing",
|
|
},
|
|
{
|
|
id: "virtual-hal",
|
|
label: "Virtual HAL state",
|
|
value: virtualHalReady ? "ready" : "missing",
|
|
},
|
|
{
|
|
id: "source-compliance",
|
|
label: "Virtual HAL source compliance",
|
|
value: sourceComplianceReady ? "ready" : "missing",
|
|
},
|
|
{
|
|
id: "sim-config-source-coverage",
|
|
label: "Virtual HAL sim-config source coverage",
|
|
value: simConfigSourceCoverageReady ? "ready" : "missing",
|
|
},
|
|
{
|
|
id: "sim-config-promotion-candidates",
|
|
label: "Virtual HAL sim-config promotion candidates",
|
|
value: hasPromotionCandidates ? (promotionCandidatesReady ? "ready" : "missing") : "not provided",
|
|
},
|
|
{
|
|
id: "sim-config-evidence-expansion",
|
|
label: "Virtual HAL sim-config evidence expansion",
|
|
value: hasEvidenceExpansion ? (evidenceExpansionReady ? "ready" : "missing") : "not provided",
|
|
},
|
|
{
|
|
id: "promotion-candidate-summary",
|
|
label: "Virtual HAL promotion candidate summary",
|
|
value: hasPromotionCandidateSummary ? (promotionCandidateSummaryReady ? "ready" : "missing") : "not provided",
|
|
},
|
|
{
|
|
id: "sim-config-macro-load-fixtures",
|
|
label: "Virtual HAL sim-config macro/load fixtures",
|
|
value: hasMacroLoadFixtures ? (macroLoadFixturesReady ? "ready" : "missing") : "not provided",
|
|
},
|
|
{
|
|
id: "command-script-fixtures",
|
|
label: "Virtual HAL command script fixtures",
|
|
value: commandScriptFixturesReady ? "ready" : "missing",
|
|
},
|
|
{
|
|
id: "motion-controller-matrix",
|
|
label: "Virtual HAL motion controller matrix",
|
|
value: motionControllerMatrixReady ? "ready" : "missing",
|
|
},
|
|
{
|
|
id: "session-diagnostics",
|
|
label: "Saved-session diagnostics",
|
|
value: hasSessionDiagnostics ? (sessionDiagnosticsReady ? "ready" : "missing") : "not provided",
|
|
},
|
|
{
|
|
id: "missing",
|
|
label: "Missing diagnostics evidence",
|
|
value: missing.length > 0 ? missing.join(", ") : "none",
|
|
},
|
|
],
|
|
};
|
|
}
|
|
|
|
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 = [],
|
|
virtualHalSimConfigSourceCoverage = null,
|
|
virtualHalMotionControllerMatrix = null,
|
|
} = {}) {
|
|
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 virtualHalSimConfigSourceCoverageReady = virtualHalSimConfigSourceCoverage === null ||
|
|
isVirtualHalSimConfigSourceCoverageReady(virtualHalSimConfigSourceCoverage);
|
|
const virtualHalMotionControllerMatrixReady = virtualHalMotionControllerMatrix === null ||
|
|
isVirtualHalMotionControllerMatrixReady(virtualHalMotionControllerMatrix);
|
|
const ready = capabilityMatrix.ready &&
|
|
batchGatePassed &&
|
|
virtualHalSimConfigSourceCoverageReady &&
|
|
virtualHalMotionControllerMatrixReady;
|
|
const missing = [
|
|
...capabilityMatrix.missing,
|
|
...(batchGatePassed ? [] : ["project-batch-acceptance"]),
|
|
...(virtualHalSimConfigSourceCoverageReady ? [] : ["virtual-hal-sim-config-source-coverage"]),
|
|
...(virtualHalMotionControllerMatrixReady ? [] : ["virtual-hal-motion-controller-matrix"]),
|
|
];
|
|
|
|
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,
|
|
virtualHalSimConfigSourceCoverage,
|
|
virtualHalSimConfigSourceCoverageReady,
|
|
virtualHalMotionControllerMatrix,
|
|
virtualHalMotionControllerMatrixReady,
|
|
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: "virtual-hal-sim-config-source-coverage",
|
|
label: "Virtual HAL sim-config source coverage",
|
|
value: virtualHalSimConfigSourceCoverage === null
|
|
? "not required"
|
|
: (virtualHalSimConfigSourceCoverageReady ? "ready" : "missing"),
|
|
},
|
|
{
|
|
id: "virtual-hal-motion-controller-matrix",
|
|
label: "Virtual HAL motion controller matrix",
|
|
value: virtualHalMotionControllerMatrix === null
|
|
? "not required"
|
|
: (virtualHalMotionControllerMatrixReady ? "ready" : "missing"),
|
|
},
|
|
{
|
|
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: "virtual-hal-sim-config-source-coverage",
|
|
label: "Virtual HAL sim-config source coverage",
|
|
status: workflow?.virtualHalSimConfigSourceCoverageReady !== false ? "pass" : "blocked",
|
|
passed: workflow?.virtualHalSimConfigSourceCoverageReady !== false,
|
|
detail: workflow?.virtualHalSimConfigSourceCoverage === null
|
|
? "not required"
|
|
: (workflow?.virtualHalSimConfigSourceCoverageReady === true ? "ready" : "missing"),
|
|
},
|
|
{
|
|
id: "virtual-hal-motion-controller-matrix",
|
|
label: "Virtual HAL motion controller matrix",
|
|
status: workflow?.virtualHalMotionControllerMatrixReady !== false ? "pass" : "blocked",
|
|
passed: workflow?.virtualHalMotionControllerMatrixReady !== false,
|
|
detail: workflow?.virtualHalMotionControllerMatrix === null
|
|
? "not required"
|
|
: (workflow?.virtualHalMotionControllerMatrixReady === true ? "ready" : "missing"),
|
|
},
|
|
{
|
|
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 = [],
|
|
virtualHalSimConfigSourceCoverage = null,
|
|
virtualHalMotionControllerMatrix = null,
|
|
} = {}) {
|
|
const capabilityMatrix = createProjectBatchAcceptanceCapabilityMatrix({ capabilities });
|
|
const workflow = createProjectBatchAcceptanceWorkflow({
|
|
batchId,
|
|
capabilities,
|
|
gateResults,
|
|
observedOutputs,
|
|
virtualHalSimConfigSourceCoverage,
|
|
virtualHalMotionControllerMatrix,
|
|
});
|
|
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,
|
|
virtualHalSimConfigSourceCoverage,
|
|
virtualHalMotionControllerMatrix,
|
|
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 virtualHalSimConfigSourceCoverage = objectOrEmpty(reportObject.virtualHalSimConfigSourceCoverage);
|
|
const virtualHalMotionControllerMatrix = objectOrEmpty(reportObject.virtualHalMotionControllerMatrix);
|
|
const rows = arrayOrEmpty(reportObject.rows);
|
|
const virtualHalSimConfigSourceCoverageRequired = reportObject.virtualHalSimConfigSourceCoverage !== null &&
|
|
reportObject.virtualHalSimConfigSourceCoverage !== undefined;
|
|
const virtualHalSimConfigSourceCoverageReady = !virtualHalSimConfigSourceCoverageRequired ||
|
|
isVirtualHalSimConfigSourceCoverageReady(virtualHalSimConfigSourceCoverage);
|
|
const virtualHalMotionControllerMatrixRequired = reportObject.virtualHalMotionControllerMatrix !== null &&
|
|
reportObject.virtualHalMotionControllerMatrix !== undefined;
|
|
const virtualHalMotionControllerMatrixReady = !virtualHalMotionControllerMatrixRequired ||
|
|
isVirtualHalMotionControllerMatrixReady(virtualHalMotionControllerMatrix);
|
|
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"]),
|
|
...(virtualHalSimConfigSourceCoverageReady ? [] : ["virtualHalSimConfigSourceCoverage"]),
|
|
...(virtualHalMotionControllerMatrixReady ? [] : ["virtualHalMotionControllerMatrix"]),
|
|
...(rows.find(({ id, value }) => id === "checklist" && /^([45])\/\1 passed$/.test(value))
|
|
? []
|
|
: ["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,
|
|
virtualHalSimConfigSourceCoverageReady,
|
|
virtualHalMotionControllerMatrixReady,
|
|
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: "virtual-hal-sim-config-source-coverage",
|
|
label: "Virtual HAL sim-config source coverage",
|
|
value: virtualHalSimConfigSourceCoverageRequired
|
|
? (virtualHalSimConfigSourceCoverageReady ? "ready" : "missing")
|
|
: "not required",
|
|
},
|
|
{
|
|
id: "virtual-hal-motion-controller-matrix",
|
|
label: "Virtual HAL motion controller matrix",
|
|
value: virtualHalMotionControllerMatrixRequired
|
|
? (virtualHalMotionControllerMatrixReady ? "ready" : "missing")
|
|
: "not required",
|
|
},
|
|
{
|
|
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,
|
|
virtualHalSimConfigSourceCoverage = null,
|
|
virtualHalSimConfigPromotionCandidates = null,
|
|
virtualHalSimConfigEvidenceExpansion = null,
|
|
promotionCandidateRows = [],
|
|
evidenceExpansionRows = [],
|
|
virtualHalSimConfigMacroLoadFixtures = null,
|
|
virtualHalMotionControllerMatrix = null,
|
|
toolDbProcessProof = {},
|
|
pythonRemapRuntimeProof = {},
|
|
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 virtualHalSimConfigSourceCoverageReady = isVirtualHalSimConfigSourceCoverageReady(
|
|
virtualHalSimConfigSourceCoverage,
|
|
);
|
|
const virtualHalSimConfigPromotionCandidatesReady = isVirtualHalSimConfigPromotionCandidateReportReady(
|
|
virtualHalSimConfigPromotionCandidates,
|
|
);
|
|
const virtualHalPromotionFamilySummary = createPromotionFamilySummaryText(virtualHalSimConfigPromotionCandidates);
|
|
const virtualHalPromotionFamilyRows = createPromotionFamilySummaryRows(virtualHalSimConfigPromotionCandidates);
|
|
const virtualHalSimConfigMacroLoadFixturesReady = isVirtualHalSimConfigMacroLoadFixtureReportReady(
|
|
virtualHalSimConfigMacroLoadFixtures,
|
|
);
|
|
const virtualHalMotionControllerMatrixReady = isVirtualHalMotionControllerMatrixReady(
|
|
virtualHalMotionControllerMatrix,
|
|
);
|
|
const blockedRuntimeReady = promotedBlockedFamilies.length === 0;
|
|
const hardBlockRuntimeLockSummary = createHardBlockRuntimeLockSummary(blockedFamilies, promotedBlockedFamilies);
|
|
const promotionCandidateArtifactSummary = createPromotionCandidateArtifactSummary({
|
|
candidateReport: virtualHalSimConfigPromotionCandidates,
|
|
evidenceExpansionReport: virtualHalSimConfigEvidenceExpansion,
|
|
inventory,
|
|
blockedFamilies,
|
|
promotedBlockedFamilies,
|
|
promotionCandidateRows,
|
|
evidenceExpansionRows,
|
|
});
|
|
const axisScreenshotArtifactSummary = createAxisScreenshotArtifactSummary(axisScreenshotArtifacts);
|
|
const axisScreenshotArtifactsReady = axisScreenshotArtifactSummary.ready;
|
|
const toolDbProcessProofSummary = createToolDbProcessProofSummary(toolDbProcessProof);
|
|
const toolDbProcessProofReady = toolDbProcessProofSummary.ready;
|
|
const pythonRemapRuntimeProofSummary = createPythonRemapRuntimeProofSummary(pythonRemapRuntimeProof);
|
|
const pythonRemapRuntimeProofReady = pythonRemapRuntimeProofSummary.ready;
|
|
const ready = releaseGatePassed &&
|
|
inventoryReady &&
|
|
virtualHalSimConfigSourceCoverageReady &&
|
|
virtualHalSimConfigPromotionCandidatesReady &&
|
|
virtualHalSimConfigMacroLoadFixturesReady &&
|
|
virtualHalMotionControllerMatrixReady &&
|
|
toolDbProcessProofReady &&
|
|
pythonRemapRuntimeProofReady &&
|
|
blockedRuntimeReady &&
|
|
axisScreenshotArtifactsReady;
|
|
const missing = [
|
|
...(releaseGatePassed ? [] : ["project-release-gate"]),
|
|
...(inventoryReady ? [] : ["sim-config-inventory"]),
|
|
...(virtualHalSimConfigSourceCoverageReady ? [] : ["virtual-hal-sim-config-source-coverage"]),
|
|
...(virtualHalSimConfigPromotionCandidatesReady ? [] : ["virtual-hal-sim-config-promotion-candidates"]),
|
|
...(virtualHalSimConfigMacroLoadFixturesReady ? [] : ["virtual-hal-sim-config-macro-load-fixtures"]),
|
|
...(virtualHalMotionControllerMatrixReady ? [] : ["virtual-hal-motion-controller-matrix"]),
|
|
...(toolDbProcessProofReady ? [] : ["tool-db-process-proof"]),
|
|
...(pythonRemapRuntimeProofReady ? [] : ["python-remap-runtime-proof"]),
|
|
...(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,
|
|
virtualHalSimConfigSourceCoverage,
|
|
virtualHalSimConfigSourceCoverageReady,
|
|
virtualHalSimConfigPromotionCandidates,
|
|
virtualHalSimConfigPromotionCandidatesReady,
|
|
virtualHalSimConfigEvidenceExpansion,
|
|
virtualHalPromotionFamilySummary,
|
|
virtualHalPromotionFamilyRows,
|
|
promotionCandidateArtifactSummary,
|
|
virtualHalSimConfigMacroLoadFixtures,
|
|
virtualHalSimConfigMacroLoadFixturesReady,
|
|
virtualHalMotionControllerMatrix,
|
|
virtualHalMotionControllerMatrixReady,
|
|
toolDbProcessProofSummary,
|
|
toolDbProcessProofReady,
|
|
pythonRemapRuntimeProofSummary,
|
|
pythonRemapRuntimeProofReady,
|
|
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: "virtual-hal-sim-config-source-coverage",
|
|
label: "Virtual HAL sim-config source coverage",
|
|
value: virtualHalSimConfigSourceCoverageReady ? "ready" : "missing",
|
|
},
|
|
{
|
|
id: "virtual-hal-sim-config-promotion-candidates",
|
|
label: "Virtual HAL sim-config promotion candidates",
|
|
value: virtualHalSimConfigPromotionCandidatesReady ? "ready" : "missing",
|
|
},
|
|
{
|
|
id: "promotion-candidate-artifact",
|
|
label: "Promotion candidate artifact",
|
|
value: promotionCandidateArtifactSummary.artifactPath,
|
|
},
|
|
{
|
|
id: "promotion-candidate-artifact-rows",
|
|
label: "Promotion candidate artifact rows",
|
|
value: `${promotionCandidateArtifactSummary.artifactRows.promotionCandidates}`,
|
|
},
|
|
{
|
|
id: "evidence-expansion-artifact-rows",
|
|
label: "Evidence expansion artifact rows",
|
|
value: `${promotionCandidateArtifactSummary.artifactRows.evidenceExpansion}`,
|
|
},
|
|
{
|
|
id: "promotion-candidate-layers",
|
|
label: "Promotion candidate layers",
|
|
value: `evidence-ready=${promotionCandidateArtifactSummary.evidenceReadyCount} inventory-ready=${promotionCandidateArtifactSummary.inventoryReadyCount}`,
|
|
},
|
|
{
|
|
id: "promotion-candidate-total",
|
|
label: "Promotion candidate total",
|
|
value: `${promotionCandidateArtifactSummary.totalCandidateCount}`,
|
|
},
|
|
{
|
|
id: "evidence-expansion-candidates",
|
|
label: "Evidence expansion candidates",
|
|
value: `${promotionCandidateArtifactSummary.evidenceExpansionCandidateCount}`,
|
|
},
|
|
{
|
|
id: "evidence-expansion-next-evidence",
|
|
label: "Evidence expansion next evidence",
|
|
value: promotionCandidateArtifactSummary.evidenceExpansion.nextEvidence,
|
|
},
|
|
{
|
|
id: "evidence-expansion-baseline-changing",
|
|
label: "Evidence expansion baseline changing",
|
|
value: promotionCandidateArtifactSummary.evidenceExpansion.baselineChanging ? "yes" : "no",
|
|
},
|
|
{
|
|
id: "evidence-expansion-artifact",
|
|
label: "Evidence expansion artifact",
|
|
value: promotionCandidateArtifactSummary.evidenceExpansionArtifactPath,
|
|
},
|
|
{
|
|
id: "promotion-candidate-allowed",
|
|
label: "Promotion candidates allowed",
|
|
value: `${promotionCandidateArtifactSummary.promotionAllowedCount}`,
|
|
},
|
|
{
|
|
id: "virtual-hal-promotion-families",
|
|
label: "Virtual HAL promotion families",
|
|
value: virtualHalPromotionFamilySummary,
|
|
},
|
|
...virtualHalPromotionFamilyRows.map((row) => ({
|
|
id: `virtual-hal-promotion-family-${row.id}`,
|
|
label: `Promotion family ${row.label}`,
|
|
value: row.value,
|
|
sourceFiles: row.sourceFiles,
|
|
})),
|
|
{
|
|
id: "virtual-hal-sim-config-macro-load-fixtures",
|
|
label: "Virtual HAL sim-config macro/load fixtures",
|
|
value: virtualHalSimConfigMacroLoadFixturesReady ? "ready" : "missing",
|
|
},
|
|
{
|
|
id: "virtual-hal-motion-controller-matrix",
|
|
label: "Virtual HAL motion controller matrix",
|
|
value: virtualHalMotionControllerMatrixReady ? "ready" : "missing",
|
|
},
|
|
{
|
|
id: "tool-db-process-proof",
|
|
label: "Tool DB process proof",
|
|
value: toolDbProcessProofReady ? "ready" : "missing",
|
|
},
|
|
{
|
|
id: "tool-db-process-proof-detail",
|
|
label: "Tool DB process proof detail",
|
|
value: `wasm=${toolDbProcessProofSummary.wasmProtocolReady ? "ready" : "missing"} browser=${toolDbProcessProofSummary.browserProtocolReady ? "ready" : "missing"} opfs=${toolDbProcessProofSummary.opfsPersistenceReady ? "ready" : "missing"} promotion_allowed=${toolDbProcessProofSummary.promotionAllowed ? "1" : "0"}`,
|
|
},
|
|
{
|
|
id: "python-remap-runtime-proof",
|
|
label: "Python remap runtime proof",
|
|
value: pythonRemapRuntimeProofReady ? "ready" : "locked",
|
|
},
|
|
{
|
|
id: "python-remap-runtime-proof-detail",
|
|
label: "Python remap runtime proof detail",
|
|
value: `native=${pythonRemapRuntimeProofSummary.nativeLifecycleReady ? "ready" : "missing"} wasm=${pythonRemapRuntimeProofSummary.wasmLifecycleReady ? "ready" : "missing"} browser=${pythonRemapRuntimeProofSummary.browserLifecycleReady ? "ready" : "missing"} promotion_allowed=${pythonRemapRuntimeProofSummary.promotionAllowed ? "1" : "0"}`,
|
|
},
|
|
{
|
|
id: "blocked-runtime-families",
|
|
label: "Blocked runtime families",
|
|
value: blockedRuntimeReady ? blockedFamilies.join(", ") : `promoted: ${promotedBlockedFamilies.join(", ")}`,
|
|
},
|
|
{
|
|
id: "hard-block-runtime-lock",
|
|
label: "Hard-block runtime lock",
|
|
value: hardBlockRuntimeLockSummary.lockValue,
|
|
},
|
|
{
|
|
id: "promoted-blocked-family-count",
|
|
label: "Promoted blocked families",
|
|
value: hardBlockRuntimeLockSummary.promotedCountValue,
|
|
},
|
|
{
|
|
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 virtualHalSimConfigSourceCoverageReady = report?.virtualHalSimConfigSourceCoverageReady === true;
|
|
const virtualHalSimConfigPromotionCandidatesReady = report?.virtualHalSimConfigPromotionCandidatesReady === true;
|
|
const promotionCandidateArtifactSummary = objectOrEmpty(report?.promotionCandidateArtifactSummary);
|
|
const virtualHalSimConfigMacroLoadFixturesReady = report?.virtualHalSimConfigMacroLoadFixturesReady === true;
|
|
const virtualHalMotionControllerMatrixReady = report?.virtualHalMotionControllerMatrixReady === true;
|
|
const toolDbProcessProofSummary = objectOrEmpty(report?.toolDbProcessProofSummary);
|
|
const toolDbProcessProofReady = report?.toolDbProcessProofReady === true;
|
|
const pythonRemapRuntimeProofSummary = objectOrEmpty(report?.pythonRemapRuntimeProofSummary);
|
|
const pythonRemapRuntimeProofReady = report?.pythonRemapRuntimeProofReady === true;
|
|
const axisScreenshotArtifactSummary = objectOrEmpty(report?.axisScreenshotArtifactSummary);
|
|
const blockedRuntimeFamilies = arrayOrEmpty(report?.blockedRuntimeFamilies);
|
|
const promotedBlockedFamilies = arrayOrEmpty(report?.promotedBlockedFamilies);
|
|
const hardBlockRuntimeLockSummary = createHardBlockRuntimeLockSummary(
|
|
blockedRuntimeFamilies,
|
|
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: "virtual-hal-sim-config-source-coverage",
|
|
label: "Virtual HAL sim-config source coverage",
|
|
value: virtualHalSimConfigSourceCoverageReady ? "ready" : "missing",
|
|
},
|
|
{
|
|
id: "virtual-hal-sim-config-promotion-candidates",
|
|
label: "Virtual HAL sim-config promotion candidates",
|
|
value: virtualHalSimConfigPromotionCandidatesReady ? "ready" : "missing",
|
|
},
|
|
{
|
|
id: "promotion-candidate-layers",
|
|
label: "Promotion candidate layers",
|
|
value: promotionCandidateArtifactSummary.ready === true
|
|
? `evidence-ready=${promotionCandidateArtifactSummary.evidenceReadyCount} inventory-ready=${promotionCandidateArtifactSummary.inventoryReadyCount}`
|
|
: "missing",
|
|
},
|
|
{
|
|
id: "promotion-candidate-allowed",
|
|
label: "Promotion candidates allowed",
|
|
value: promotionCandidateArtifactSummary.ready === true
|
|
? `${promotionCandidateArtifactSummary.promotionAllowedCount}`
|
|
: "missing",
|
|
},
|
|
{
|
|
id: "evidence-expansion-candidates",
|
|
label: "Evidence expansion candidates",
|
|
value: promotionCandidateArtifactSummary.ready === true
|
|
? `${promotionCandidateArtifactSummary.evidenceExpansionCandidateCount}`
|
|
: "missing",
|
|
},
|
|
{
|
|
id: "evidence-expansion-next-evidence",
|
|
label: "Evidence expansion next evidence",
|
|
value: promotionCandidateArtifactSummary.ready === true
|
|
? promotionCandidateArtifactSummary.evidenceExpansion.nextEvidence
|
|
: "missing",
|
|
},
|
|
{
|
|
id: "evidence-expansion-baseline-changing",
|
|
label: "Evidence expansion baseline changing",
|
|
value: promotionCandidateArtifactSummary.ready === true
|
|
? (promotionCandidateArtifactSummary.evidenceExpansion.baselineChanging ? "yes" : "no")
|
|
: "missing",
|
|
},
|
|
{
|
|
id: "virtual-hal-sim-config-macro-load-fixtures",
|
|
label: "Virtual HAL sim-config macro/load fixtures",
|
|
value: virtualHalSimConfigMacroLoadFixturesReady ? "ready" : "missing",
|
|
},
|
|
{
|
|
id: "virtual-hal-motion-controller-matrix",
|
|
label: "Virtual HAL motion controller matrix",
|
|
value: virtualHalMotionControllerMatrixReady ? "ready" : "missing",
|
|
},
|
|
{
|
|
id: "tool-db-process-proof",
|
|
label: "Tool DB process proof",
|
|
value: toolDbProcessProofReady ? "ready" : "missing",
|
|
},
|
|
{
|
|
id: "tool-db-process-proof-detail",
|
|
label: "Tool DB process proof detail",
|
|
value: toolDbProcessProofSummary.apiName
|
|
? `wasm=${toolDbProcessProofSummary.wasmProtocolReady ? "ready" : "missing"} browser=${toolDbProcessProofSummary.browserProtocolReady ? "ready" : "missing"} opfs=${toolDbProcessProofSummary.opfsPersistenceReady ? "ready" : "missing"} promotion_allowed=${toolDbProcessProofSummary.promotionAllowed ? "1" : "0"}`
|
|
: "missing",
|
|
},
|
|
{
|
|
id: "python-remap-runtime-proof",
|
|
label: "Python remap runtime proof",
|
|
value: pythonRemapRuntimeProofReady ? "ready" : "locked",
|
|
},
|
|
{
|
|
id: "python-remap-runtime-proof-detail",
|
|
label: "Python remap runtime proof detail",
|
|
value: pythonRemapRuntimeProofSummary.apiName
|
|
? `native=${pythonRemapRuntimeProofSummary.nativeLifecycleReady ? "ready" : "missing"} wasm=${pythonRemapRuntimeProofSummary.wasmLifecycleReady ? "ready" : "missing"} browser=${pythonRemapRuntimeProofSummary.browserLifecycleReady ? "ready" : "missing"} promotion_allowed=${pythonRemapRuntimeProofSummary.promotionAllowed ? "1" : "0"}`
|
|
: "missing",
|
|
},
|
|
{
|
|
id: "blocked-runtime-families",
|
|
label: "Blocked runtime families",
|
|
value: promotedBlockedFamilies.length > 0
|
|
? `promoted: ${promotedBlockedFamilies.join(", ")}`
|
|
: (blockedRuntimeFamilies.join(", ") || "none"),
|
|
},
|
|
{
|
|
id: "hard-block-runtime-lock",
|
|
label: "Hard-block runtime lock",
|
|
value: hardBlockRuntimeLockSummary.lockValue,
|
|
},
|
|
{
|
|
id: "promoted-blocked-family-count",
|
|
label: "Promoted blocked families",
|
|
value: hardBlockRuntimeLockSummary.promotedCountValue,
|
|
},
|
|
{
|
|
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: "virtual-hal-sim-config-source-coverage",
|
|
label: "Virtual HAL sim-config source coverage",
|
|
value: validation?.virtualHalSimConfigSourceCoverageReady === true ? "ready" : "missing",
|
|
},
|
|
{
|
|
id: "virtual-hal-sim-config-promotion-candidates",
|
|
label: "Virtual HAL sim-config promotion candidates",
|
|
value: validation?.virtualHalSimConfigPromotionCandidatesReady === true ? "ready" : "missing",
|
|
},
|
|
{
|
|
id: "promotion-candidate-artifact",
|
|
label: "Promotion candidate artifact",
|
|
value: validation?.promotionCandidateArtifactSummaryReady === true
|
|
? validation.promotionCandidateArtifactSummary.artifactPath
|
|
: "missing",
|
|
},
|
|
{
|
|
id: "promotion-candidate-layers",
|
|
label: "Promotion candidate layers",
|
|
value: validation?.promotionCandidateArtifactSummaryReady === true
|
|
? `evidence-ready=${validation.promotionCandidateArtifactSummary.evidenceReadyCount} inventory-ready=${validation.promotionCandidateArtifactSummary.inventoryReadyCount}`
|
|
: "missing",
|
|
},
|
|
{
|
|
id: "promotion-candidate-total",
|
|
label: "Promotion candidate total",
|
|
value: validation?.promotionCandidateArtifactSummaryReady === true
|
|
? `${validation.promotionCandidateArtifactSummary.totalCandidateCount}`
|
|
: "missing",
|
|
},
|
|
{
|
|
id: "promotion-candidate-artifact-rows",
|
|
label: "Promotion candidate artifact rows",
|
|
value: validation?.promotionCandidateArtifactSummaryReady === true
|
|
? `${validation.promotionCandidateArtifactSummary.artifactRows.promotionCandidates}`
|
|
: "missing",
|
|
},
|
|
{
|
|
id: "evidence-expansion-candidates",
|
|
label: "Evidence expansion candidates",
|
|
value: validation?.promotionCandidateArtifactSummaryReady === true
|
|
? `${validation.promotionCandidateArtifactSummary.evidenceExpansionCandidateCount}`
|
|
: "missing",
|
|
},
|
|
{
|
|
id: "evidence-expansion-artifact-rows",
|
|
label: "Evidence expansion artifact rows",
|
|
value: validation?.promotionCandidateArtifactSummaryReady === true
|
|
? `${validation.promotionCandidateArtifactSummary.artifactRows.evidenceExpansion}`
|
|
: "missing",
|
|
},
|
|
{
|
|
id: "evidence-expansion-next-evidence",
|
|
label: "Evidence expansion next evidence",
|
|
value: validation?.promotionCandidateArtifactSummaryReady === true
|
|
? validation.promotionCandidateArtifactSummary.evidenceExpansion.nextEvidence
|
|
: "missing",
|
|
},
|
|
{
|
|
id: "evidence-expansion-baseline-changing",
|
|
label: "Evidence expansion baseline changing",
|
|
value: validation?.promotionCandidateArtifactSummaryReady === true
|
|
? (validation.promotionCandidateArtifactSummary.evidenceExpansion.baselineChanging ? "yes" : "no")
|
|
: "missing",
|
|
},
|
|
{
|
|
id: "evidence-expansion-artifact",
|
|
label: "Evidence expansion artifact",
|
|
value: validation?.promotionCandidateArtifactSummaryReady === true
|
|
? validation.promotionCandidateArtifactSummary.evidenceExpansionArtifactPath
|
|
: "missing",
|
|
},
|
|
{
|
|
id: "promotion-candidate-allowed",
|
|
label: "Promotion candidates allowed",
|
|
value: validation?.promotionCandidateArtifactSummaryReady === true
|
|
? `${validation.promotionCandidateArtifactSummary.promotionAllowedCount}`
|
|
: "missing",
|
|
},
|
|
{
|
|
id: "virtual-hal-promotion-families",
|
|
label: "Virtual HAL promotion families",
|
|
value: validation?.virtualHalPromotionFamilySummary ?? "not provided",
|
|
},
|
|
...arrayOrEmpty(validation?.virtualHalPromotionFamilyRows).map((row) => ({
|
|
id: `virtual-hal-promotion-family-${row.id}`,
|
|
label: `Promotion family ${row.label}`,
|
|
value: row.value,
|
|
sourceFiles: row.sourceFiles,
|
|
})),
|
|
{
|
|
id: "virtual-hal-sim-config-macro-load-fixtures",
|
|
label: "Virtual HAL sim-config macro/load fixtures",
|
|
value: validation?.virtualHalSimConfigMacroLoadFixturesReady === true ? "ready" : "missing",
|
|
},
|
|
{
|
|
id: "virtual-hal-motion-controller-matrix",
|
|
label: "Virtual HAL motion controller matrix",
|
|
value: validation?.virtualHalMotionControllerMatrixReady === true ? "ready" : "missing",
|
|
},
|
|
{
|
|
id: "tool-db-process-proof",
|
|
label: "Tool DB process proof",
|
|
value: validation?.toolDbProcessProofReady === true ? "ready" : "missing",
|
|
},
|
|
{
|
|
id: "tool-db-process-proof-detail",
|
|
label: "Tool DB process proof detail",
|
|
value: validation?.toolDbProcessProofReady === true
|
|
? `wasm=${validation.toolDbProcessProofSummary.wasmProtocolReady ? "ready" : "missing"} browser=${validation.toolDbProcessProofSummary.browserProtocolReady ? "ready" : "missing"} opfs=${validation.toolDbProcessProofSummary.opfsPersistenceReady ? "ready" : "missing"} promotion_allowed=${validation.toolDbProcessProofSummary.promotionAllowed ? "1" : "0"}`
|
|
: "missing",
|
|
},
|
|
{
|
|
id: "python-remap-runtime-proof",
|
|
label: "Python remap runtime proof",
|
|
value: validation?.pythonRemapRuntimeProofSummary?.ready === true ? "ready" : "locked",
|
|
},
|
|
{
|
|
id: "python-remap-runtime-proof-detail",
|
|
label: "Python remap runtime proof detail",
|
|
value: validation?.pythonRemapRuntimeProofSummary?.apiName
|
|
? `native=${validation.pythonRemapRuntimeProofSummary.nativeLifecycleReady ? "ready" : "missing"} wasm=${validation.pythonRemapRuntimeProofSummary.wasmLifecycleReady ? "ready" : "missing"} browser=${validation.pythonRemapRuntimeProofSummary.browserLifecycleReady ? "ready" : "missing"} promotion_allowed=${validation.pythonRemapRuntimeProofSummary.promotionAllowed ? "1" : "0"}`
|
|
: "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,
|
|
artifact,
|
|
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 = "",
|
|
diagnosticsUrl = "",
|
|
requireSavedSessionDiagnostics = false,
|
|
fetchRef = globalThis.fetch,
|
|
} = {}) {
|
|
if (!artifactUrl || typeof fetchRef !== "function") {
|
|
const jsonWorkflow = createProjectReleaseReadinessArtifactJsonWorkflow({ artifactJson: "" });
|
|
const diagnosticsValidation = diagnosticsUrl
|
|
? createProjectReleaseBrowserDiagnosticsArtifactValidation({})
|
|
: null;
|
|
const missing = [
|
|
...(artifactUrl ? [] : ["artifact-url"]),
|
|
...(typeof fetchRef === "function" ? [] : ["fetch"]),
|
|
...jsonWorkflow.missing,
|
|
...(diagnosticsUrl ? ["diagnostics-fetch", ...diagnosticsValidation.missing.map((item) => `diagnostics.${item}`)] : []),
|
|
];
|
|
return {
|
|
apiName: "project-release-readiness-artifact-url-workflow",
|
|
workflowVersion: 1,
|
|
phase: "blocked",
|
|
ready: false,
|
|
artifactUrl,
|
|
diagnosticsUrl,
|
|
requireSavedSessionDiagnostics,
|
|
fetched: false,
|
|
diagnosticsFetched: false,
|
|
httpStatus: null,
|
|
diagnosticsHttpStatus: null,
|
|
fetchError: null,
|
|
diagnosticsFetchError: null,
|
|
jsonWorkflow,
|
|
validation: jsonWorkflow.validation,
|
|
diagnosticsValidation,
|
|
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,
|
|
diagnosticsUrl,
|
|
requireSavedSessionDiagnostics,
|
|
fetched: false,
|
|
diagnosticsFetched: false,
|
|
httpStatus,
|
|
diagnosticsHttpStatus: null,
|
|
fetchError,
|
|
diagnosticsFetchError: null,
|
|
jsonWorkflow,
|
|
validation: jsonWorkflow.validation,
|
|
diagnosticsValidation: diagnosticsUrl ? createProjectReleaseBrowserDiagnosticsArtifactValidation({}) : null,
|
|
summaryViewModel: jsonWorkflow.summaryViewModel,
|
|
actionPlan: jsonWorkflow.actionPlan,
|
|
missing: [
|
|
"artifact-fetch",
|
|
...jsonWorkflow.missing,
|
|
...(diagnosticsUrl ? ["diagnostics-fetch"] : []),
|
|
],
|
|
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 });
|
|
let diagnosticsFetched = false;
|
|
let diagnosticsHttpStatus = null;
|
|
let diagnosticsFetchError = null;
|
|
let diagnosticsValidation = null;
|
|
let diagnosticsArtifact = null;
|
|
const diagnosticsMissing = [];
|
|
if (diagnosticsUrl) {
|
|
try {
|
|
const diagnosticsResponse = await fetchRef(diagnosticsUrl);
|
|
diagnosticsHttpStatus = diagnosticsResponse?.status ?? null;
|
|
if (!diagnosticsResponse?.ok) {
|
|
diagnosticsFetchError = `diagnostics artifact URL fetch failed${diagnosticsHttpStatus ? `: ${diagnosticsHttpStatus}` : ""}`;
|
|
diagnosticsMissing.push("diagnostics-fetch");
|
|
diagnosticsValidation = createProjectReleaseBrowserDiagnosticsArtifactValidation({});
|
|
} else {
|
|
diagnosticsFetched = true;
|
|
diagnosticsArtifact = JSON.parse(await diagnosticsResponse.text());
|
|
diagnosticsValidation = createProjectReleaseBrowserDiagnosticsArtifactValidation(diagnosticsArtifact);
|
|
diagnosticsMissing.push(...diagnosticsValidation.missing.map((item) => `diagnostics.${item}`));
|
|
if (requireSavedSessionDiagnostics && diagnosticsValidation.hasSessionDiagnostics !== true) {
|
|
diagnosticsMissing.push("diagnostics.virtualHalSessionDiagnostics");
|
|
}
|
|
}
|
|
} catch (error) {
|
|
diagnosticsFetchError = error?.message ?? "diagnostics artifact URL fetch error";
|
|
diagnosticsValidation = createProjectReleaseBrowserDiagnosticsArtifactValidation({});
|
|
diagnosticsMissing.push("diagnostics-fetch");
|
|
}
|
|
}
|
|
const missing = [
|
|
...jsonWorkflow.missing,
|
|
...diagnosticsMissing,
|
|
];
|
|
const ready = jsonWorkflow.ready === true &&
|
|
(!diagnosticsUrl || (diagnosticsValidation?.ready === true && diagnosticsMissing.length === 0));
|
|
return {
|
|
apiName: "project-release-readiness-artifact-url-workflow",
|
|
workflowVersion: 1,
|
|
phase: ready ? "ready" : "blocked",
|
|
ready,
|
|
artifactUrl,
|
|
diagnosticsUrl,
|
|
requireSavedSessionDiagnostics,
|
|
fetched: true,
|
|
diagnosticsFetched,
|
|
httpStatus,
|
|
diagnosticsHttpStatus,
|
|
fetchError: null,
|
|
diagnosticsFetchError,
|
|
jsonWorkflow,
|
|
validation: jsonWorkflow.validation,
|
|
diagnosticsValidation,
|
|
diagnosticsArtifact,
|
|
summaryViewModel: jsonWorkflow.summaryViewModel,
|
|
actionPlan: jsonWorkflow.actionPlan,
|
|
missing,
|
|
rows: [
|
|
{
|
|
id: "fetch",
|
|
label: "Artifact URL fetch",
|
|
value: "ready",
|
|
},
|
|
{
|
|
id: "json-workflow",
|
|
label: "Artifact JSON workflow",
|
|
value: jsonWorkflow.ready === true ? "ready" : "blocked",
|
|
},
|
|
{
|
|
id: "diagnostics-workflow",
|
|
label: "Browser diagnostics workflow",
|
|
value: diagnosticsUrl
|
|
? (diagnosticsValidation?.ready === true ? "ready" : "blocked")
|
|
: "not requested",
|
|
},
|
|
{
|
|
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,
|
|
diagnosticsUrl,
|
|
requireSavedSessionDiagnostics,
|
|
fetched: false,
|
|
diagnosticsFetched: false,
|
|
httpStatus: null,
|
|
diagnosticsHttpStatus: null,
|
|
fetchError: error?.message ?? "artifact URL fetch error",
|
|
diagnosticsFetchError: null,
|
|
jsonWorkflow,
|
|
validation: jsonWorkflow.validation,
|
|
diagnosticsValidation: diagnosticsUrl ? createProjectReleaseBrowserDiagnosticsArtifactValidation({}) : null,
|
|
summaryViewModel: jsonWorkflow.summaryViewModel,
|
|
actionPlan: jsonWorkflow.actionPlan,
|
|
missing: [
|
|
"artifact-fetch",
|
|
...jsonWorkflow.missing,
|
|
...(diagnosticsUrl ? ["diagnostics-fetch"] : []),
|
|
],
|
|
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 promotionCandidateSummary = objectOrEmpty(workflow?.diagnosticsArtifact?.virtualHalPromotionCandidateSummary);
|
|
const promotionCandidateReportRows = arrayOrEmpty(
|
|
workflow?.diagnosticsArtifact?.virtualHalSimConfigPromotionCandidates?.rows,
|
|
);
|
|
const evidenceReadyCandidateRows = promotionCandidateReportRows.filter((row) =>
|
|
row?.complete === true &&
|
|
row?.currentNodeInventoryStatus === "PASS" &&
|
|
row?.blockedKind === "-" &&
|
|
row?.targetBrowserEvidence === "explicit-browser-diagnostics" &&
|
|
row?.currentMatrixBrowserStatus === "explicit-browser-diagnostics"
|
|
);
|
|
const preferredEvidenceReadyCandidate = evidenceReadyCandidateRows[0] ?? {};
|
|
const promotionCandidateSummaryReady = promotionCandidateSummary.ready === true;
|
|
const validationRows = arrayOrEmpty(workflow?.validation?.rows);
|
|
const hardBlockRuntimeLock = validationRows.find(({ id }) => id === "hard-block-runtime-lock")?.value;
|
|
const promotedBlockedFamilyCount = validationRows.find(({ id }) => id === "promoted-blocked-family-count")?.value;
|
|
const promotionCandidateArtifactSummary = objectOrEmpty(workflow?.validation?.promotionCandidateArtifactSummary);
|
|
const promotionCandidateArtifactSummaryReady =
|
|
workflow?.validation?.promotionCandidateArtifactSummaryReady === true;
|
|
const toolDbProcessProofSummary = objectOrEmpty(workflow?.validation?.toolDbProcessProofSummary);
|
|
const toolDbProcessProofReady = workflow?.validation?.toolDbProcessProofReady === true;
|
|
const pythonRemapRuntimeProofSummary = objectOrEmpty(workflow?.validation?.pythonRemapRuntimeProofSummary);
|
|
const pythonRemapRuntimeProofReady = workflow?.validation?.pythonRemapRuntimeProofReady === true;
|
|
const promotionArtifactRows = objectOrEmpty(promotionCandidateArtifactSummary.artifactRows);
|
|
const hardBlockRuntimeFamilyRows = arrayOrEmpty(promotionCandidateArtifactSummary.hardBlockRuntimeFamilyRows);
|
|
const hardBlockRuntimeFamilySummaryRows = arrayOrEmpty(
|
|
promotionCandidateArtifactSummary.hardBlockRuntimeFamilySummaryRows,
|
|
);
|
|
const hardBlockRuntimeFamilyDetailRows = arrayOrEmpty(
|
|
promotionCandidateArtifactSummary.hardBlockRuntimeFamilyDetailRows,
|
|
);
|
|
const hardBlockRuntimeDetailPreviewRows = hardBlockRuntimeFamilyDetailRows.slice(0, 5);
|
|
const hardBlockRuntimePreviewRows = hardBlockRuntimeFamilyRows.slice(0, 3);
|
|
const evidenceExpansionRows = arrayOrEmpty(
|
|
workflow?.diagnosticsArtifact?.virtualHalSimConfigEvidenceExpansion?.rows,
|
|
);
|
|
const preferredEvidenceExpansion = evidenceExpansionRows[0] ?? {};
|
|
const evidenceExpansionPreviewRows = evidenceExpansionRows.slice(0, 3);
|
|
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: "virtual-hal-promotion-families",
|
|
label: "Virtual HAL promotion families",
|
|
value: workflow?.validation?.virtualHalPromotionFamilySummary ?? "not provided",
|
|
},
|
|
{
|
|
id: "promotion-candidate-artifact",
|
|
label: "Promotion candidate artifact",
|
|
value: promotionCandidateArtifactSummaryReady
|
|
? promotionCandidateArtifactSummary.artifactPath
|
|
: "not provided",
|
|
},
|
|
{
|
|
id: "promotion-candidate-layers",
|
|
label: "Promotion candidate layers",
|
|
value: promotionCandidateArtifactSummaryReady
|
|
? `evidence-ready=${promotionCandidateArtifactSummary.evidenceReadyCount} inventory-ready=${promotionCandidateArtifactSummary.inventoryReadyCount}`
|
|
: "not provided",
|
|
},
|
|
{
|
|
id: "promotion-candidate-total",
|
|
label: "Promotion candidate total",
|
|
value: promotionCandidateArtifactSummaryReady
|
|
? `${promotionCandidateArtifactSummary.totalCandidateCount}`
|
|
: "not provided",
|
|
},
|
|
{
|
|
id: "evidence-ready-candidate-rows",
|
|
label: "Evidence-ready candidate rows",
|
|
value: `${evidenceReadyCandidateRows.length}`,
|
|
},
|
|
{
|
|
id: "evidence-ready-candidate-preview",
|
|
label: "Evidence-ready candidate preview",
|
|
value: preferredEvidenceReadyCandidate.id ?? "not provided",
|
|
},
|
|
{
|
|
id: "evidence-ready-candidate-preview-gcode",
|
|
label: "Evidence-ready candidate preview G-code",
|
|
value: preferredEvidenceReadyCandidate.gcodePath ?? "not provided",
|
|
},
|
|
{
|
|
id: "evidence-ready-candidate-promotion-allowed",
|
|
label: "Evidence-ready candidate promotion allowed",
|
|
value: "0",
|
|
},
|
|
{
|
|
id: "evidence-ready-candidate-baseline-changing",
|
|
label: "Evidence-ready candidate baseline changing",
|
|
value: "no",
|
|
},
|
|
{
|
|
id: "promotion-candidate-artifact-rows",
|
|
label: "Promotion candidate artifact rows",
|
|
value: promotionCandidateArtifactSummaryReady
|
|
? `${promotionArtifactRows.promotionCandidates ?? "not provided"}`
|
|
: "not provided",
|
|
},
|
|
{
|
|
id: "evidence-expansion-candidates",
|
|
label: "Evidence expansion candidates",
|
|
value: promotionCandidateArtifactSummaryReady
|
|
? `${promotionCandidateArtifactSummary.evidenceExpansionCandidateCount}`
|
|
: "not provided",
|
|
},
|
|
{
|
|
id: "evidence-expansion-artifact-rows",
|
|
label: "Evidence expansion artifact rows",
|
|
value: promotionCandidateArtifactSummaryReady
|
|
? `${promotionArtifactRows.evidenceExpansion ?? "not provided"}`
|
|
: "not provided",
|
|
},
|
|
{
|
|
id: "evidence-expansion-next-evidence",
|
|
label: "Evidence expansion next evidence",
|
|
value: promotionCandidateArtifactSummaryReady
|
|
? promotionCandidateArtifactSummary.evidenceExpansion.nextEvidence
|
|
: "not provided",
|
|
},
|
|
{
|
|
id: "evidence-expansion-baseline-changing",
|
|
label: "Evidence expansion baseline changing",
|
|
value: promotionCandidateArtifactSummaryReady
|
|
? (promotionCandidateArtifactSummary.evidenceExpansion.baselineChanging ? "yes" : "no")
|
|
: "not provided",
|
|
},
|
|
{
|
|
id: "evidence-expansion-preferred-candidate",
|
|
label: "Evidence expansion preferred candidate",
|
|
value: preferredEvidenceExpansion.id ?? "not provided",
|
|
},
|
|
{
|
|
id: "evidence-expansion-preferred-gcode",
|
|
label: "Evidence expansion preferred G-code",
|
|
value: preferredEvidenceExpansion.gcodePath ?? "not provided",
|
|
},
|
|
{
|
|
id: "evidence-expansion-preferred-source-count",
|
|
label: "Evidence expansion preferred source count",
|
|
value: Array.isArray(preferredEvidenceExpansion.sourceFiles)
|
|
? `${preferredEvidenceExpansion.sourceFiles.length}`
|
|
: "not provided",
|
|
},
|
|
{
|
|
id: "evidence-expansion-candidate-list",
|
|
label: "Evidence expansion candidate list",
|
|
value: evidenceExpansionPreviewRows.length > 0
|
|
? evidenceExpansionPreviewRows.map((row) => row.id).join(", ")
|
|
: "not provided",
|
|
},
|
|
{
|
|
id: "evidence-expansion-gcode-list",
|
|
label: "Evidence expansion G-code list",
|
|
value: evidenceExpansionPreviewRows.length > 0
|
|
? evidenceExpansionPreviewRows.map((row) => row.gcodePath).join(", ")
|
|
: "not provided",
|
|
},
|
|
{
|
|
id: "evidence-expansion-source-count-list",
|
|
label: "Evidence expansion source count list",
|
|
value: evidenceExpansionPreviewRows.length > 0
|
|
? evidenceExpansionPreviewRows.map((row) =>
|
|
Array.isArray(row.sourceFiles) ? `${row.sourceFiles.length}` : "0"
|
|
).join(", ")
|
|
: "not provided",
|
|
},
|
|
{
|
|
id: "evidence-expansion-artifact",
|
|
label: "Evidence expansion artifact",
|
|
value: promotionCandidateArtifactSummaryReady
|
|
? promotionCandidateArtifactSummary.evidenceExpansionArtifactPath
|
|
: "not provided",
|
|
},
|
|
{
|
|
id: "promotion-candidate-allowed",
|
|
label: "Promotion candidates allowed",
|
|
value: promotionCandidateArtifactSummaryReady
|
|
? `${promotionCandidateArtifactSummary.promotionAllowedCount}`
|
|
: "not provided",
|
|
},
|
|
{
|
|
id: "hard-block-runtime-locked-rows",
|
|
label: "Hard-block locked inventory rows",
|
|
value: promotionCandidateArtifactSummaryReady
|
|
? `${hardBlockRuntimeFamilyRows.length}`
|
|
: "not provided",
|
|
},
|
|
{
|
|
id: "hard-block-runtime-locked-preview",
|
|
label: "Hard-block locked row preview",
|
|
value: hardBlockRuntimePreviewRows.length > 0
|
|
? hardBlockRuntimePreviewRows.map((row) => `${row.skipKind}:${row.path}`).join(", ")
|
|
: "not provided",
|
|
},
|
|
{
|
|
id: "hard-block-runtime-promotion-allowed",
|
|
label: "Hard-block promotions allowed",
|
|
value: promotionCandidateArtifactSummaryReady
|
|
? `${promotionCandidateArtifactSummary.hardBlockRuntimePromotionAllowedCount ?? "not provided"}`
|
|
: "not provided",
|
|
},
|
|
{
|
|
id: "hard-block-runtime-family-count",
|
|
label: "Hard-block runtime families",
|
|
value: promotionCandidateArtifactSummaryReady
|
|
? `${hardBlockRuntimeFamilySummaryRows.length}`
|
|
: "not provided",
|
|
},
|
|
{
|
|
id: "hard-block-runtime-family-summary",
|
|
label: "Hard-block runtime family summary",
|
|
value: hardBlockRuntimeFamilySummaryRows.length > 0
|
|
? hardBlockRuntimeFamilySummaryRows
|
|
.map((row) => `${row.family}:${row.candidateCount} locked=${row.locked ? "yes" : "no"}`)
|
|
.join(", ")
|
|
: "not provided",
|
|
},
|
|
{
|
|
id: "hard-block-runtime-detail-row-count",
|
|
label: "Hard-block detail rows",
|
|
value: promotionCandidateArtifactSummaryReady
|
|
? `${hardBlockRuntimeFamilyDetailRows.length}`
|
|
: "not provided",
|
|
},
|
|
...hardBlockRuntimeDetailPreviewRows.map((row, index) => ({
|
|
id: `hard-block-runtime-detail-preview-${index + 1}`,
|
|
label: `Hard-block detail ${index + 1}`,
|
|
value: `${row.family}:${row.path}; reason=${row.blockReason}; next=${row.nextProof}; promotion_allowed=${row.promotionAllowed ? "1" : "0"}`,
|
|
family: row.family,
|
|
path: row.path,
|
|
blockReason: row.blockReason,
|
|
nextProof: row.nextProof,
|
|
promotionAllowed: row.promotionAllowed,
|
|
})),
|
|
...hardBlockRuntimeFamilySummaryRows.map((row) => ({
|
|
id: `hard-block-runtime-family-${row.family}`,
|
|
label: `Hard-block family ${row.family}`,
|
|
value: [
|
|
`${row.candidateCount} locked rows`,
|
|
`promotion_allowed=${row.promotionAllowedCount}`,
|
|
`first=${row.firstPath ?? "not provided"}`,
|
|
`reason=${row.firstBlockReason ?? "not provided"}`,
|
|
`next=${row.nextProof ?? "not provided"}`,
|
|
].join("; "),
|
|
firstPath: row.firstPath,
|
|
firstBlockReason: row.firstBlockReason,
|
|
nextProof: row.nextProof,
|
|
})),
|
|
...hardBlockRuntimeFamilySummaryRows.map((row) => ({
|
|
id: `hard-block-runtime-family-${row.family}-paths`,
|
|
label: `Hard-block family ${row.family} paths`,
|
|
value: arrayOrEmpty(row.lockedPaths).length > 0
|
|
? row.lockedPaths.join(", ")
|
|
: "not provided",
|
|
lockedPaths: arrayOrEmpty(row.lockedPaths),
|
|
})),
|
|
{
|
|
id: "tool-db-process-proof",
|
|
label: "Tool DB process proof",
|
|
value: toolDbProcessProofReady ? "ready" : "not provided",
|
|
},
|
|
{
|
|
id: "tool-db-process-proof-detail",
|
|
label: "Tool DB process proof detail",
|
|
value: toolDbProcessProofReady
|
|
? `wasm=${toolDbProcessProofSummary.wasmProtocolReady ? "ready" : "missing"} browser=${toolDbProcessProofSummary.browserProtocolReady ? "ready" : "missing"} opfs=${toolDbProcessProofSummary.opfsPersistenceReady ? "ready" : "missing"} promotion_allowed=${toolDbProcessProofSummary.promotionAllowed ? "1" : "0"}`
|
|
: "not provided",
|
|
},
|
|
{
|
|
id: "python-remap-runtime-proof",
|
|
label: "Python remap runtime proof",
|
|
value: pythonRemapRuntimeProofSummary.ready === true ? "ready" : "locked",
|
|
},
|
|
{
|
|
id: "python-remap-runtime-proof-detail",
|
|
label: "Python remap runtime proof detail",
|
|
value: pythonRemapRuntimeProofSummary.apiName
|
|
? `native=${pythonRemapRuntimeProofSummary.nativeLifecycleReady ? "ready" : "missing"} wasm=${pythonRemapRuntimeProofSummary.wasmLifecycleReady ? "ready" : "missing"} browser=${pythonRemapRuntimeProofSummary.browserLifecycleReady ? "ready" : "missing"} promotion_allowed=${pythonRemapRuntimeProofSummary.promotionAllowed ? "1" : "0"}`
|
|
: "not provided",
|
|
},
|
|
...arrayOrEmpty(workflow?.validation?.virtualHalPromotionFamilyRows).map((row) => ({
|
|
id: `virtual-hal-promotion-family-${row.id}`,
|
|
label: `Promotion family ${row.label}`,
|
|
value: row.value,
|
|
sourceFiles: row.sourceFiles,
|
|
})),
|
|
{
|
|
id: "browser-diagnostics",
|
|
label: "Browser diagnostics",
|
|
value: workflow?.diagnosticsUrl
|
|
? (workflow?.diagnosticsValidation?.ready === true ? "ready" : "blocked")
|
|
: "not requested",
|
|
},
|
|
{
|
|
id: "virtual-hal-promotion-candidate-summary",
|
|
label: "Virtual HAL promotion candidate summary",
|
|
value: workflow?.diagnosticsUrl
|
|
? (promotionCandidateSummaryReady ? "ready" : "not provided")
|
|
: "not requested",
|
|
},
|
|
{
|
|
id: "virtual-hal-promotion-candidate",
|
|
label: "Virtual HAL promotion candidate",
|
|
value: promotionCandidateSummary.preferredCandidateId ?? "not provided",
|
|
},
|
|
{
|
|
id: "virtual-hal-promotion-candidate-ini",
|
|
label: "Candidate INI",
|
|
value: promotionCandidateSummary.preferredIniPath ?? "not provided",
|
|
},
|
|
{
|
|
id: "virtual-hal-promotion-candidate-gcode",
|
|
label: "Candidate G-code",
|
|
value: promotionCandidateSummary.preferredGcodePath ?? "not provided",
|
|
},
|
|
{
|
|
id: "virtual-hal-promotion-candidate-count",
|
|
label: "Candidate readiness",
|
|
value: Number.isFinite(promotionCandidateSummary.readyCandidateCount) &&
|
|
Number.isFinite(promotionCandidateSummary.candidateCount)
|
|
? `${promotionCandidateSummary.readyCandidateCount}/${promotionCandidateSummary.candidateCount} ready`
|
|
: "not provided",
|
|
},
|
|
{
|
|
id: "virtual-hal-promotion-family-count",
|
|
label: "Promotion family count",
|
|
value: Number.isFinite(promotionCandidateSummary.familyCount)
|
|
? `${promotionCandidateSummary.familyCount} families`
|
|
: "not provided",
|
|
},
|
|
{
|
|
id: "virtual-hal-promotion-source-file-count",
|
|
label: "Promotion source files",
|
|
value: Number.isFinite(promotionCandidateSummary.sourceFileCount)
|
|
? `${promotionCandidateSummary.sourceFileCount} source files`
|
|
: "not provided",
|
|
},
|
|
{
|
|
id: "virtual-hal-promotion-browser-diagnostics-count",
|
|
label: "Promotion browser diagnostics",
|
|
value: Number.isFinite(promotionCandidateSummary.explicitBrowserDiagnosticsCount)
|
|
? `${promotionCandidateSummary.explicitBrowserDiagnosticsCount} diagnostics-ready`
|
|
: "not provided",
|
|
},
|
|
{
|
|
id: "virtual-hal-promotion-candidate-baseline",
|
|
label: "Candidate inventory baseline",
|
|
value: promotionCandidateSummary.inventoryBaseline ?? "not provided",
|
|
},
|
|
{
|
|
id: "hard-block-runtime-lock",
|
|
label: "Hard-block runtime lock",
|
|
value: hardBlockRuntimeLock ?? "not provided",
|
|
},
|
|
{
|
|
id: "promoted-blocked-family-count",
|
|
label: "Promoted blocked families",
|
|
value: promotedBlockedFamilyCount ?? "not provided",
|
|
},
|
|
{
|
|
id: "saved-session-diagnostics",
|
|
label: "Saved-session diagnostics",
|
|
value: workflow?.diagnosticsUrl
|
|
? (workflow?.diagnosticsValidation?.hasSessionDiagnostics
|
|
? (workflow?.diagnosticsValidation?.sessionDiagnosticsReady === true ? "ready" : "blocked")
|
|
: "not provided")
|
|
: "not requested",
|
|
},
|
|
{
|
|
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,
|
|
}]
|
|
: []),
|
|
...(missing.includes("diagnostics-fetch")
|
|
? [{
|
|
id: "verify-diagnostics-url-fetch",
|
|
label: "Verify browser diagnostics 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 virtualHalSimConfigSourceCoverage = objectOrEmpty(artifactObject.virtualHalSimConfigSourceCoverage);
|
|
const virtualHalSimConfigPromotionCandidates = objectOrEmpty(artifactObject.virtualHalSimConfigPromotionCandidates);
|
|
const virtualHalSimConfigMacroLoadFixtures = objectOrEmpty(artifactObject.virtualHalSimConfigMacroLoadFixtures);
|
|
const virtualHalMotionControllerMatrix = objectOrEmpty(artifactObject.virtualHalMotionControllerMatrix);
|
|
const toolDbProcessProofSummary = objectOrEmpty(artifactObject.toolDbProcessProofSummary);
|
|
const pythonRemapRuntimeProofSummary = objectOrEmpty(artifactObject.pythonRemapRuntimeProofSummary);
|
|
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 virtualHalSimConfigSourceCoverageReady = isVirtualHalSimConfigSourceCoverageReady(
|
|
virtualHalSimConfigSourceCoverage,
|
|
) && artifactObject.virtualHalSimConfigSourceCoverageReady === true;
|
|
const virtualHalSimConfigPromotionCandidatesReady = isVirtualHalSimConfigPromotionCandidateReportReady(
|
|
virtualHalSimConfigPromotionCandidates,
|
|
) && artifactObject.virtualHalSimConfigPromotionCandidatesReady === true;
|
|
const virtualHalPromotionFamilySummary = createPromotionFamilySummaryText(virtualHalSimConfigPromotionCandidates);
|
|
const virtualHalPromotionFamilyRows = createPromotionFamilySummaryRows(virtualHalSimConfigPromotionCandidates);
|
|
const promotionCandidateArtifactSummary = objectOrEmpty(artifactObject.promotionCandidateArtifactSummary);
|
|
const expectedPromotionCandidateArtifactSummary = createPromotionCandidateArtifactSummary({
|
|
candidateReport: virtualHalSimConfigPromotionCandidates,
|
|
evidenceExpansionReport: artifactObject.virtualHalSimConfigEvidenceExpansion,
|
|
inventory: simConfigInventory,
|
|
blockedFamilies: arrayOrEmpty(artifactObject.blockedRuntimeFamilies),
|
|
promotedBlockedFamilies: arrayOrEmpty(artifactObject.promotedBlockedFamilies),
|
|
});
|
|
const artifactRows = objectOrEmpty(promotionCandidateArtifactSummary.artifactRows);
|
|
const promotionCandidateArtifactSummaryReady =
|
|
promotionCandidateArtifactSummary.apiName === expectedPromotionCandidateArtifactSummary.apiName &&
|
|
promotionCandidateArtifactSummary.summaryVersion === 1 &&
|
|
promotionCandidateArtifactSummary.ready === true &&
|
|
promotionCandidateArtifactSummary.artifactPath === expectedPromotionCandidateArtifactSummary.artifactPath &&
|
|
promotionCandidateArtifactSummary.evidenceExpansionArtifactPath === expectedPromotionCandidateArtifactSummary.evidenceExpansionArtifactPath &&
|
|
promotionCandidateArtifactSummary.layerCount === 2 &&
|
|
promotionCandidateArtifactSummary.evidenceReadyCount === expectedPromotionCandidateArtifactSummary.evidenceReadyCount &&
|
|
promotionCandidateArtifactSummary.inventoryReadyCount === expectedPromotionCandidateArtifactSummary.inventoryReadyCount &&
|
|
promotionCandidateArtifactSummary.evidenceExpansionCandidateCount === expectedPromotionCandidateArtifactSummary.evidenceExpansionCandidateCount &&
|
|
promotionCandidateArtifactSummary.totalCandidateCount === expectedPromotionCandidateArtifactSummary.totalCandidateCount &&
|
|
promotionCandidateArtifactSummary.promotionAllowedCount === 0 &&
|
|
promotionCandidateArtifactSummary.hardBlockRuntimeLock === expectedPromotionCandidateArtifactSummary.hardBlockRuntimeLock &&
|
|
promotionCandidateArtifactSummary.hardBlockRuntimePromotionAllowedCount === 0 &&
|
|
Number.isFinite(promotionCandidateArtifactSummary.hardBlockRuntimePromotionAllowedCount) &&
|
|
promotionCandidateArtifactSummary.inventoryBaseline === expectedPromotionCandidateArtifactSummary.inventoryBaseline &&
|
|
artifactRows.promotionCandidates ===
|
|
promotionCandidateArtifactSummary.evidenceReadyCount + promotionCandidateArtifactSummary.inventoryReadyCount &&
|
|
(
|
|
artifactRows.evidenceExpansion === 0 ||
|
|
artifactRows.evidenceExpansion === promotionCandidateArtifactSummary.evidenceExpansionCandidateCount
|
|
) &&
|
|
arrayOrEmpty(promotionCandidateArtifactSummary.layers).some((row) =>
|
|
row.id === "evidence-ready" &&
|
|
row.candidateCount === expectedPromotionCandidateArtifactSummary.evidenceReadyCount &&
|
|
row.virtualHalEvidenceReady === true &&
|
|
row.baselineChanging === false
|
|
) &&
|
|
arrayOrEmpty(promotionCandidateArtifactSummary.layers).some((row) =>
|
|
row.id === "inventory-ready" &&
|
|
row.candidateCount === expectedPromotionCandidateArtifactSummary.inventoryReadyCount &&
|
|
row.promotionAllowedCount === 0 &&
|
|
row.virtualHalEvidenceReady === false &&
|
|
row.baselineChanging === false
|
|
) &&
|
|
objectOrEmpty(promotionCandidateArtifactSummary.evidenceExpansion).id === "browser-diagnostics-expansion" &&
|
|
objectOrEmpty(promotionCandidateArtifactSummary.evidenceExpansion).candidateCount ===
|
|
expectedPromotionCandidateArtifactSummary.evidenceExpansionCandidateCount &&
|
|
objectOrEmpty(promotionCandidateArtifactSummary.evidenceExpansion).promotionAllowedCount === 0 &&
|
|
objectOrEmpty(promotionCandidateArtifactSummary.evidenceExpansion).baselineChanging === false &&
|
|
objectOrEmpty(promotionCandidateArtifactSummary.evidenceExpansion).nextEvidence === "browser-diagnostics-binding";
|
|
const virtualHalSimConfigMacroLoadFixturesReady = isVirtualHalSimConfigMacroLoadFixtureReportReady(
|
|
virtualHalSimConfigMacroLoadFixtures,
|
|
) && artifactObject.virtualHalSimConfigMacroLoadFixturesReady === true;
|
|
const virtualHalMotionControllerMatrixReady = isVirtualHalMotionControllerMatrixReady(
|
|
virtualHalMotionControllerMatrix,
|
|
) && artifactObject.virtualHalMotionControllerMatrixReady === true;
|
|
const expectedToolDbProcessProofSummary = createToolDbProcessProofSummary(toolDbProcessProofSummary);
|
|
const toolDbProcessProofReady =
|
|
toolDbProcessProofSummary.apiName === "tool-db-process-proof-summary" &&
|
|
toolDbProcessProofSummary.summaryVersion === 1 &&
|
|
toolDbProcessProofSummary.ready === true &&
|
|
toolDbProcessProofSummary.boundaryClass === "L4-TOOL-DB" &&
|
|
toolDbProcessProofSummary.path === "axis/db_demo/base.ngc" &&
|
|
toolDbProcessProofSummary.dbProgramPath === "./db_nonran.py" &&
|
|
toolDbProcessProofSummary.wasmProtocolReady === true &&
|
|
toolDbProcessProofSummary.browserProtocolReady === true &&
|
|
toolDbProcessProofSummary.opfsPersistenceReady === true &&
|
|
toolDbProcessProofSummary.tblFallbackSufficient === false &&
|
|
toolDbProcessProofSummary.promotionAllowed === false &&
|
|
toolDbProcessProofSummary.executionEnabled === false &&
|
|
arrayOrEmpty(toolDbProcessProofSummary.requiredCommands).join("\n") ===
|
|
expectedToolDbProcessProofSummary.requiredCommands.join("\n") &&
|
|
artifactObject.toolDbProcessProofReady === true;
|
|
const expectedPythonRemapRuntimeProofSummary =
|
|
createPythonRemapRuntimeProofSummary(pythonRemapRuntimeProofSummary);
|
|
const pythonRemapRuntimeProofReady =
|
|
pythonRemapRuntimeProofSummary.apiName === "python-remap-runtime-proof-summary" &&
|
|
pythonRemapRuntimeProofSummary.summaryVersion === 1 &&
|
|
pythonRemapRuntimeProofSummary.boundaryClass === "L4-PYTHON-REMAP" &&
|
|
pythonRemapRuntimeProofSummary.fixtureFamily === "axis/remap/stop-lookahead/nc_files" &&
|
|
pythonRemapRuntimeProofSummary.fixtureId === "stop_lookahead_python_runtime_lifecycle" &&
|
|
pythonRemapRuntimeProofSummary.iniPath === "axis/remap/stop-lookahead/demo.ini" &&
|
|
pythonRemapRuntimeProofSummary.pythonPathPrepend === "python" &&
|
|
pythonRemapRuntimeProofSummary.topLevelPath === "python/toplevel.py" &&
|
|
pythonRemapRuntimeProofSummary.callableName === "queuebuster" &&
|
|
pythonRemapRuntimeProofSummary.nativeProbeStatus === (
|
|
pythonRemapRuntimeProofSummary.nativeLifecycleReady
|
|
? "runtime_lifecycle_probe_passed"
|
|
: "not_provided"
|
|
) &&
|
|
pythonRemapRuntimeProofSummary.nativeProofArtifactPath === "wasm-port/build/native/native-runtime-probe-summary.tsv" &&
|
|
pythonRemapRuntimeProofSummary.promotionAllowed === false &&
|
|
pythonRemapRuntimeProofSummary.executionEnabled === false &&
|
|
pythonRemapRuntimeProofSummary.bulkPromotionAllowed === false &&
|
|
pythonRemapRuntimeProofSummary.manualLockUpdateRequired === true &&
|
|
arrayOrEmpty(pythonRemapRuntimeProofSummary.requiredCommands).join("\n") ===
|
|
expectedPythonRemapRuntimeProofSummary.requiredCommands.join("\n") &&
|
|
artifactObject.pythonRemapRuntimeProofReady === pythonRemapRuntimeProofSummary.ready;
|
|
const axisScreenshotArtifacts = arrayOrEmpty(axisScreenshotArtifactSummary.artifacts);
|
|
const blockedRuntimeFamilies = arrayOrEmpty(artifactObject.blockedRuntimeFamilies);
|
|
const promotedBlockedFamilies = arrayOrEmpty(artifactObject.promotedBlockedFamilies);
|
|
const hardBlockRuntimeLockSummary = createHardBlockRuntimeLockSummary(
|
|
blockedRuntimeFamilies,
|
|
promotedBlockedFamilies,
|
|
);
|
|
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 === DEFAULT_SIM_CONFIG_INVENTORY_BASELINE.executed ? [] : ["simConfigInventory.executed"]),
|
|
...(simConfigInventory.passed === DEFAULT_SIM_CONFIG_INVENTORY_BASELINE.passed ? [] : ["simConfigInventory.passed"]),
|
|
...(simConfigInventory.skipped === DEFAULT_SIM_CONFIG_INVENTORY_BASELINE.skipped ? [] : ["simConfigInventory.skipped"]),
|
|
...(simConfigInventory.unexpectedFail === DEFAULT_SIM_CONFIG_INVENTORY_BASELINE.unexpectedFail ? [] : ["simConfigInventory.unexpectedFail"]),
|
|
...(virtualHalSimConfigSourceCoverageReady ? [] : ["virtualHalSimConfigSourceCoverage"]),
|
|
...(virtualHalSimConfigPromotionCandidatesReady ? [] : ["virtualHalSimConfigPromotionCandidates"]),
|
|
...(promotionCandidateArtifactSummaryReady ? [] : ["promotionCandidateArtifactSummary"]),
|
|
...(virtualHalSimConfigMacroLoadFixturesReady ? [] : ["virtualHalSimConfigMacroLoadFixtures"]),
|
|
...(virtualHalMotionControllerMatrixReady ? [] : ["virtualHalMotionControllerMatrix"]),
|
|
...(toolDbProcessProofReady ? [] : ["toolDbProcessProofSummary"]),
|
|
...(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,
|
|
virtualHalSimConfigSourceCoverageReady,
|
|
virtualHalSimConfigPromotionCandidatesReady,
|
|
virtualHalPromotionFamilySummary,
|
|
virtualHalPromotionFamilyRows,
|
|
promotionCandidateArtifactSummary,
|
|
promotionCandidateArtifactSummaryReady,
|
|
virtualHalSimConfigMacroLoadFixturesReady,
|
|
virtualHalMotionControllerMatrixReady,
|
|
toolDbProcessProofSummary,
|
|
toolDbProcessProofReady,
|
|
pythonRemapRuntimeProofSummary,
|
|
pythonRemapRuntimeProofReady,
|
|
axisScreenshotArtifactSummaryReady: axisScreenshotSummaryReady,
|
|
axisScreenshotArtifactCount: axisScreenshotArtifacts.length,
|
|
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: "virtual-hal-sim-config-source-coverage",
|
|
label: "Virtual HAL sim-config source coverage",
|
|
value: virtualHalSimConfigSourceCoverageReady ? "ready" : "missing",
|
|
},
|
|
{
|
|
id: "virtual-hal-sim-config-promotion-candidates",
|
|
label: "Virtual HAL sim-config promotion candidates",
|
|
value: virtualHalSimConfigPromotionCandidatesReady ? "ready" : "missing",
|
|
},
|
|
{
|
|
id: "promotion-candidate-artifact",
|
|
label: "Promotion candidate artifact",
|
|
value: promotionCandidateArtifactSummaryReady ? promotionCandidateArtifactSummary.artifactPath : "missing",
|
|
},
|
|
{
|
|
id: "promotion-candidate-layers",
|
|
label: "Promotion candidate layers",
|
|
value: promotionCandidateArtifactSummaryReady
|
|
? `evidence-ready=${promotionCandidateArtifactSummary.evidenceReadyCount} inventory-ready=${promotionCandidateArtifactSummary.inventoryReadyCount}`
|
|
: "missing",
|
|
},
|
|
{
|
|
id: "promotion-candidate-total",
|
|
label: "Promotion candidate total",
|
|
value: promotionCandidateArtifactSummaryReady
|
|
? `${promotionCandidateArtifactSummary.totalCandidateCount}`
|
|
: "missing",
|
|
},
|
|
{
|
|
id: "promotion-candidate-artifact-rows",
|
|
label: "Promotion candidate artifact rows",
|
|
value: promotionCandidateArtifactSummaryReady
|
|
? `${artifactRows.promotionCandidates}`
|
|
: "missing",
|
|
},
|
|
{
|
|
id: "evidence-expansion-candidates",
|
|
label: "Evidence expansion candidates",
|
|
value: promotionCandidateArtifactSummaryReady
|
|
? `${promotionCandidateArtifactSummary.evidenceExpansionCandidateCount}`
|
|
: "missing",
|
|
},
|
|
{
|
|
id: "evidence-expansion-artifact-rows",
|
|
label: "Evidence expansion artifact rows",
|
|
value: promotionCandidateArtifactSummaryReady
|
|
? `${artifactRows.evidenceExpansion}`
|
|
: "missing",
|
|
},
|
|
{
|
|
id: "evidence-expansion-next-evidence",
|
|
label: "Evidence expansion next evidence",
|
|
value: promotionCandidateArtifactSummaryReady
|
|
? promotionCandidateArtifactSummary.evidenceExpansion.nextEvidence
|
|
: "missing",
|
|
},
|
|
{
|
|
id: "evidence-expansion-baseline-changing",
|
|
label: "Evidence expansion baseline changing",
|
|
value: promotionCandidateArtifactSummaryReady
|
|
? (promotionCandidateArtifactSummary.evidenceExpansion.baselineChanging ? "yes" : "no")
|
|
: "missing",
|
|
},
|
|
{
|
|
id: "evidence-expansion-artifact",
|
|
label: "Evidence expansion artifact",
|
|
value: promotionCandidateArtifactSummaryReady
|
|
? promotionCandidateArtifactSummary.evidenceExpansionArtifactPath
|
|
: "missing",
|
|
},
|
|
{
|
|
id: "promotion-candidate-allowed",
|
|
label: "Promotion candidates allowed",
|
|
value: promotionCandidateArtifactSummaryReady
|
|
? `${promotionCandidateArtifactSummary.promotionAllowedCount}`
|
|
: "missing",
|
|
},
|
|
{
|
|
id: "virtual-hal-promotion-families",
|
|
label: "Virtual HAL promotion families",
|
|
value: virtualHalPromotionFamilySummary,
|
|
},
|
|
...virtualHalPromotionFamilyRows.map((row) => ({
|
|
id: `virtual-hal-promotion-family-${row.id}`,
|
|
label: `Promotion family ${row.label}`,
|
|
value: row.value,
|
|
sourceFiles: row.sourceFiles,
|
|
})),
|
|
{
|
|
id: "virtual-hal-sim-config-macro-load-fixtures",
|
|
label: "Virtual HAL sim-config macro/load fixtures",
|
|
value: virtualHalSimConfigMacroLoadFixturesReady ? "ready" : "missing",
|
|
},
|
|
{
|
|
id: "virtual-hal-motion-controller-matrix",
|
|
label: "Virtual HAL motion controller matrix",
|
|
value: virtualHalMotionControllerMatrixReady ? "ready" : "missing",
|
|
},
|
|
{
|
|
id: "tool-db-process-proof",
|
|
label: "Tool DB process proof",
|
|
value: toolDbProcessProofReady ? "ready" : "missing",
|
|
},
|
|
{
|
|
id: "tool-db-process-proof-detail",
|
|
label: "Tool DB process proof detail",
|
|
value: toolDbProcessProofReady
|
|
? `wasm=${toolDbProcessProofSummary.wasmProtocolReady ? "ready" : "missing"} browser=${toolDbProcessProofSummary.browserProtocolReady ? "ready" : "missing"} opfs=${toolDbProcessProofSummary.opfsPersistenceReady ? "ready" : "missing"} promotion_allowed=${toolDbProcessProofSummary.promotionAllowed ? "1" : "0"}`
|
|
: "missing",
|
|
},
|
|
{
|
|
id: "python-remap-runtime-proof",
|
|
label: "Python remap runtime proof",
|
|
value: pythonRemapRuntimeProofSummary.ready === true ? "ready" : "locked",
|
|
},
|
|
{
|
|
id: "python-remap-runtime-proof-detail",
|
|
label: "Python remap runtime proof detail",
|
|
value: pythonRemapRuntimeProofSummary.apiName
|
|
? `native=${pythonRemapRuntimeProofSummary.nativeLifecycleReady ? "ready" : "missing"} wasm=${pythonRemapRuntimeProofSummary.wasmLifecycleReady ? "ready" : "missing"} browser=${pythonRemapRuntimeProofSummary.browserLifecycleReady ? "ready" : "missing"} promotion_allowed=${pythonRemapRuntimeProofSummary.promotionAllowed ? "1" : "0"}`
|
|
: "missing",
|
|
},
|
|
{
|
|
id: "blocked-runtime-families",
|
|
label: "Blocked runtime families",
|
|
value: promotedBlockedFamilies.length > 0
|
|
? `promoted: ${promotedBlockedFamilies.join(", ")}`
|
|
: (blockedRuntimeFamilies.join(", ") || "none"),
|
|
},
|
|
{
|
|
id: "hard-block-runtime-lock",
|
|
label: "Hard-block runtime lock",
|
|
value: hardBlockRuntimeLockSummary.lockValue,
|
|
},
|
|
{
|
|
id: "promoted-blocked-family-count",
|
|
label: "Promoted blocked families",
|
|
value: hardBlockRuntimeLockSummary.promotedCountValue,
|
|
},
|
|
{
|
|
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(", "),
|
|
},
|
|
],
|
|
};
|
|
}
|