import { gitMirrorFlushJobManifest, gitMirrorStatusSummary, gitMirrorSyncJobManifest, gitMirrorV02SyncRequirement, hwlabG14Help, hwlabG14MonitorStateFileName, parseGitMirrorStatusRefs, parsePipelineTaskRunMetrics, parseV02TriggerSnapshot, rolloutRecordBody, semanticChangelogBullets, 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 { return typeof value === "object" && value !== null && !Array.isArray(value) ? value as Record : {}; } 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")), "v0.2 control-plane status help must expose targeted PipelineRun/source-commit 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(), ); 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"), "v0.2 PipelineRun service matrix must not build hwlab-cli because cli is short-connection source tool", 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 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[] : []; 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 help exposes 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 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));