新增 release gate execution manifest API
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -2,6 +2,7 @@ export { createLinuxCncIniSdk } from "./linuxcnc-ini.js";
|
||||
export { createLinuxCncInterpSdk } from "./linuxcnc-interp.js";
|
||||
export {
|
||||
createProjectReleaseGateActionPlan,
|
||||
createProjectReleaseGateExecutionManifest,
|
||||
createProjectReleaseGateManifest,
|
||||
createProjectReleaseGateResultMatrix,
|
||||
createProjectReleaseReadinessArtifactJsonWorkflow,
|
||||
|
||||
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user