新增 release gate execution manifest API

This commit is contained in:
2026-06-16 17:20:21 +08:00
parent 2f6e8480bf
commit 741afc5103
13 changed files with 443 additions and 21 deletions

View File

@@ -19,8 +19,8 @@ Use these documents as the current handoff set:
reuse and non-drift rules.
- `../PROJECT_COMPLETION_TRACKER.md` for project-level acceptance tracking.
Old turn logs `text1.txt` through `text13.txt` are historical context. Current
continuation records are in `../text14.txt`.
Old turn logs `text1.txt` through `text14.txt` are historical context. Current
continuation records are in `../text15.txt`.
## Supported current workflows
@@ -39,10 +39,13 @@ continuation records are in `../text14.txt`.
expected smoke outputs, `createProjectReleaseGateResultMatrix()` to map
observed outputs onto passed/unknown gate rows,
`createProjectReleaseGateActionPlan()` to derive pending gate commands, the
next command, display rows, and a shell script from that matrix, and
next command, display rows, and a shell script from that matrix,
`createProjectReleaseGateExecutionManifest()` to package each gate command,
expected output, observed-output evidence, and passed/unknown status, and
`createProjectReleaseReadinessReport()` for a machine-readable release
readiness report covering the same manifest, result matrix, action plan,
sim-config inventory baseline, and blocked runtime families.
readiness report covering the same manifest, execution manifest, result
matrix, action plan, sim-config inventory baseline, and blocked runtime
families.
`createProjectReleaseReadinessSummaryViewModel()` turns that report into
stable status text and rows for dashboards. The report helper is evidence
driven and only reports `ready: true` when the caller supplies release gate
@@ -52,8 +55,9 @@ continuation records are in `../text14.txt`.
External tools can load that artifact with
`parseProjectReleaseReadinessArtifactJson()` and validate it with
`createProjectReleaseReadinessArtifactValidation()`. Artifact validation
requires the embedded gate manifest, result matrix, and action plan to be
ready, so stale release JSON cannot pass the executable artifact gate.
requires the embedded gate manifest, execution manifest, result matrix, and
action plan to be ready, so stale release JSON cannot pass the executable
artifact gate.
`createProjectReleaseReadinessArtifactValidationSummaryViewModel()` turns the
validation into stable dashboard rows without reinterpreting artifact fields.
`createProjectReleaseReadinessArtifactValidationActionPlan()` turns a blocked

View File

