新增发布 artifact validation summary view model

This commit is contained in:
2026-06-16 11:08:38 +08:00
parent ce2c145374
commit 82bb02de15
9 changed files with 163 additions and 3 deletions

View File

@@ -54,6 +54,8 @@ continuation records are in `../text14.txt`.
`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.
`createProjectReleaseReadinessArtifactValidationSummaryViewModel()` turns the
validation into stable dashboard rows without reinterpreting artifact fields.
Browser shells can also pass the JSON to
`linuxCncIniPanelWorkflowOverviewApi.validateWorkflowOverviewReleaseReadinessArtifactJson()`
through the workflow overview iframe and receive stable validation rows. They

View File

@@ -55,6 +55,7 @@ import {
createProjectReleaseGateManifest,
createProjectReleaseGateResultMatrix,
createProjectReleaseReadinessArtifactValidation,
createProjectReleaseReadinessArtifactValidationSummaryViewModel,
createProjectReleaseReadinessReport,
createProjectReleaseReadinessSummaryViewModel,
createSessionSnapshot,
@@ -226,6 +227,9 @@ host test assertions. The validation result includes `expectedGateCount` and
current manifest, plus `gateManifestReady`, `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.
## OPFS/session persistence exports

View File

@@ -5,6 +5,7 @@ export {
createProjectReleaseGateManifest,
createProjectReleaseGateResultMatrix,
createProjectReleaseReadinessArtifactValidation,
createProjectReleaseReadinessArtifactValidationSummaryViewModel,
createProjectReleaseReadinessReport,
createProjectReleaseReadinessSummaryViewModel,
parseProjectReleaseReadinessArtifactJson,

View File

@@ -324,6 +324,59 @@ export function createProjectReleaseReadinessSummaryViewModel(
};
}
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-result-matrix",
label: "Gate result matrix",
value: validation?.gateResultMatrixReady === true ? "ready" : "missing",
},
{
id: "gate-action-plan",
label: "Gate action plan",
value: validation?.gateActionPlanReady === true ? "ready" : "missing",
},
{
id: "missing",
label: "Missing artifact evidence",
value: missing.length > 0 ? missing.join(", ") : "none",
},
],
};
}
export function parseProjectReleaseReadinessArtifactJson(text) {
return JSON.parse(String(text));
}

View File

@@ -38,6 +38,7 @@ for (const phrase of [
"createProjectReleaseReadinessSummaryViewModel()",
"parseProjectReleaseReadinessArtifactJson()",
"createProjectReleaseReadinessArtifactValidation()",
"createProjectReleaseReadinessArtifactValidationSummaryViewModel()",
"embedded gate manifest, result matrix, and action plan",
"validateWorkflowOverviewReleaseReadinessArtifactJson()",
"loadWorkflowOverviewReleaseReadinessArtifactUrl()",
@@ -108,6 +109,7 @@ for (const phrase of [
"createProjectReleaseReadinessSummaryViewModel()",
"parseProjectReleaseReadinessArtifactJson()",
"createProjectReleaseReadinessArtifactValidation()",
"createProjectReleaseReadinessArtifactValidationSummaryViewModel()",
"gateManifestReady",
"gateResultMatrixReady",
"gateActionPlanReady",

View File

@@ -4,6 +4,8 @@ import {
createProjectReleaseGateActionPlan,
createProjectReleaseGateManifest,
createProjectReleaseGateResultMatrix,
createProjectReleaseReadinessArtifactValidation,
createProjectReleaseReadinessArtifactValidationSummaryViewModel,
createProjectReleaseReadinessReport,
createProjectReleaseReadinessSummaryViewModel,
} from "../../../runtime/sdk/src/index.js";
@@ -171,4 +173,31 @@ assert.equal(
"promoted: L4-TOOL-DB",
);
const artifactValidationSummary = createProjectReleaseReadinessArtifactValidationSummaryViewModel(
createProjectReleaseReadinessArtifactValidation(report),
);
assert.equal(
artifactValidationSummary.apiName,
"project-release-readiness-artifact-validation-summary-view-model",
);
assert.equal(artifactValidationSummary.phase, "ready");
assert.equal(artifactValidationSummary.statusLine, "Ready: Artifact evidence complete");
assert.deepEqual(
artifactValidationSummary.rows.map(({ id, value }) => [id, value]),
[
["artifact", "project-release-readiness-report"],
["gate-count", "10/10"],
["gate-manifest", "ready"],
["gate-result-matrix", "ready"],
["gate-action-plan", "ready"],
["missing", "none"],
],
);
const staleArtifactValidationSummary = createProjectReleaseReadinessArtifactValidationSummaryViewModel(
createProjectReleaseReadinessArtifactValidation({}),
);
assert.equal(staleArtifactValidationSummary.phase, "blocked");
assert.match(staleArtifactValidationSummary.statusLine, /^Blocked: apiName, reportVersion/);
assert.equal(staleArtifactValidationSummary.rows.find(({ id }) => id === "gate-action-plan")?.value, "missing");
console.log("project_release_gate_manifest_node_smoke=ok");

View File

@@ -36,6 +36,7 @@ import {
createProjectReleaseGateManifest,
createProjectReleaseGateResultMatrix,
createProjectReleaseReadinessArtifactValidation,
createProjectReleaseReadinessArtifactValidationSummaryViewModel,
createProjectReleaseReadinessReport,
createProjectReleaseReadinessSummaryViewModel,
parseProjectReleaseReadinessArtifactJson,
@@ -91,6 +92,10 @@ const requiredExports = [
["createProjectReleaseGateManifest", createProjectReleaseGateManifest],
["createProjectReleaseGateResultMatrix", createProjectReleaseGateResultMatrix],
["createProjectReleaseReadinessArtifactValidation", createProjectReleaseReadinessArtifactValidation],
[
"createProjectReleaseReadinessArtifactValidationSummaryViewModel",
createProjectReleaseReadinessArtifactValidationSummaryViewModel,
],
["createProjectReleaseReadinessReport", createProjectReleaseReadinessReport],
["createProjectReleaseReadinessSummaryViewModel", createProjectReleaseReadinessSummaryViewModel],
["parseProjectReleaseReadinessArtifactJson", parseProjectReleaseReadinessArtifactJson],
@@ -255,7 +260,8 @@ const releaseReadinessArtifact = parseProjectReleaseReadinessArtifactJson(JSON.s
},
})));
assert.equal(releaseReadinessArtifact.apiName, "project-release-readiness-report");
assert.deepEqual(createProjectReleaseReadinessArtifactValidation(releaseReadinessArtifact), {
const releaseReadinessArtifactValidation = createProjectReleaseReadinessArtifactValidation(releaseReadinessArtifact);
assert.deepEqual(releaseReadinessArtifactValidation, {
apiName: "project-release-readiness-artifact-validation",
validationVersion: 1,
phase: "ready",
@@ -307,6 +313,12 @@ assert.deepEqual(createProjectReleaseReadinessArtifactValidation(releaseReadines
},
],
});
assert.equal(
createProjectReleaseReadinessArtifactValidationSummaryViewModel(
releaseReadinessArtifactValidation,
).statusLine,
"Ready: Artifact evidence complete",
);
assert.deepEqual(
createProjectReleaseReadinessArtifactValidation({}).missing,
[