Files
pikasTech-unidesk/scripts/hwlab-g14-contract-test.ts
T

707 lines
35 KiB
TypeScript

import { activeV02PipelineRuns, gitMirrorFlushJobManifest, gitMirrorStatusSummary, gitMirrorSyncJobManifest, gitMirrorV02SyncRequirement, hwlabG14Help, hwlabG14MonitorStateFileName, parseGitMirrorStatusRefs, parsePipelineTaskRunMetrics, parseV02TriggerSnapshot, rolloutRecordBody, semanticChangelogBullets, summarizeV02CdStatus, v02CloseoutVerdict, v02CommitAlignment, v02ControlPlaneRefreshScriptHash, v02ControlPlaneRenderScript, v02ExistingPipelineRunReuseDecision, v02FalseGreenGuard, v02GitMirrorPreSyncWaitMs, v02LatestOnlyTargetValidation, v02PipelineServiceIds, v02PrAutomationCommentBody, v02ReusableGitMirrorPreSyncMarker, v02ReusableRefreshMarker, v02TaskRunPerformanceSummary } from "./src/hwlab-g14";
function assertCondition(condition: unknown, message: string, detail: unknown = {}): void {
if (!condition) throw new Error(`${message}: ${JSON.stringify(detail)}`);
}
function record(value: unknown): Record<string, unknown> {
return typeof value === "object" && value !== null && !Array.isArray(value) ? value as Record<string, unknown> : {};
}
assertCondition(
hwlabG14MonitorStateFileName({ once: false, dryRun: false }) === "latest-monitor-job.json",
"long-running monitor should own latest-monitor-job.json",
);
assertCondition(
hwlabG14MonitorStateFileName({ once: true, dryRun: false }) === "latest-once-job.json",
"once jobs must not overwrite the long-running monitor pointer",
);
assertCondition(
hwlabG14MonitorStateFileName({ once: false, dryRun: true }) === "latest-dry-run-job.json",
"dry-run monitor jobs must not overwrite the live monitor pointer",
);
assertCondition(
hwlabG14MonitorStateFileName({ once: true, dryRun: true }) === "latest-once-dry-run-job.json",
"once dry-runs need a distinct pointer for low-noise diagnostics",
);
assertCondition(
hwlabG14MonitorStateFileName({ lane: "v02", once: false, dryRun: false }) === "latest-v02-monitor-job.json"
&& hwlabG14MonitorStateFileName({ lane: "v02", once: true, dryRun: false }) === "latest-v02-once-job.json"
&& hwlabG14MonitorStateFileName({ lane: "v02", once: false, dryRun: true }) === "latest-v02-dry-run-job.json"
&& hwlabG14MonitorStateFileName({ lane: "v02", once: true, dryRun: true }) === "latest-v02-once-dry-run-job.json",
"v0.2 PR monitor state pointers must not overwrite the legacy G14 monitor pointers",
);
const hwlabHelpUsage = Array.isArray(hwlabG14Help().usage) ? hwlabG14Help().usage.map(String) : [];
assertCondition(
hwlabHelpUsage.some((line) => line.includes("control-plane status --lane v02 --pipeline-run"))
&& hwlabHelpUsage.some((line) => line.includes("control-plane status --lane v02 --source-commit"))
&& hwlabHelpUsage.some((line) => line.includes("control-plane closeout --lane v02 --pipeline-run"))
&& hwlabHelpUsage.some((line) => line.includes("control-plane closeout --lane v02 --source-commit")),
"v0.2 control-plane help must expose targeted PipelineRun/source-commit status and closeout inspection",
hwlabHelpUsage,
);
assertCondition(
hwlabHelpUsage.some((line) => line.includes("monitor-prs --lane v02"))
&& hwlabHelpUsage.some((line) => line.includes("monitor-prs --lane v02 --status"))
&& JSON.stringify(hwlabG14Help()).includes("v02-pr-comment-signatures.json")
&& JSON.stringify(hwlabG14Help()).includes("latest-only"),
"v0.2 PR monitor help must expose the auto CI/CD lane, status query, latest-only CD, and dedupe comment state",
hwlabG14Help(),
);
assertCondition(
hwlabHelpUsage.some((line) => line.includes("secret status --lane v02 --name hwlab-v02-openfga"))
&& hwlabHelpUsage.some((line) => line.includes("secret ensure --lane v02 --name hwlab-v02-openfga --confirm")),
"v0.2 secret help must expose the controlled OpenFGA SecretRef bootstrap path",
hwlabHelpUsage,
);
assertCondition(
hwlabHelpUsage.some((line) => line.includes("upstream-image status --name openfga --tag v1.17.0"))
&& hwlabHelpUsage.some((line) => line.includes("upstream-image ensure --name openfga --tag v1.17.0 --confirm")),
"v0.2 help must expose the controlled OpenFGA upstream image mirroring path",
hwlabHelpUsage,
);
const v02CommentBody = v02PrAutomationCommentBody({
pr: {
number: 848,
title: "迁移:v0.2 PR 合并后自动触发 CD",
url: "https://github.com/pikasTech/HWLAB/pull/848",
baseRefName: "v0.2",
headRefName: "fix/v02-auto-cd",
},
phase: "cd-passed",
state: "cd-succeeded",
startedAt: "2026-06-04T00:00:00.000Z",
observedAt: "2026-06-04T00:07:30.000Z",
elapsedSeconds: 450,
preflight: {
conclusion: "ready",
readyForCommanderMerge: true,
mergeable: "MERGEABLE",
mergeStateStatus: "CLEAN",
blockers: [],
pending: [],
},
sourceCommit: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
pipelineRun: "hwlab-v02-ci-poll-aaaaaaaaaaaa",
cd: {
pipelineStatus: "True",
pipelineReason: "Succeeded",
targetValidationState: "passed",
argoSync: "Synced",
argoHealth: "Healthy",
webAssetsOk: true,
pendingFlush: false,
githubInSync: true,
rolloutServices: ["hwlab-cloud-api"],
buildServices: [],
reusedServices: ["hwlab-cloud-web"],
},
flush: { ok: true },
});
assertCondition(
v02CommentBody.includes("elapsed: 7m30s")
&& v02CommentBody.includes("conflict: `clear-or-unknown`")
&& v02CommentBody.includes("hwlab-v02-ci-poll-aaaaaaaaaaaa")
&& v02CommentBody.includes("pendingFlush=`false`")
&& v02CommentBody.includes("targetValidation: `passed`"),
"v0.2 PR automation comments must include duration, conflict state, PipelineRun, target validation, and git mirror flush status",
v02CommentBody,
);
const gitMirrorJob = gitMirrorSyncJobManifest("git-mirror-hwlab-sync-manual-test");
assertCondition(gitMirrorJob.kind === "Job", "git mirror sync must be a manual Job, not a CronJob", gitMirrorJob);
assertCondition(record(gitMirrorJob.metadata).namespace === "devops-infra", "git mirror sync Job must target devops-infra", gitMirrorJob);
assertCondition(record(record(gitMirrorJob.metadata).labels)["hwlab.pikastech.local/trigger"] === "manual-cli", "git mirror sync Job must be labeled as manual CLI triggered", gitMirrorJob);
assertCondition(record(gitMirrorJob.spec).backoffLimit === 0, "git mirror sync Job should fail visibly instead of retrying in the background", gitMirrorJob);
const gitMirrorFlushJob = gitMirrorFlushJobManifest("git-mirror-hwlab-flush-manual-test");
assertCondition(gitMirrorFlushJob.kind === "Job", "git mirror flush must be a manual Job", gitMirrorFlushJob);
assertCondition(record(record(gitMirrorFlushJob.metadata).labels)["app.kubernetes.io/component"] === "flush-controller", "git mirror flush Job must be labeled separately from sync", gitMirrorFlushJob);
assertCondition(record(record(gitMirrorFlushJob.metadata).labels)["hwlab.pikastech.local/trigger"] === "manual-cli", "git mirror flush Job must be labeled as manual CLI triggered", gitMirrorFlushJob);
const flushTemplate = record(record(gitMirrorFlushJob.spec).template);
const flushPodSpec = record(flushTemplate.spec);
const flushContainer = record(Array.isArray(flushPodSpec.containers) ? flushPodSpec.containers[0] : null);
assertCondition(JSON.stringify(flushContainer.command) === JSON.stringify(["/script/flush.sh"]), "git mirror flush Job must run the flush script", gitMirrorFlushJob);
const gitMirrorStatusRaw = [
'lastSync={"ok":true}',
"lastWrite=",
"lastFlush=",
'refs={"refs":{"localV02":"0cf79ecc9d8a784b7712b0b3a58d5e39025ba0dc","githubV02":"0cf79ecc9d8a784b7712b0b3a58d5e39025ba0dc","localG14":"1111111111111111111111111111111111111111","githubG14":"1111111111111111111111111111111111111111","localGitops":"2222222222222222222222222222222222222222","githubGitops":"3333333333333333333333333333333333333333"},"pendingFlush":true}',
].join("\n");
const parsedGitMirrorRefs = parseGitMirrorStatusRefs(gitMirrorStatusRaw);
assertCondition(
parsedGitMirrorRefs.refs.localV02 === "0cf79ecc9d8a784b7712b0b3a58d5e39025ba0dc",
"git mirror status parser must expose local v0.2 ref for trigger pre-sync",
parsedGitMirrorRefs,
);
assertCondition(parsedGitMirrorRefs.pendingFlush === true, "git mirror status parser must preserve pending flush signal", parsedGitMirrorRefs);
assertCondition(
parsedGitMirrorRefs.refs.githubV02 === "0cf79ecc9d8a784b7712b0b3a58d5e39025ba0dc",
"git mirror status parser must expose GitHub source branch staging ref",
parsedGitMirrorRefs,
);
assertCondition(
gitMirrorV02SyncRequirement("0cf79ecc9d8a784b7712b0b3a58d5e39025ba0dc", gitMirrorStatusRaw).required === false,
"trigger-current must not sync mirror when local v0.2 already matches source commit",
);
assertCondition(
gitMirrorV02SyncRequirement("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", gitMirrorStatusRaw).required === true,
"trigger-current must sync mirror before creating PipelineRun when local v0.2 is stale",
);
const reusableGitMirrorPreSyncMarker = v02ReusableGitMirrorPreSyncMarker({
ok: true,
sourceCommit: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
syncedAt: "2026-06-04T16:00:10.000Z",
localV02: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
githubV02: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
}, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", Date.parse("2026-06-04T16:01:00.000Z"), Date.parse("2026-06-04T16:00:00.000Z"));
const preObservationGitMirrorMarker = v02ReusableGitMirrorPreSyncMarker({
ok: true,
sourceCommit: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
syncedAt: "2026-06-04T16:00:10.000Z",
}, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", Date.parse("2026-06-04T16:01:00.000Z"), Date.parse("2026-06-04T16:00:20.000Z"));
const staleGitMirrorPreSyncMarker = v02ReusableGitMirrorPreSyncMarker({
ok: true,
sourceCommit: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
syncedAt: "2026-06-04T16:00:10.000Z",
}, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", Date.parse("2026-06-04T16:03:00.000Z"), Date.parse("2026-06-04T16:00:00.000Z"));
assertCondition(
reusableGitMirrorPreSyncMarker !== null && preObservationGitMirrorMarker === null && staleGitMirrorPreSyncMarker === null,
"v0.2 git mirror pre-sync marker must only reuse fresh same-commit markers written after the stale status observation",
{ reusableGitMirrorPreSyncMarker, preObservationGitMirrorMarker, staleGitMirrorPreSyncMarker },
);
assertCondition(
v02GitMirrorPreSyncWaitMs(10) === 10_000
&& v02GitMirrorPreSyncWaitMs(180) === 120_000
&& v02GitMirrorPreSyncWaitMs(0) === 120_000,
"v0.2 git mirror pre-sync wait must not impose a hidden 30s minimum latency",
{ tenSeconds: v02GitMirrorPreSyncWaitMs(10), capped: v02GitMirrorPreSyncWaitMs(180), default: v02GitMirrorPreSyncWaitMs(0) },
);
const gitMirrorSummary = gitMirrorStatusSummary(gitMirrorStatusRaw);
assertCondition(
gitMirrorSummary.flushNeeded === true && gitMirrorSummary.flushCommand === "bun scripts/cli.ts hwlab g14 git-mirror flush --confirm",
"git mirror status summary must expose pending flush and the exact controlled flush command",
gitMirrorSummary,
);
assertCondition(gitMirrorSummary.githubInSync === false, "git mirror status summary must expose GitHub GitOps drift", gitMirrorSummary);
assertCondition(gitMirrorSummary.sourceInSync === true && gitMirrorSummary.gitopsInSync === false, "git mirror status must split source and gitops GitHub sync state", gitMirrorSummary);
const renderScript = v02ControlPlaneRenderScript("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
const renderScriptHash = v02ControlPlaneRefreshScriptHash("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
const sourceText = await Bun.file(new URL("./src/hwlab-g14.ts", import.meta.url)).text();
assertCondition(
renderScript.includes("git clone --shared --no-checkout \"$cicd_repo\" \"$worktree_dir\"")
&& renderScript.includes("git -C \"$worktree_dir\" checkout --detach \"$source_commit\"")
&& renderScript.includes("/tmp/hwlab-v02-control-plane-source-aaaaaaaaaaaa-"),
"v0.2 control-plane render must use an isolated temp clone from the CI/CD repo so same-commit concurrent triggers do not share worktree metadata",
renderScript,
);
assertCondition(
renderScript.includes("flock -w 120 9") && renderScript.includes("v0.2 CI/CD repo refresh failed status="),
"v0.2 CI/CD repo refresh must serialize only the shared bare repo fetch and expose lock/fetch failures",
renderScript,
);
const reusableRefreshMarker = v02ReusableRefreshMarker({
ok: true,
sourceCommit: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
refreshedAt: "2026-06-04T16:00:00.000Z",
renderScriptHash,
renderDir: "/tmp/hwlab-v02-control-plane-aaaaaaaaaaaa-contract",
}, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", renderScriptHash, Date.parse("2026-06-04T16:01:00.000Z"));
const staleRefreshMarker = v02ReusableRefreshMarker({
ok: true,
sourceCommit: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
refreshedAt: "2026-06-04T16:00:00.000Z",
renderScriptHash,
}, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", renderScriptHash, Date.parse("2026-06-04T16:10:01.000Z"));
const wrongScriptMarker = v02ReusableRefreshMarker({
ok: true,
sourceCommit: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
refreshedAt: "2026-06-04T16:00:00.000Z",
renderScriptHash: "old-script",
}, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", renderScriptHash, Date.parse("2026-06-04T16:01:00.000Z"));
assertCondition(
reusableRefreshMarker !== null && staleRefreshMarker === null && wrongScriptMarker === null,
"v0.2 control-plane refresh marker must only reuse recent markers for the same render script contract",
{ reusableRefreshMarker, staleRefreshMarker, wrongScriptMarker },
);
assertCondition(
renderScript.includes("cicd_repo='/root/hwlab-v02-cicd.git'") && !renderScript.includes("git -C /root/hwlab-v02") && !renderScript.includes("git checkout v0.2"),
"v0.2 control-plane render must not use the fixed workspace checkout or its clean status",
renderScript,
);
const existingPipelineRunReuse = v02ExistingPipelineRunReuseDecision({
sourceCommit: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
before: { exists: true, status: "True" },
latestSourceCommit: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
});
const existingPipelineRunFailed = v02ExistingPipelineRunReuseDecision({
sourceCommit: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
before: { exists: true, status: "False" },
latestSourceCommit: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
});
const existingPipelineRunHeadAdvanced = v02ExistingPipelineRunReuseDecision({
sourceCommit: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
before: { exists: true, status: "True" },
latestSourceCommit: "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
});
const existingPipelineRunHeadUnresolved = v02ExistingPipelineRunReuseDecision({
sourceCommit: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
before: { exists: true, status: "True" },
latestSourceCommit: null,
});
const triggerSnapshot = parseV02TriggerSnapshot({
ok: true,
command: ["test"],
exitCode: 0,
stdout: [
"sourceCommit\taaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"pipelineRun\thwlab-v02-ci-poll-aaaaaaaaaaaa",
"pipelineRunExitCode\t0",
"status\tTrue",
"reason\tCompleted",
"message\tTasks Completed: 9",
"pipelineRunStderr\t",
].join("\n"),
stderr: "",
parsed: null,
}, Date.parse("2026-06-04T16:00:00.000Z"));
assertCondition(
existingPipelineRunReuse.reusable === true
&& existingPipelineRunReuse.alreadyUsable === true
&& existingPipelineRunFailed.reusable === true
&& existingPipelineRunFailed.alreadyUsable === false
&& existingPipelineRunHeadAdvanced.reusable === false
&& existingPipelineRunHeadAdvanced.reason === "source-head-advanced-before-existing-pipelinerun-reuse"
&& existingPipelineRunHeadUnresolved.reusable === false
&& existingPipelineRunHeadUnresolved.reason === "source-head-recheck-unresolved-before-existing-pipelinerun-reuse",
"trigger-current must recheck latest v0.2 head before reusing an existing PipelineRun",
{ existingPipelineRunReuse, existingPipelineRunFailed, existingPipelineRunHeadAdvanced, existingPipelineRunHeadUnresolved },
);
assertCondition(
triggerSnapshot.sourceCommit === "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
&& triggerSnapshot.pipelineRun === "hwlab-v02-ci-poll-aaaaaaaaaaaa"
&& record(triggerSnapshot.before).status === "True",
"trigger-current fast snapshot must parse source head and PipelineRun status in one G14:k3s route while latest head is checked locally",
triggerSnapshot,
);
assertCondition(
!v02PipelineServiceIds().includes("hwlab-cli")
&& !v02PipelineServiceIds().includes("hwlab-agent-mgr")
&& !v02PipelineServiceIds().includes("hwlab-agent-worker"),
"v0.2 PipelineRun service matrix must exclude short-connection cli and removed HWLAB-owned code-agent control-plane services",
v02PipelineServiceIds(),
);
assertCondition(
sourceText.includes("curl -fsS --connect-timeout 2 --max-time 10")
&& !sourceText.includes("curl -fsS --connect-timeout 2 --max-time 5"),
"v0.2 web asset probes must not use a 5s app.js timeout that creates false deployment failures",
);
assertCondition(
sourceText.includes("printf 'probeElapsedMs\\\\t%s\\\\n'")
&& sourceText.includes("probeElapsedMs: numericField(fields.probeElapsedMs)"),
"v0.2 web asset probes must expose probeElapsedMs for timeout diagnostics",
);
assertCondition(
sourceText.includes("probe_start_s=$(date +%s")
&& !sourceText.includes("date +%s%3N"),
"v0.2 web asset elapsed timing must avoid non-portable date +%s%3N in k3s probe shells",
);
assertCondition(
sourceText.includes("if (args.includes(\"--status\")) return monitorStatus(options);")
&& sourceText.indexOf("if (args.includes(\"--status\")) return monitorStatus(options);") < sourceText.indexOf("const command = [\"bun\", \"scripts/cli.ts\", \"hwlab\", \"g14\", \"monitor-prs\""),
"monitor-prs --status must be a read-only query before async monitor startJob",
);
const staleSuccessAlignment = v02CommitAlignment({
expectedSourceHead: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
sourceHeads: {
cicdRepo: "/root/hwlab-v02-cicd.git",
cicdSourceHead: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
originHead: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
workspace: { path: "/root/hwlab-v02", head: "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", originHead: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", dirty: true, dirtyCount: 1 },
},
gitMirrorSummary: { localV02: "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", githubV02: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", sourceInSync: false, gitopsInSync: true },
pipelineRun: { pipelineRun: "hwlab-v02-ci-poll-aaaaaaaaaaaa", status: null },
recentPipelineRuns: { items: [{ name: "hwlab-v02-ci-poll-bbbbbbbbbbbb", sourceCommit: "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", status: "True", reason: "Completed" }] },
runtimeWorkloads: { items: [] },
webAssets: { apiRevision: "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" },
});
assertCondition(
staleSuccessAlignment.aligned === false
&& staleSuccessAlignment.state === "stale-success"
&& JSON.stringify(staleSuccessAlignment.staleReasons).includes("mirror-source-stale")
&& JSON.stringify(staleSuccessAlignment.workspaceWarnings).includes("workspace-dirty-but-isolated-from-cicd"),
"v0.2 commit alignment must call out stale-success while keeping dirty workspace isolated from CI source selection",
staleSuccessAlignment,
);
const latestOnlySuperseded = v02LatestOnlyTargetValidation({
targetMode: "source-commit",
sourceCommit: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
pipelineRun: { exists: true, status: "True", pipelineRun: "hwlab-v02-ci-poll-aaaaaaaaaaaa" },
commitAlignment: { staleReasons: ["origin-head-mismatch"], originHead: "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" },
targetValidation: {
ok: false,
state: "failed",
failures: [{ reason: "runtime-service-source-mismatch", serviceId: "hwlab-cloud-api" }],
},
});
assertCondition(
latestOnlySuperseded.ok === true
&& latestOnlySuperseded.state === "superseded"
&& latestOnlySuperseded.latestOnlySuperseded === true
&& Array.isArray(latestOnlySuperseded.failures)
&& latestOnlySuperseded.failures.length === 0
&& JSON.stringify(latestOnlySuperseded.supersededFailures).includes("runtime-service-source-mismatch"),
"v0.2 latest-only target validation must close a succeeded stale source commit as superseded instead of failing runtime mismatch",
latestOnlySuperseded,
);
const latestOnlyStalePassed = v02LatestOnlyTargetValidation({
targetMode: "pipeline-run",
sourceCommit: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
pipelineRun: { exists: true, status: "True", pipelineRun: "hwlab-v02-ci-poll-aaaaaaaaaaaa" },
commitAlignment: { staleReasons: ["latest-pipelinerun-not-current"], latestPipelineSourceCommit: "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" },
targetValidation: {
ok: true,
state: "passed",
failures: [],
},
});
assertCondition(
latestOnlyStalePassed.ok === true
&& latestOnlyStalePassed.state === "superseded"
&& latestOnlyStalePassed.latestOnlySuperseded === true
&& latestOnlyStalePassed.originalState === "passed"
&& Array.isArray(latestOnlyStalePassed.failures)
&& latestOnlyStalePassed.failures.length === 0,
"v0.2 latest-only target validation must mark stale successful historical runs as superseded instead of plain passed",
latestOnlyStalePassed,
);
const supersededCloseoutStatus = {
command: "hwlab g14 control-plane status --lane v02 --source-commit aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
sourceCommit: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
statusTarget: { mode: "source-commit", sourceCommit: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", pipelineRun: "hwlab-v02-ci-poll-aaaaaaaaaaaa" },
pipelineRun: { exists: true, pipelineRun: "hwlab-v02-ci-poll-aaaaaaaaaaaa", status: "True", reason: "Completed" },
targetValidation: { ok: true, state: "superseded", latestOnlySuperseded: true, latestOnlyReasons: ["origin-head-mismatch"], failures: [] },
sourceHeads: {
originHead: "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
target: {
sourceCommit: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
objectExists: true,
ancestorOfOriginHead: true,
ancestorExitCode: 0,
},
},
commitAlignment: { originHead: "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", staleReasons: ["origin-head-mismatch"] },
gitMirror: {
summary: {
pendingFlush: false,
githubInSync: true,
flushNeeded: false,
flushCommand: null,
lastFlush: { status: "flushed", at: "2026-06-04T00:00:00Z" },
},
},
argo: { fields: { syncStatus: "Synced", health: "Healthy", syncRevision: "gitopsbbbb", targetRevision: "v0.2-gitops", path: "deploy/gitops/g14/runtime-v02" } },
webAssets: { ok: true, summary: "19666/19667 React/Vite asset probes passed", apiRevision: "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" },
activePipelineRuns: [],
};
const supersededCloseout = v02CloseoutVerdict(supersededCloseoutStatus);
assertCondition(
supersededCloseout.ok === true
&& supersededCloseout.closeable === true
&& supersededCloseout.state === "superseded"
&& record(record(supersededCloseout.latestOnly).ancestorCheck).ancestorOfLatest === true
&& String(supersededCloseout.issueCommentMarkdown).includes("ancestorOfLatest=`true`")
&& String(record(supersededCloseout.recommendedNext).action) === "comment-and-close-issue",
"v0.2 closeout verdict must close historical successful runs only after ancestor, Argo, and git mirror checks pass",
supersededCloseout,
);
const unresolvedAncestorCloseout = v02CloseoutVerdict({
...supersededCloseoutStatus,
sourceHeads: { originHead: "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", target: { sourceCommit: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", objectExists: true, ancestorOfOriginHead: null } },
});
assertCondition(
unresolvedAncestorCloseout.closeable === false
&& unresolvedAncestorCloseout.state === "pending"
&& JSON.stringify(unresolvedAncestorCloseout.pendingReasons).includes("ancestor-check-unresolved"),
"v0.2 closeout verdict must not close superseded targets when ancestor evidence is missing",
unresolvedAncestorCloseout,
);
const pendingFlushCloseout = v02CloseoutVerdict({
...supersededCloseoutStatus,
targetValidation: {
ok: false,
state: "failed",
failures: [{ reason: "git-mirror-not-flushed", pendingFlush: true, githubInSync: false }],
},
gitMirror: {
summary: {
pendingFlush: true,
githubInSync: false,
flushNeeded: true,
flushCommand: "bun scripts/cli.ts hwlab g14 git-mirror flush --confirm",
},
},
});
assertCondition(
pendingFlushCloseout.closeable === false
&& pendingFlushCloseout.state === "pending"
&& String(record(pendingFlushCloseout.recommendedNext).action) === "flush-git-mirror"
&& String(record(pendingFlushCloseout.recommendedNext).command).includes("git-mirror flush --confirm --wait"),
"v0.2 closeout verdict must turn pendingFlush into an explicit controlled flush command",
pendingFlushCloseout,
);
const cdSummaryWithFields = summarizeV02CdStatus({
pipelineRun: { pipelineRun: "hwlab-v02-ci-poll-aaaaaaaaaaaa", status: "True", reason: "Completed" },
targetValidation: { state: "passed", ok: true, failures: [] },
gitMirror: { summary: { pendingFlush: false, githubInSync: true } },
argo: { fields: { syncStatus: "Synced", health: "Healthy" } },
webAssets: { ok: true },
planArtifacts: { buildServices: ["hwlab-cloud-api"], reusedServices: ["hwlab-cloud-web"], rolloutServices: ["hwlab-cloud-api"] },
});
assertCondition(
cdSummaryWithFields.argoSync === "Synced"
&& cdSummaryWithFields.argoHealth === "Healthy",
"v0.2 CD summary must read Argo sync/health from status.argo.fields",
cdSummaryWithFields,
);
const activeRunSummary = activeV02PipelineRuns({
activePipelineRuns: [
{ name: "hwlab-v02-ci-poll-aaaaaaaaaaaa", status: "Unknown" },
{ name: "hwlab-v02-ci-poll-bbbbbbbbbbbb", status: "True" },
],
});
assertCondition(
activeRunSummary.length === 1 && activeRunSummary[0]?.name === "hwlab-v02-ci-poll-aaaaaaaaaaaa",
"v0.2 active PipelineRun helper must accept the status.activePipelineRuns array shape",
activeRunSummary,
);
const falseGreenPassed = v02FalseGreenGuard({
sourceCommit: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
pipelineRun: { exists: true, status: "True" },
taskRuns: {
items: [
{ name: "hwlab-v02-ci-poll-aaaaaaaaaaaa-build-hwlab-cloud-api", status: "True" },
{ name: "hwlab-v02-ci-poll-aaaaaaaaaaaa-build-hwlab-cloud-web", status: "True" },
],
},
planArtifacts: {
ok: true,
buildServices: ["hwlab-cloud-api"],
reusedServices: ["hwlab-cloud-web"],
artifactProvenanceAudit: {
ok: true,
unsafeReuseServices: [],
},
},
runtimeWorkloads: {
ok: true,
items: [
{ serviceId: "hwlab-cloud-api", artifactSourceCommit: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" },
{ serviceId: "hwlab-cloud-web", artifactSourceCommit: "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" },
],
},
});
assertCondition(
falseGreenPassed.ok === true && falseGreenPassed.state === "passed",
"v0.2 false-green guard should pass when built runtime services and reuse provenance are proven",
falseGreenPassed,
);
const falseGreenReuseMissingAudit = v02FalseGreenGuard({
sourceCommit: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
pipelineRun: { exists: true, status: "True" },
taskRuns: { items: [{ name: "hwlab-v02-ci-poll-aaaaaaaaaaaa-build-hwlab-cloud-api", status: "True" }] },
planArtifacts: {
ok: true,
buildServices: ["hwlab-cloud-api"],
reusedServices: ["hwlab-cloud-web"],
artifactProvenanceAudit: null,
},
runtimeWorkloads: { ok: true, items: [{ serviceId: "hwlab-cloud-api", artifactSourceCommit: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" }] },
});
assertCondition(
falseGreenReuseMissingAudit.ok === true
&& JSON.stringify(falseGreenReuseMissingAudit.provenanceWarnings).includes("artifact-provenance-audit-missing-for-reuse"),
"v0.2 false-green guard should not fail a completed rollout only because optional reuse provenance is missing",
falseGreenReuseMissingAudit,
);
const falseGreenRuntimeMismatch = v02FalseGreenGuard({
sourceCommit: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
pipelineRun: { exists: true, status: "True" },
taskRuns: { items: [{ name: "hwlab-v02-ci-poll-aaaaaaaaaaaa-build-hwlab-cloud-api", status: "True" }] },
planArtifacts: {
ok: true,
buildServices: ["hwlab-cloud-api"],
reusedServices: [],
artifactProvenanceAudit: null,
},
runtimeWorkloads: { ok: true, items: [{ serviceId: "hwlab-cloud-api", artifactSourceCommit: "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" }] },
});
assertCondition(
falseGreenRuntimeMismatch.ok === false
&& JSON.stringify(falseGreenRuntimeMismatch.failures).includes("artifact-source-commit-mismatch"),
"v0.2 false-green guard must fail when a built service still runs an old artifact",
falseGreenRuntimeMismatch,
);
const slowBuildSummary = v02TaskRunPerformanceSummary([
{
name: "hwlab-v02-ci-poll-f8a090b66616-build-hwlab-agent-worker",
status: "True",
reason: "Succeeded",
durationSeconds: 234,
},
{
name: "hwlab-v02-ci-poll-f8a090b66616-build-hwlab-cloud-web",
status: "True",
reason: "Succeeded",
durationSeconds: 37,
},
]);
const slowBuildItems = Array.isArray(record(slowBuildSummary).slowTaskRuns) ? record(slowBuildSummary).slowTaskRuns as Record<string, unknown>[] : [];
assertCondition(
slowBuildSummary.ok === false
&& record(record(slowBuildSummary).thresholds).buildTaskRunWarningSeconds === 120
&& slowBuildItems.length === 1
&& slowBuildItems[0]?.serviceId === "hwlab-agent-worker"
&& slowBuildItems[0]?.severity === "critical",
"v0.2 status must warn on slow build TaskRuns like issue #659",
slowBuildSummary,
);
const prBody = [
"## 背景",
"",
"G14 DEV Cloud Workbench 中,内部 `message-trace-body` 的 `pre` 会在 trace 更新时被替换,导致用户滚到中间后被重置到顶部。",
"",
"## 修改",
"",
"- trace row patch 从 index/replace 改为 stable `data-trace-row-id` keyed reconciliation。",
"- 已存在 row 只移动和原地更新 header/body,不替换 `li/pre`。",
"- `message-trace-body` 内部滚动状态按 `traceUiKey + rowId` 记忆。",
].join("\n");
const semanticBullets = semanticChangelogBullets("fix: preserve inner trace scroll", prBody, {
keyFiles: [
"modified web/hwlab-cloud-web/app.mjs",
"modified web/hwlab-cloud-web/scripts/trace-scroll.test.mjs",
],
});
assertCondition(
semanticBullets.some((line) => line.includes("修复目标") && line.includes("滚到中间后被重置到顶部")),
"semantic changelog should explain the user-visible bug being fixed",
semanticBullets,
);
assertCondition(
semanticBullets.some((line) => line.includes("data-trace-row-id")),
"semantic changelog should include PR body change bullets instead of only file stats",
semanticBullets,
);
const summaryBullets = semanticChangelogBullets("feat: preload gateway tran helper", [
"## 摘要",
"- 新增 `/app/tools/hwlab-gateway-tran.mjs`,支持 `cmd`、`ps`、`upload`、`download`。",
].join("\n"), {});
assertCondition(
summaryBullets.some((line) => line.includes("hwlab-gateway-tran.mjs") && line.includes("upload")),
"semantic changelog should also extract Chinese summary sections",
summaryBullets,
);
const rolloutBody = rolloutRecordBody({
pr: { number: 506, title: "fix: preserve inner trace scroll", url: "https://github.com/pikasTech/HWLAB/pull/506" },
prData: { json: { title: "fix: preserve inner trace scroll", body: prBody, url: "https://github.com/pikasTech/HWLAB/pull/506" } },
fileSummary: {
summary: { files: 2, additions: 268, deletions: 22, commits: 1 },
keyFiles: [
"modified web/hwlab-cloud-web/app.mjs",
"modified web/hwlab-cloud-web/scripts/trace-scroll.test.mjs",
],
},
sourceCommit: "1a3fa9e6fbc987142463ecff2cbcef240a6278f2",
pipelineRun: "hwlab-g14-ci-poll-1a3fa9e6fbc9",
gitopsRevision: "21462b78ce4e7dba4ea374398f60db690e290147",
mergedAt: "2026-05-27T06:52:22Z",
pipelineSucceededAt: "2026-05-27T06:55:38Z",
finishedAt: "2026-05-27T06:55:47Z",
rollout: { pipelineText: "True\nSucceeded", argoText: "21462\nSynced\nHealthy\nSucceeded\n21462", healthOk: true },
ciMetrics: parsePipelineTaskRunMetrics("hwlab-g14-ci-poll-test", [
"taskrun-a\tbuild-hwlab-cloud-api\t2026-05-27T06:54:43Z\t2026-05-27T06:54:52Z\tservice-id=hwlab-cloud-api;status=reused;build-backend=reused-catalog;",
"taskrun-b\tbuild-hwlab-cloud-web\t2026-05-27T06:54:43Z\t2026-05-27T06:55:08Z\tservice-id=hwlab-cloud-web;status=published;build-backend=buildkit;",
].join("\n")),
});
assertCondition(
rolloutBody.includes("- 上线 changelog(语义化):"),
"rollout record must label natural-language changelog separately",
);
assertCondition(
rolloutBody.includes("- 自动 diff 摘要:"),
"rollout record must keep automatic diff summary separately",
);
assertCondition(
rolloutBody.indexOf("- 上线 changelog(语义化):") < rolloutBody.indexOf("- 自动 diff 摘要:"),
"semantic changelog should appear before automatic diff summary",
);
assertCondition(
rolloutBody.includes("changed files: 2; +268 / -22; commits: 1"),
"automatic diff summary should preserve file/stat evidence",
);
assertCondition(
rolloutBody.includes("lazy build reused: 1/2"),
"rollout record should include lazy-build reused ratio",
rolloutBody,
);
assertCondition(
rolloutBody.includes("reused services: hwlab-cloud-api") && rolloutBody.includes("rebuild services: hwlab-cloud-web"),
"rollout record should list reused and rebuild services",
rolloutBody,
);
assertCondition(
rolloutBody.includes("hwlab-cloud-api: reused, 9s") && rolloutBody.includes("hwlab-cloud-web: published, 25s"),
"rollout record should include each service duration",
rolloutBody,
);
console.log(JSON.stringify({
ok: true,
checks: [
"long-running monitor owns latest-monitor-job.json",
"once jobs do not overwrite long-running monitor state",
"dry-run jobs do not overwrite live monitor state",
"once dry-run jobs have a distinct diagnostic state file",
"git mirror sync is a manual devops-infra Job, not a CronJob",
"git mirror flush is a manual devops-infra Job, not a CronJob",
"trigger-current can decide whether v0.2 git mirror pre-sync is required",
"v0.2 git mirror pre-sync marker only reuses fresh same-commit post-observation markers",
"git mirror status exposes source and gitops GitHub sync state plus controlled flush command",
"v0.2 control-plane status and closeout expose targeted PipelineRun/source-commit inspection",
"v0.2 control-plane render uses an isolated temp clone from a CI/CD dedicated bare repo",
"v0.2 control-plane refresh marker only reuses recent same-contract refreshes",
"trigger-current rechecks latest v0.2 head before reusing an existing PipelineRun",
"trigger-current fast snapshot parses source head and PipelineRun status in one G14:k3s route while latest head is checked locally",
"v0.2 status alignment reports stale-success without coupling CI to dirty workspace state",
"v0.2 closeout verdicts distinguish passed, superseded, pending flush, and unresolved ancestor states",
"v0.2 CD summary and active run helpers expose Argo fields plus concurrent PipelineRun visibility",
"v0.2 PipelineRun service matrix excludes hwlab-cli",
"v0.2 false-green guard checks build TaskRuns, runtime artifact source commits, and reuse provenance",
"v0.2 status warns on slow build TaskRuns",
"rollout brief includes natural-language changelog before automatic diff summary",
"semantic changelog extracts Chinese summary sections",
"rollout brief includes lazy-build reused/rebuild metrics and service durations",
],
}, null, 2));