docs: record hwlab agentrun failure attribution
This commit is contained in:
+121
-1
@@ -1233,6 +1233,101 @@ export function v02FalseGreenGuard(input: {
|
||||
};
|
||||
}
|
||||
|
||||
function v02TargetValidation(input: {
|
||||
targetMode: V02StatusTargetMode;
|
||||
sourceCommit: string | null;
|
||||
pipelineRun: Record<string, unknown> | null;
|
||||
argo: Record<string, unknown>;
|
||||
runtimeWorkloads: Record<string, unknown>;
|
||||
webAssets: Record<string, unknown>;
|
||||
gitMirror: Record<string, unknown>;
|
||||
recentPipelineRuns: Record<string, unknown>;
|
||||
}): Record<string, unknown> {
|
||||
if (input.targetMode === "latest-source-head") {
|
||||
return {
|
||||
applicable: false,
|
||||
state: "latest-source-head",
|
||||
summary: "default status uses strict latest source head alignment; use --pipeline-run or --source-commit for historical run validation",
|
||||
};
|
||||
}
|
||||
const sourceCommit = input.sourceCommit;
|
||||
const argoFields = record(input.argo.fields);
|
||||
const gitMirrorSummary = record(input.gitMirror.summary);
|
||||
const workloadItems = Array.isArray(input.runtimeWorkloads.items)
|
||||
? input.runtimeWorkloads.items.map((item) => record(item))
|
||||
: [];
|
||||
const serviceSourceCommits = Object.fromEntries(workloadItems
|
||||
.filter((item) => item.serviceId === "hwlab-cloud-api" || item.serviceId === "hwlab-cloud-web")
|
||||
.map((item) => [String(item.serviceId), stringOrNull(item.sourceCommit) ?? stringOrNull(item.artifactSourceCommit)]));
|
||||
const recentItems = Array.isArray(input.recentPipelineRuns.items)
|
||||
? input.recentPipelineRuns.items.map((item) => record(item))
|
||||
: [];
|
||||
const targetPipelineRunName = stringOrNull(input.pipelineRun?.pipelineRun) ?? stringOrNull(input.pipelineRun?.name);
|
||||
const targetRecentIndex = recentItems.findIndex((item) => (
|
||||
(targetPipelineRunName !== null && item.name === targetPipelineRunName)
|
||||
|| (sourceCommit !== null && item.sourceCommit === sourceCommit)
|
||||
));
|
||||
const newerCandidates = targetRecentIndex >= 0 ? recentItems.slice(0, targetRecentIndex) : recentItems;
|
||||
const newerSucceededRuns = sourceCommit === null
|
||||
? []
|
||||
: newerCandidates.filter((item) => item.sourceCommit !== sourceCommit && item.status === "True");
|
||||
const failures: Record<string, unknown>[] = [];
|
||||
const supersededRuntimeServices: Record<string, unknown>[] = [];
|
||||
if (sourceCommit === null) failures.push({ reason: "source-commit-unresolved" });
|
||||
if (input.pipelineRun === null || input.pipelineRun.exists === false) {
|
||||
failures.push({ reason: "pipeline-run-missing" });
|
||||
} else if (input.pipelineRun.status !== "True") {
|
||||
failures.push({ reason: "pipeline-run-not-succeeded", status: input.pipelineRun.status ?? null, reasonDetail: input.pipelineRun.reason ?? null });
|
||||
}
|
||||
if (input.argo.ok !== true || argoFields.syncStatus !== "Synced" || argoFields.health !== "Healthy") {
|
||||
failures.push({ reason: "argo-not-synced-healthy", syncStatus: argoFields.syncStatus ?? null, health: argoFields.health ?? null });
|
||||
}
|
||||
if (input.webAssets.ok !== true) failures.push({ reason: "web-assets-probe-failed", summary: input.webAssets.summary ?? null });
|
||||
if (gitMirrorSummary.pendingFlush !== false || gitMirrorSummary.githubInSync !== true) {
|
||||
failures.push({
|
||||
reason: "git-mirror-not-flushed",
|
||||
pendingFlush: gitMirrorSummary.pendingFlush ?? null,
|
||||
githubInSync: gitMirrorSummary.githubInSync ?? null,
|
||||
});
|
||||
}
|
||||
for (const serviceId of ["hwlab-cloud-api", "hwlab-cloud-web"]) {
|
||||
const serviceCommit = serviceSourceCommits[serviceId];
|
||||
if (sourceCommit !== null && serviceCommit !== sourceCommit) {
|
||||
const mismatch = { reason: "runtime-service-source-mismatch", serviceId, expectedSourceCommit: sourceCommit, actualSourceCommit: serviceCommit ?? null };
|
||||
if (newerSucceededRuns.length > 0) supersededRuntimeServices.push(mismatch);
|
||||
else failures.push(mismatch);
|
||||
}
|
||||
}
|
||||
const superseded = failures.length === 0 && supersededRuntimeServices.length > 0;
|
||||
return {
|
||||
applicable: true,
|
||||
ok: failures.length === 0,
|
||||
state: failures.length === 0 ? (superseded ? "superseded" : "passed") : "failed",
|
||||
summary: failures.length === 0
|
||||
? superseded
|
||||
? `target ${input.targetMode} completed for ${shortSha(sourceCommit ?? "")}; runtime now reflects newer v0.2 PipelineRun`
|
||||
: `target ${input.targetMode} validation passed for ${shortSha(sourceCommit ?? "")}`
|
||||
: `target ${input.targetMode} validation failed with ${failures.length} issue(s)`,
|
||||
sourceCommit,
|
||||
pipelineRun: input.pipelineRun === null
|
||||
? null
|
||||
: { name: input.pipelineRun.pipelineRun ?? null, status: input.pipelineRun.status ?? null, reason: input.pipelineRun.reason ?? null },
|
||||
argo: { syncRevision: argoFields.syncRevision ?? null, syncStatus: argoFields.syncStatus ?? null, health: argoFields.health ?? null },
|
||||
runtimeServices: serviceSourceCommits,
|
||||
superseded,
|
||||
supersededRuntimeServices,
|
||||
newerSucceededRuns: newerSucceededRuns.slice(0, 3).map((item) => ({
|
||||
name: item.name ?? null,
|
||||
sourceCommit: item.sourceCommit ?? null,
|
||||
createdAt: item.createdAt ?? null,
|
||||
durationSeconds: item.durationSeconds ?? null,
|
||||
})),
|
||||
webAssets: { ok: input.webAssets.ok ?? null, summary: input.webAssets.summary ?? null, appJsBytes: record(input.webAssets.assetBytes).appJs ?? null },
|
||||
gitMirror: { pendingFlush: gitMirrorSummary.pendingFlush ?? null, githubInSync: gitMirrorSummary.githubInSync ?? null },
|
||||
failures,
|
||||
};
|
||||
}
|
||||
|
||||
export function v02CommitAlignment(input: {
|
||||
expectedSourceHead: string | null;
|
||||
sourceHeads: Record<string, unknown>;
|
||||
@@ -1939,6 +2034,30 @@ function v02ControlPlaneStatus(target: V02ControlPlaneStatusTarget = {}): Record
|
||||
runtimeWorkloads,
|
||||
webAssets,
|
||||
});
|
||||
const targetValidation = v02TargetValidation({
|
||||
targetMode,
|
||||
sourceCommit,
|
||||
pipelineRun: pipelineRunInfo,
|
||||
argo: {
|
||||
ok: shellSectionOk(argo),
|
||||
fields: { targetRevision, path, syncRevision, syncStatus, health },
|
||||
},
|
||||
runtimeWorkloads,
|
||||
webAssets,
|
||||
gitMirror,
|
||||
recentPipelineRuns,
|
||||
});
|
||||
const falseGreenGuard = targetValidation.state === "superseded"
|
||||
? {
|
||||
ok: null,
|
||||
state: "superseded",
|
||||
summary: "target PipelineRun succeeded but runtime now reflects a newer v0.2 PipelineRun; current-runtime false-green guard is not applicable to this historical target",
|
||||
sourceCommit,
|
||||
targetValidationState: targetValidation.state,
|
||||
newerSucceededRuns: targetValidation.newerSucceededRuns ?? [],
|
||||
supersededRuntimeServices: targetValidation.supersededRuntimeServices ?? [],
|
||||
}
|
||||
: v02FalseGreenGuard({ sourceCommit, pipelineRun: pipelineRunInfo, taskRuns, planArtifacts, runtimeWorkloads });
|
||||
const baseOk = sourceCommit !== null && isCommandSuccess(bundle) && shellSectionOk(controlPlane) && shellSectionOk(argo);
|
||||
const targetPipelineRunOk = strictHeadAlignment
|
||||
? true
|
||||
@@ -1984,6 +2103,7 @@ function v02ControlPlaneStatus(target: V02ControlPlaneStatusTarget = {}): Record
|
||||
argoApplication: V02_APP,
|
||||
},
|
||||
commitAlignment,
|
||||
targetValidation,
|
||||
sourceHeads,
|
||||
gitMirror,
|
||||
controlPlane: {
|
||||
@@ -2007,7 +2127,7 @@ function v02ControlPlaneStatus(target: V02ControlPlaneStatusTarget = {}): Record
|
||||
taskRuns,
|
||||
planArtifacts,
|
||||
runtimeWorkloads,
|
||||
falseGreenGuard: v02FalseGreenGuard({ sourceCommit, pipelineRun: pipelineRunInfo, taskRuns, planArtifacts, runtimeWorkloads }),
|
||||
falseGreenGuard,
|
||||
webAssets,
|
||||
activePipelineRuns,
|
||||
recentPipelineRuns,
|
||||
|
||||
Reference in New Issue
Block a user