@@ -54,6 +54,7 @@ import {
createMachineSessionPersistenceSummary,
createMachineSessionSnapshotPayload,
createProjectReleaseGateActionPlan,
createProjectReleaseGateExecutionManifest,
createProjectReleaseGateManifest,
createProjectReleaseGateResultMatrix,
createProjectReleaseReadinessArtifactJsonWorkflow,
@@ -208,12 +209,17 @@ and explicit gate results onto that manifest as stable `passed`/`unknown` rows.
`createProjectReleaseGateActionPlan()` turns that matrix into pending gate
commands, the next gate command, stable display rows, and a shell script that
external CI can execute or show without duplicating the manifest order.
`createProjectReleaseGateExecutionManifest()` packages the same gate manifest
with caller-provided explicit results and observed output evidence. Each row
includes the command, expected output, matched observed output when present,
evidence kind, and `passed`/`unknown` status so CI or browser dashboards can
render release-gate execution state without scraping shell scripts.
`createProjectReleaseReadinessReport()` returns a machine-readable release
readiness report that embeds the same manifest, result matrix, and action plan
beside the sim-config inventory baseline and blocked runtime families. It is
evidence driven: the default report is `ready: false` until the caller supplies
observed gate output such as `project_release_gate=ok` or explicit gate
results.
beside the gate execution manifest, sim-config inventory baseline, and blocked
runtime families. It is evidence driven: the default report is `ready: false`
until the caller supplies observed gate output such as `project_release_gate=ok`
or explicit gate results.
`createProjectReleaseReadinessSummaryViewModel()` turns the report into stable
status text, a status line, and display rows for dashboards without executing
any gate.
@@ -231,9 +237,9 @@ load that JSON with `parseProjectReleaseReadinessArtifactJson()` and validate
it with `createProjectReleaseReadinessArtifactValidation()` without copying the
host test assertions. The validation result includes `expectedGateCount` and
`expectedGateIds` so external callers can compare an artifact against the
current manifest, plus `gateManifestReady`, `gateResultMatrixReady`, and
`gateActionPlanReady` so stale artifacts that omit the embedded gate evidence
chain fail the same executable gate.
current manifest, plus `gateManifestReady`, `gateExecutionManifestReady`,
`gateResultMatrixReady`, and `gateActionPlanReady` so stale artifacts that omit
the embedded gate evidence chain fail the same executable gate.
`createProjectReleaseReadinessArtifactValidationSummaryViewModel()` turns that
validation result into stable status text and display rows for dashboards or
browser shells without reinterpreting the artifact fields.

View File

@@ -2,6 +2,7 @@ export { createLinuxCncIniSdk } from "./linuxcnc-ini.js";
export { createLinuxCncInterpSdk } from "./linuxcnc-interp.js";
export {
createProjectReleaseGateActionPlan,
createProjectReleaseGateExecutionManifest,
createProjectReleaseGateManifest,
createProjectReleaseGateResultMatrix,
createProjectReleaseReadinessArtifactJsonWorkflow,

View File

@@ -98,6 +98,16 @@ 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 [];
}
export function createProjectReleaseGateManifest() {
return {
apiName: "project-release-gate-manifest",
@@ -113,6 +123,53 @@ export function createProjectReleaseGateManifest() {
};
}
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 createProjectReleaseGateResultMatrix({
gateResults = {},
observedOutputs = [],
@@ -206,6 +263,11 @@ export function createProjectReleaseReadinessReport({
promotedRuntimeFamilies = [],
} = {}) {
const gateManifest = createProjectReleaseGateManifest();
const gateExecutionManifest = createProjectReleaseGateExecutionManifest({
gateResults,
observedOutputs,
manifest: gateManifest,
});
const gateResultMatrix = createProjectReleaseGateResultMatrix({
gateResults,
observedOutputs,
@@ -249,6 +311,7 @@ export function createProjectReleaseReadinessReport({
promotedRuntimeFamilies: promotedFamilies,
promotedBlockedFamilies,
gateManifest,
gateExecutionManifest,
gateResultMatrix,
gateActionPlan,
gates: gateRows,
@@ -358,6 +421,11 @@ export function createProjectReleaseReadinessArtifactValidationSummaryViewModel(
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-result-matrix",
label: "Gate result matrix",
@@ -814,10 +882,12 @@ export function createProjectReleaseReadinessArtifactValidation(artifact = {}) {
const releaseGate = objectOrEmpty(artifactObject.releaseGate);
const simConfigInventory = objectOrEmpty(artifactObject.simConfigInventory);
const gateManifest = objectOrEmpty(artifactObject.gateManifest);
const gateExecutionManifest = objectOrEmpty(artifactObject.gateExecutionManifest);
const gateResultMatrix = objectOrEmpty(artifactObject.gateResultMatrix);
const gateActionPlan = objectOrEmpty(artifactObject.gateActionPlan);
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);
@@ -834,6 +904,13 @@ export function createProjectReleaseReadinessArtifactValidation(artifact = {}) {
&& 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 actionPlanReady = gateActionPlan.apiName === "project-release-gate-action-plan"
&& gateActionPlan.planVersion === 1
&& gateActionPlan.ready === true
@@ -858,6 +935,7 @@ export function createProjectReleaseReadinessArtifactValidation(artifact = {}) {
: ["blockedRuntimeFamilies"]),
...(arrayOrEmpty(artifactObject.promotedBlockedFamilies).length === 0 ? [] : ["promotedBlockedFamilies"]),
...(manifestReady ? [] : ["gateManifest"]),
...(executionManifestReady ? [] : ["gateExecutionManifest"]),
...(matrixReady ? [] : ["gateResultMatrix"]),
...(actionPlanReady ? [] : ["gateActionPlan"]),
...(gateCountReady ? [] : ["gates.length"]),
@@ -879,6 +957,7 @@ export function createProjectReleaseReadinessArtifactValidation(artifact = {}) {
expectedGateCount: REQUIRED_RELEASE_GATES.length,
expectedGateIds,
gateManifestReady: manifestReady,
gateExecutionManifestReady: executionManifestReady,
gateResultMatrixReady: matrixReady,
gateActionPlanReady: actionPlanReady,
blockedRuntimeFamilies: arrayOrEmpty(artifactObject.blockedRuntimeFamilies),
@@ -898,6 +977,11 @@ export function createProjectReleaseReadinessArtifactValidation(artifact = {}) {
label: "Gate manifest",
value: manifestReady ? "ready" : "missing",
},
{
id: "gate-execution-manifest",
label: "Gate execution manifest",
value: executionManifestReady ? "ready" : "missing",
},
{
id: "gate-result-matrix",
label: "Gate result matrix",

View File

@@ -469,9 +469,14 @@ TOOL_TABLE = browser-shell-tool.tbl
);
assertEqual(
workflowOverviewReleaseArtifactRenderResult.rowCount,
6,
7,
"shell workflow overview release artifact render rows",
);
assertEqual(
workflowOverviewReleaseArtifactRenderResult.rowIds.includes("gate-execution-manifest"),
true,
"shell workflow overview release artifact execution manifest row",
);
assertEqual(
workflowOverviewReleaseArtifactMountResult.ready,
true,

View File

@@ -262,9 +262,14 @@
.getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowSummaryViewModel(releaseArtifactUrlWorkflow);
assertEqual(releaseArtifactDomReadinessReady.ready, true, "workflow overview release artifact DOM readiness");
assertEqual(releaseArtifactRenderResult.rendered, true, "workflow overview release artifact render result");
assertEqual(releaseArtifactRenderResult.rowCount, 6, "workflow overview release artifact render row count");
assertEqual(releaseArtifactRenderResult.rowCount, 7, "workflow overview release artifact render row count");
assertEqual(
releaseArtifactRenderResult.rowIds.includes("gate-execution-manifest"),
true,
"workflow overview release artifact execution manifest row",
);
assertEqual(releaseArtifactMountResult.ready, true, "workflow overview release artifact mount readiness");
assertEqual(releaseArtifactMountResult.renderResult.rowCount, 6, "workflow overview release artifact mount rows");
assertEqual(releaseArtifactMountResult.renderResult.rowCount, 7, "workflow overview release artifact mount rows");
assertEqual(releaseArtifactUrlWorkflow.ready, true, "workflow overview release artifact URL workflow readiness");
assertEqual(releaseArtifactUrlWorkflow.httpStatus, 200, "workflow overview release artifact URL workflow status");
assertEqual(

View File

@@ -34,6 +34,7 @@ for (const phrase of [
"createProjectReleaseGateManifest()",
"createProjectReleaseGateResultMatrix()",
"createProjectReleaseGateActionPlan()",
"createProjectReleaseGateExecutionManifest()",
"createProjectReleaseReadinessReport()",
"createProjectReleaseReadinessSummaryViewModel()",
"parseProjectReleaseReadinessArtifactJson()",
@@ -44,7 +45,7 @@ for (const phrase of [
"loadProjectReleaseReadinessArtifactUrlWorkflow()",
"createProjectReleaseReadinessArtifactUrlWorkflowActionPlan()",
"createProjectReleaseReadinessArtifactUrlWorkflowSummaryViewModel()",
"embedded gate manifest, result matrix, and action plan",
"embedded gate manifest, execution manifest, result matrix, and",
"validateWorkflowOverviewReleaseReadinessArtifactJson()",
"loadWorkflowOverviewReleaseReadinessArtifactUrl()",
"getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowSummaryViewModel()",
@@ -122,6 +123,7 @@ for (const phrase of [
"createProjectReleaseReadinessArtifactValidationActionPlan()",
"createProjectReleaseReadinessArtifactValidationSummaryViewModel()",
"gateManifestReady",
"gateExecutionManifestReady",
"gateResultMatrixReady",
"gateActionPlanReady",
"loadIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrl()",

View File

@@ -101,6 +101,207 @@
}
]
},
"gateExecutionManifest": {
"apiName": "project-release-gate-execution-manifest",
"manifestVersion": 1,
"phase": "ready",
"ready": true,
"gateCount": 10,
"passedCount": 10,
"unknownCount": 0,
"missingGateIds": [],
"manifest": {
"apiName": "project-release-gate-manifest",
"manifestVersion": 1,
"gateCount": 10,
"gateIds": [
"diff-check",
"vendor-sync",
"standalone-cnc-semantics",
"interp-wasm",
"sim-config-inventory",
"ini-panel-browser",
"release-artifact-url-browser",
"ui-node-smokes",
"host-smokes",
"project-release-gate"
],
"gates": [
{
"id": "diff-check",
"label": "Git diff check",
"command": "git diff --check",
"expectedOutput": null
},
{
"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": "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": "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"
}
]
},
"rows": [
{
"id": "diff-check",
"label": "Git diff check",
"command": "git diff --check",
"expectedOutput": null,
"observedOutput": null,
"observedOutputCount": 9,
"status": "passed",
"passed": true,
"evidence": "explicit-result"
},
{
"id": "vendor-sync",
"label": "Vendor sync guard",
"command": "wasm-port/tools/verify_vendor_sync.sh",
"expectedOutput": "vendor sync up to date",
"observedOutput": "vendor sync up to date",
"observedOutputCount": 9,
"status": "passed",
"passed": true,
"evidence": "expected-output"
},
{
"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",
"observedOutput": "standalone CNC semantics guard complete",
"observedOutputCount": 9,
"status": "passed",
"passed": true,
"evidence": "expected-output"
},
{
"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",
"observedOutput": "interp_wasm_node_smoke=ok",
"observedOutputCount": 9,
"status": "passed",
"passed": true,
"evidence": "expected-output"
},
{
"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",
"observedOutput": "sim_configs_wasm_node_inventory_unexpected_fail=0",
"observedOutputCount": 9,
"status": "passed",
"passed": true,
"evidence": "expected-output"
},
{
"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",
"observedOutput": "browser_ini_shell_integration_workflow_smoke=ok",
"observedOutputCount": 9,
"status": "passed",
"passed": true,
"evidence": "expected-output"
},
{
"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",
"observedOutput": "browser_release_artifact_url_workflow_smoke=ok",
"observedOutputCount": 9,
"status": "passed",
"passed": true,
"evidence": "expected-output"
},
{
"id": "ui-node-smokes",
"label": "UI Node smokes",
"command": "wasm-port/tests/ui/node/verify_ui_node_smokes.sh",
"expectedOutput": "ui_node_smokes=ok",
"observedOutput": "ui_node_smokes=ok",
"observedOutputCount": 9,
"status": "passed",
"passed": true,
"evidence": "expected-output"
},
{
"id": "host-smokes",
"label": "Host aggregate smoke",
"command": "wasm-port/tests/host/verify_host_smokes.sh",
"expectedOutput": "host_wasm_opfs_browser_smokes=ok",
"observedOutput": "host_wasm_opfs_browser_smokes=ok",
"observedOutputCount": 9,
"status": "passed",
"passed": true,
"evidence": "expected-output"
},
{
"id": "project-release-gate",
"label": "Project release gate",
"command": "wasm-port/tests/host/verify_project_release_gate.sh",
"expectedOutput": "project_release_gate=ok",
"observedOutput": "project_release_gate=ok",
"observedOutputCount": 9,
"status": "passed",
"passed": true,
"evidence": "expected-output"
}
]
},
"gateResultMatrix": {
"apiName": "project-release-gate-result-matrix",
"matrixVersion": 1,

View File

@@ -23,6 +23,7 @@ assert.equal(validation.artifactVersion, 1);
assert.equal(validation.gateCount, 10);
assert.equal(validation.expectedGateCount, 10);
assert.equal(validation.gateManifestReady, true);
assert.equal(validation.gateExecutionManifestReady, true);
assert.equal(validation.gateResultMatrixReady, true);
assert.equal(validation.gateActionPlanReady, true);
assert.deepEqual(validation.expectedGateIds, [
@@ -38,6 +39,8 @@ assert.deepEqual(validation.expectedGateIds, [
"project-release-gate",
]);
assert.equal(artifact.gateManifest.apiName, "project-release-gate-manifest");
assert.equal(artifact.gateExecutionManifest.apiName, "project-release-gate-execution-manifest");
assert.equal(artifact.gateExecutionManifest.passedCount, 10);
assert.equal(artifact.gateResultMatrix.apiName, "project-release-gate-result-matrix");
assert.equal(artifact.gateActionPlan.apiName, "project-release-gate-action-plan");
assert.equal(artifact.gateActionPlan.pendingCount, 0);

View File

@@ -2,6 +2,7 @@ import assert from "node:assert/strict";
import {
createProjectReleaseGateActionPlan,
createProjectReleaseGateExecutionManifest,
createProjectReleaseGateManifest,
createProjectReleaseGateResultMatrix,
createProjectReleaseReadinessArtifactJsonWorkflow,
@@ -85,6 +86,40 @@ assert.deepEqual(
],
);
const partialExecutionManifest = createProjectReleaseGateExecutionManifest({
gateResults: {
"diff-check": true,
},
observedOutputs: [
"vendor sync up to date",
"browser_release_artifact_url_workflow_smoke=ok",
],
});
assert.equal(partialExecutionManifest.apiName, "project-release-gate-execution-manifest");
assert.equal(partialExecutionManifest.manifestVersion, 1);
assert.equal(partialExecutionManifest.phase, "waiting");
assert.equal(partialExecutionManifest.ready, false);
assert.equal(partialExecutionManifest.gateCount, 10);
assert.equal(partialExecutionManifest.passedCount, 3);
assert.equal(partialExecutionManifest.unknownCount, 7);
assert.deepEqual(partialExecutionManifest.missingGateIds, partialMatrix.missingGateIds);
assert.deepEqual(
partialExecutionManifest.rows.filter(({ passed }) => passed).map(({ id, evidence }) => [id, evidence]),
[
["diff-check", "explicit-result"],
["vendor-sync", "expected-output"],
["release-artifact-url-browser", "expected-output"],
],
);
assert.equal(
partialExecutionManifest.rows.find(({ id }) => id === "vendor-sync")?.observedOutput,
"vendor sync up to date",
);
assert.equal(
partialExecutionManifest.rows.find(({ id }) => id === "standalone-cnc-semantics")?.observedOutput,
null,
);
const partialActionPlan = createProjectReleaseGateActionPlan({ gateResultMatrix: partialMatrix });
assert.equal(partialActionPlan.apiName, "project-release-gate-action-plan");
assert.equal(partialActionPlan.planVersion, 1);
@@ -120,6 +155,9 @@ const report = createProjectReleaseReadinessReport({
gateResults: Object.fromEntries(manifest.gateIds.map((id) => [id, true])),
});
assert.deepEqual(report.gateManifest, manifest);
assert.equal(report.gateExecutionManifest.apiName, "project-release-gate-execution-manifest");
assert.equal(report.gateExecutionManifest.ready, true);
assert.equal(report.gateExecutionManifest.passedCount, manifest.gateCount);
assert.equal(report.gateResultMatrix.ready, true);
assert.equal(report.gateResultMatrix.passedCount, manifest.gateCount);
assert.equal(report.gateActionPlan.ready, true);
@@ -209,6 +247,7 @@ assert.deepEqual(
["artifact", "project-release-readiness-report"],
["gate-count", "10/10"],
["gate-manifest", "ready"],
["gate-execution-manifest", "ready"],
["gate-result-matrix", "ready"],
["gate-action-plan", "ready"],
["missing", "none"],
@@ -332,6 +371,10 @@ const failedArtifactUrlWorkflowSummary = createProjectReleaseReadinessArtifactUr
assert.equal(staleArtifactValidationSummary.phase, "blocked");
assert.match(staleArtifactValidationSummary.statusLine, /^Blocked: apiName, reportVersion/);
assert.equal(staleArtifactValidationSummary.rows.find(({ id }) => id === "gate-action-plan")?.value, "missing");
assert.equal(
staleArtifactValidationSummary.rows.find(({ id }) => id === "gate-execution-manifest")?.value,
"missing",
);
assert.equal(staleArtifactValidationActionPlan.phase, "blocked");
assert.equal(staleArtifactValidationActionPlan.ready, false);
assert.equal(staleArtifactValidationActionPlan.missingCount, staleArtifactValidation.missing.length);

View File

@@ -35,6 +35,7 @@ import {
createMachineSessionPersistenceRenderState,
createMachineSessionPersistenceSummary,
createProjectReleaseGateActionPlan,
createProjectReleaseGateExecutionManifest,
createProjectReleaseGateManifest,
createProjectReleaseGateResultMatrix,
createProjectReleaseReadinessArtifactJsonWorkflow,
@@ -96,6 +97,7 @@ const requiredExports = [
["createLinuxCncIniSdk", createLinuxCncIniSdk],
["createLinuxCncInterpSdk", createLinuxCncInterpSdk],
["createProjectReleaseGateActionPlan", createProjectReleaseGateActionPlan],
["createProjectReleaseGateExecutionManifest", createProjectReleaseGateExecutionManifest],
["createProjectReleaseGateManifest", createProjectReleaseGateManifest],
["createProjectReleaseGateResultMatrix", createProjectReleaseGateResultMatrix],
["createProjectReleaseReadinessArtifactJsonWorkflow", createProjectReleaseReadinessArtifactJsonWorkflow],
@@ -302,6 +304,7 @@ assert.deepEqual(releaseReadinessArtifactValidation, {
expectedGateCount: 10,
expectedGateIds: createProjectReleaseGateManifest().gateIds,
gateManifestReady: true,
gateExecutionManifestReady: true,
gateResultMatrixReady: true,
gateActionPlanReady: true,
blockedRuntimeFamilies: [
@@ -325,6 +328,11 @@ assert.deepEqual(releaseReadinessArtifactValidation, {
label: "Gate manifest",
value: "ready",
},
{
id: "gate-execution-manifest",
label: "Gate execution manifest",
value: "ready",
},
{
id: "gate-result-matrix",
label: "Gate result matrix",
@@ -428,6 +436,7 @@ assert.deepEqual(
"simConfigInventory.unexpectedFail",
"blockedRuntimeFamilies",
"gateManifest",
"gateExecutionManifest",
"gateResultMatrix",
"gateActionPlan",
"gates.length",

View File

@@ -1366,14 +1366,18 @@ const releaseArtifactMountResult = mountIniPanelShellWorkflowOverviewReleaseRead
rowsNode: releaseArtifactRowsNode,
});
assert.equal(releaseArtifactRenderResult.rendered, true);
assert.equal(releaseArtifactRenderResult.rowCount, 6);
assert.equal(releaseArtifactRenderResult.rowCount, 7);
assert.equal(releaseArtifactRenderResult.dataset.handoffScope, "workflow-overview-release-readiness-artifact");
assert.equal(
releaseArtifactRenderResult.rowIds.includes("gate-execution-manifest"),
true,
);
assert.equal(
releaseArtifactRenderResult.rowIds.includes("gate-action-plan"),
true,
);
assert.equal(releaseArtifactMountResult.ready, true);
assert.equal(releaseArtifactMountResult.renderResult.rowCount, 6);
assert.equal(releaseArtifactMountResult.renderResult.rowCount, 7);
assert.deepEqual(
validateIniPanelShellWorkflowOverviewReleaseReadinessArtifactJson("{").missing,
["artifact-json"],
@@ -1414,7 +1418,7 @@ assert.equal(
);
assert.equal(releaseArtifactUrlWorkflow.renderState.statusLine, "Ready: No blocking reasons");
assert.equal(releaseArtifactUrlWorkflow.mountResult.ready, true);
assert.equal(releaseArtifactUrlWorkflow.mountResult.renderResult.rowCount, 6);
assert.equal(releaseArtifactUrlWorkflow.mountResult.renderResult.rowCount, 7);
const releaseArtifactUrlWorkflowSummary =
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowSummaryViewModel(
releaseArtifactUrlWorkflow,