import { mkdirSync, writeFileSync } from "node:fs"; import path from "node:path"; import { parseDeployObservationCliArgs, parseGlobalReviewCliArgs, parseWorkstreamCloseoutCliArgs, } from "./core/cli.js"; import { registeredToolNames } from "./core/constants.js"; import { buildBlockerResolution } from "./tools/blocker.js"; import { buildChangeDocSync } from "./tools/docSync.js"; import { resolveInsideRoot } from "./core/workspace.js"; import { hasEntityLoadTimeSerializationMutation } from "./tools/envRisk.js"; import { buildGuardrails } from "./tools/guardrails.js"; import { readObsidianContext, syncObsidianWorkflow, validateObsidianVault, } from "./tools/obsidian.js"; import { cleanupHistoryArtifacts, parseCliCleanupArgs, } from "./tools/cleanup.js"; import { buildWorkflowLoopAudit } from "./tools/loop.js"; import { inspectProject } from "./tools/inspect.js"; import { prepareTask, readWorkflowContext } from "./tools/task.js"; import { buildGlobalCodeReview, findNapcatImageGovernanceFindings, findQqbotNapcatCaptchaFlowFindings, findQqbotCommandServiceTestImportFindings, findQqbotStatusBoundaryFindings, findTaskRecordGovernanceFindings, isBenignCredentialReviewValue, } from "./tools/review.js"; import { buildBusinessTestPlan } from "./tools/testing.js"; import { buildVerificationPlan } from "./tools/verification.js"; import { buildWorkstreamCloseout } from "./tools/closeout.js"; import { buildDeployObservation, buildDeployObservationSectionMap, normalizeDeployObservationEvidence, } from "./tools/deployObservation.js"; import { buildCommitPlan, buildComponentWorkflow, buildDbSyncPlan, buildFinishTask, buildPushPlan, buildRemoteHealthCheck, } from "./tools/workflow.js"; export async function runSelfTest(): Promise { const reviewClassifier = { localizedSecretLabelAllowed: isBenignCredentialReviewValue("密钥"), realTokenRejected: !isBenignCredentialReviewValue( "eqc2Sbp8ufP2AcS6I2YReLqxnu3XJKC6D7C6PUcx", ), testTokenAllowed: isBenignCredentialReviewValue("token-test"), }; if ( !reviewClassifier.localizedSecretLabelAllowed || !reviewClassifier.realTokenRejected || !reviewClassifier.testTokenAllowed ) { throw new Error("review credential classifier self-check failed"); } const taskRecordFindings = findTaskRecordGovernanceFindings([ "## 最近记录", "### 2026-06-10:长记录污染", "- 范围:root", "- 关键词:TASKS 治理", "- 页面测试用例:配置类任务,无页面测试。", "- 验证:global-review", "## 历史索引", ]); if ( !taskRecordFindings.some( (item) => item.category === "tasks-record-extra-field", ) ) { throw new Error("TASKS record governance self-check failed"); } const napcatImageFindings = findNapcatImageGovernanceFindings( ["QQBOT_NAPCAT_IMAGE=mlikiowa/napcat-docker:latest"], { file: ".env.example", project: "Node/kt-template-online-api", }, ); if ( !napcatImageFindings.some((item) => item.category === "napcat-latest-image") ) { throw new Error("NapCat image governance self-check failed"); } const qqbotCommandServiceImportFindings = findQqbotCommandServiceTestImportFindings( [ "import { QqbotCommandService } from '@/qqbot/command/qqbot-command.service';", ], { file: "test/qqbot/command/qqbot-command-cooldown.service.spec.ts", project: "Node/kt-template-online-api", }, ); if ( !qqbotCommandServiceImportFindings.some( (item) => item.category === "qqbot-command-service-heavy-test-import", ) ) { throw new Error( "QQBot command service test import governance self-check failed", ); } const qqbotStatusBoundaryFindings = [ ...findQqbotStatusBoundaryFindings( [ "async markOnline(", " selfId: string,", " clientRole: QqbotConnectionRole,", " lastError: null | string = null,", ") {}", "if (account.connectStatus === 'online') return 'online';", "return this.toTime(account.lastHeartbeatAt) > checkedAt;", ], { file: "src/qqbot/account/qqbot-account.service.ts", project: "Node/kt-template-online-api", }, ), ...findQqbotStatusBoundaryFindings( [ "ws.on('close', async () => {", " this.connections.delete(key);", " await this.accountService.markOffline(context.selfId);", "});", ], { file: "src/qqbot/connection/qqbot-reverse-ws.service.ts", project: "Node/kt-template-online-api", }, ), ]; for (const category of [ "qqbot-mark-online-clears-login-error", "qqbot-connect-status-drives-login-status", "qqbot-heartbeat-bypasses-login-check", "qqbot-stale-ws-close-marks-offline", ]) { if (!qqbotStatusBoundaryFindings.some((item) => item.category === category)) { throw new Error(`QQBot status boundary self-check failed: ${category}`); } } const qqbotCaptchaFlowFindings = findQqbotNapcatCaptchaFlowFindings( [ "private async resolvePasswordCaptchaUrl(container, loginStatus, sinceMs) {", " return await this.containerService.detectRuntimeCaptchaUrl(container, sinceMs);", " return await this.waitForPasswordCaptchaUrl(container, sinceMs);", "}", "private async waitForPasswordCaptchaUrl(container, sinceMs) {", " await this.toolsService.sleep(this.getLoginPollIntervalMs());", " return await this.detectPasswordCaptchaUrl(container, sinceMs, true);", "}", "private async waitForPasswordLoginStatus(container) {", " latestStatus = await this.getLoginStatus(container, true);", "}", "async status(sessionId) {", " if (session.captchaUrl) {", " return this.failCaptchaLogin(session, container, status.loginError);", " }", "}", "if (captchaResult?.needNewDevice && captchaResult.jumpUrl) {", " return this.keepSessionPending(session, '请在 NapCat WebUI 继续完成', true);", "}", "private async tryPasswordRelogin(session, container) {", " await this.containerService.ensureRuntimeLoginEnv(container, { loginPassword: 'x' });", " session.lastRestartedAt = Date.now();", " loginStatus = await this.waitForPasswordLoginStatus(container, session.lastRestartedAt);", " await this.resolvePasswordCaptchaUrl(container, loginStatus, session.lastRestartedAt);", "}", ], { file: "src/qqbot/account/qqbot-napcat-login.service.ts", project: "Node/kt-template-online-api", }, ); for (const category of [ "qqbot-napcat-captcha-error-throws-before-pending", "qqbot-napcat-captcha-log-single-read", "qqbot-napcat-captcha-status-before-log-url", "qqbot-napcat-captcha-status-clears-pending", "qqbot-napcat-captcha-log-window-late", "qqbot-napcat-new-device-drops-pending", ]) { if (!qqbotCaptchaFlowFindings.some((item) => item.category === category)) { throw new Error(`QQBot captcha flow self-check failed: ${category}`); } } const qqbotCaptchaFlowValidFindings = findQqbotNapcatCaptchaFlowFindings( [ "private async resolvePasswordCaptchaUrl(container, loginStatus, sinceMs) {", " await this.detectPasswordCaptchaUrl(container, sinceMs, false);", " await this.waitForPasswordCaptchaUrl(container, sinceMs);", "}", "private async waitForPasswordLoginStatus(container, sinceMs) {", " if (this.toolsService.isNapcatCaptchaRequiredMessage(errorMessage)) {", " await this.detectPasswordCaptchaUrl(container, sinceMs);", " }", "}", "async status(sessionId) {", " if (session.captchaUrl) {", " if (this.isPasswordCaptchaStillRequired(status)) {", " return this.keepPasswordCaptchaPending(session, session.captchaUrl, status.loginError);", " }", " }", "}", "if (captchaResult?.needNewDevice && captchaResult.jumpUrl) {", " return this.keepDeviceVerifyPending(session, captchaResult.jumpUrl, '验证码已通过');", "}", "private keepDeviceVerifyPending(session, deviceVerifyUrl) {", " session.deviceVerifyUrl = deviceVerifyUrl;", " session.captchaUrl = undefined;", "}", "private async detectPasswordCaptchaUrl(container, sinceMs, allowTailFallback = true) {", " await this.containerService.detectRuntimeCaptchaUrl(container, sinceMs);", " await this.containerService.detectRuntimeCaptchaUrl(container);", "}", "private async waitForPasswordCaptchaUrl(container, sinceMs) {", " const attempts = 5;", " for (let index = 0; index < attempts; index += 1) {", " await this.toolsService.sleep(this.getLoginPollIntervalMs());", " await this.detectPasswordCaptchaUrl(container, sinceMs, true);", " }", "}", "private async tryPasswordRelogin(session, container) {", " const passwordLogSinceMs = Date.now();", " await this.containerService.ensureRuntimeLoginEnv(container, { loginPassword: 'x' });", " loginStatus = await this.waitForPasswordLoginStatus(container, passwordLogSinceMs);", " await this.resolvePasswordCaptchaUrl(container, loginStatus, passwordLogSinceMs);", "}", ], { file: "src/qqbot/account/qqbot-napcat-login.service.ts", project: "Node/kt-template-online-api", }, ); if ( qqbotCaptchaFlowValidFindings.some( (item) => item.category === "qqbot-napcat-captcha-log-window-late" || item.category === "qqbot-napcat-captcha-status-before-log-url" || item.category === "qqbot-napcat-captcha-status-clears-pending", ) ) { throw new Error("QQBot captcha flow valid self-check produced false positive"); } const qqbotCaptchaLogTimeoutFindings = findQqbotNapcatCaptchaFlowFindings( [ "async detectRuntimeCaptchaUrl(runtime, sinceMs) {", " const result = await this.runProcess(", " 'ssh',", " [...this.getSshArgs(), 'sh -s'],", " script,", " undefined,", " this.getRuntimeCheckTimeoutMs(),", " );", "}", ], { file: "src/qqbot/napcat/qqbot-napcat-container.service.ts", project: "Node/kt-template-online-api", }, ); if ( !qqbotCaptchaLogTimeoutFindings.some( (item) => item.category === "qqbot-napcat-captcha-log-timeout-too-short", ) ) { throw new Error("QQBot captcha log timeout self-check failed"); } const qqbotCaptchaLogTimeoutValidFindings = findQqbotNapcatCaptchaFlowFindings( [ "async detectRuntimeCaptchaUrl(runtime, sinceMs) {", " const result = await this.runProcess(", " 'ssh',", " [...this.getSshArgs(), 'sh -s'],", " script,", " undefined,", " this.getCaptchaLogReadTimeoutMs(),", " );", "}", ], { file: "src/qqbot/napcat/qqbot-napcat-container.service.ts", project: "Node/kt-template-online-api", }, ); if (qqbotCaptchaLogTimeoutValidFindings.length) { throw new Error("QQBot captcha log timeout valid self-check false positive"); } const reviewCliParser = { dashedAll: parseGlobalReviewCliArgs([ "node", "server", "--global-review", "--content-scan-all", ]).contentScanMode, keyValueAll: parseGlobalReviewCliArgs([ "node", "server", "--global-review", "--contentScanMode=all", ]).contentScanMode, splitAll: parseGlobalReviewCliArgs([ "node", "server", "--global-review", "--content-scan-mode", "all", ]).contentScanMode, }; if ( reviewCliParser.dashedAll !== "all" || reviewCliParser.keyValueAll !== "all" || reviewCliParser.splitAll !== "all" ) { throw new Error( "global-review CLI content scan mode parser self-check failed", ); } const closeoutCliParser = parseWorkstreamCloseoutCliArgs([ "node", "server", "--workstream-closeout", "--review", "global-review findings=0", "--superpowers-review", "Superpowers reviewer completed", ]); if ( closeoutCliParser.reviewEvidence?.[0] !== "global-review findings=0" || closeoutCliParser.superpowersReviewEvidence?.[0] !== "Superpowers reviewer completed" ) { throw new Error("workstream closeout review CLI parser self-check failed"); } const deployObservationInput = parseDeployObservationCliArgs([ "node", "server", "--deploy-observation", "--project", "api", "--job", "kt-template-online-api", "--build", "132", "--commit", "abc1234", "--image-tag", "abc1234", "--namespace", "kt-prod", "--deployment", "kt-template-online-api", "--container", "api", "--health-url", "http://127.0.0.1:48085/health/runtime", "--smoke", "curl -fsS --max-time 8 http://127.0.0.1:48085/health/runtime", ]); if ( deployObservationInput.project !== "api" || deployObservationInput.jobName !== "kt-template-online-api" || deployObservationInput.buildNumber !== "132" || deployObservationInput.expectedCommit !== "abc1234" || deployObservationInput.imageTag !== "abc1234" || deployObservationInput.namespace !== "kt-prod" || deployObservationInput.deployment !== "kt-template-online-api" || deployObservationInput.container !== "api" || deployObservationInput.healthUrl !== "http://127.0.0.1:48085/health/runtime" || deployObservationInput.smoke !== "curl -fsS --max-time 8 http://127.0.0.1:48085/health/runtime" || deployObservationInput.execute !== false ) { throw new Error("deploy observation CLI parser self-check failed"); } const buildDeployObservationTestSections = (overrides: { desiredReplicas?: number; healthStatus?: string; jenkinsBuildXml?: string; logCommit?: string; omitSmoke?: boolean; readyReplicas?: number; smokeExitCode?: number; containerName?: string; podContainerName?: string; updatedReplicas?: number; } = {}) => buildDeployObservationSectionMap([ "__KT_SECTION:jenkins_meta__", "buildNumber=132", "result=SUCCESS", "__KT_SECTION:jenkins_log_tail__", overrides.logCommit === "" ? "Finished: SUCCESS" : `Checking out Revision ${overrides.logCommit ?? "abc1234"}`, "Finished: SUCCESS", "__KT_SECTION:jenkins_build_xml__", overrides.jenkinsBuildXml ?? "", "__KT_SECTION:deployment_json__", JSON.stringify({ metadata: { generation: 12 }, spec: { replicas: overrides.desiredReplicas ?? 1, template: { spec: { containers: [ { image: "k3d-kt-registry.localhost:5000/kt-template-online-api:abc1234", name: overrides.containerName ?? "api", }, ], }, }, }, status: { observedGeneration: 12, readyReplicas: overrides.readyReplicas ?? 1, updatedReplicas: overrides.updatedReplicas ?? 1, }, }), "__KT_SECTION:pods_json__", JSON.stringify({ items: [ { metadata: { name: "kt-template-online-api-abc", }, spec: { containers: [ { image: "k3d-kt-registry.localhost:5000/kt-template-online-api:abc1234", name: overrides.podContainerName ?? "api", }, ], }, status: { containerStatuses: [ { image: "k3d-kt-registry.localhost:5000/kt-template-online-api:abc1234", name: overrides.podContainerName ?? "api", ready: true, restartCount: 0, }, ], phase: "Running", startTime: "2026-06-14T00:00:00Z", }, }, ], }), "__KT_SECTION:health_json__", JSON.stringify({ checkedAt: "2026-06-14T00:00:01.000Z", checks: [ { critical: true, message: "NestJS process answered runtime health request", name: "process", status: "live", }, ], service: "kt-template-online-api", status: overrides.healthStatus ?? "ready", }), "__KT_SECTION:smoke_text__", overrides.omitSmoke ? "no task smoke command provided" : '{"service":"kt-template-online-api","status":"ready"}', "__KT_SECTION:smoke_status__", overrides.omitSmoke ? "exitCode=" : `exitCode=${overrides.smokeExitCode ?? 0}`, ].join("\n")); const deployObservationSections = buildDeployObservationTestSections(); const deployObservationEvidence = normalizeDeployObservationEvidence({ checkedAt: new Date("2026-06-14T00:00:02.000Z"), input: deployObservationInput, sectionMap: deployObservationSections, }); const deployObservationSmoke = deployObservationEvidence.details.smoke as { exitCode?: number | null; success?: boolean | null; }; if ( deployObservationEvidence.status !== "passed" || deployObservationEvidence.details.deployment.image !== "k3d-kt-registry.localhost:5000/kt-template-online-api:abc1234" || deployObservationEvidence.details.deployment.observedGeneration !== 12 || deployObservationEvidence.details.pod.name !== "kt-template-online-api-abc" || deployObservationEvidence.details.pod.restartCount !== 0 || deployObservationEvidence.details.runtimeHealth.status !== "ready" || deployObservationSmoke.exitCode !== 0 || deployObservationSmoke.success !== true || !deployObservationEvidence.assertions.every((item) => item.passed) ) { throw new Error("deploy observation evidence normalization self-check failed"); } const deployObservationDegradedHealthEvidence = normalizeDeployObservationEvidence({ checkedAt: new Date("2026-06-14T00:00:02.000Z"), input: deployObservationInput, sectionMap: buildDeployObservationTestSections({ healthStatus: "degraded", }), }); if ( deployObservationDegradedHealthEvidence.status !== "passed" || !deployObservationDegradedHealthEvidence.assertions.every( (item) => item.passed, ) ) { throw new Error("deploy observation degraded health self-check failed"); } const deployObservationBuildXmlCommitEvidence = normalizeDeployObservationEvidence({ checkedAt: new Date("2026-06-14T00:00:02.000Z"), input: deployObservationInput, sectionMap: buildDeployObservationTestSections({ jenkinsBuildXml: [ "", "", "abc1234", "", "", ].join("\n"), logCommit: "", }), }); if ( deployObservationBuildXmlCommitEvidence.status !== "passed" || deployObservationBuildXmlCommitEvidence.details.jenkins.commitMatched !== true ) { throw new Error("deploy observation build.xml commit self-check failed"); } const deployObservationFailedSmokeEvidence = normalizeDeployObservationEvidence({ checkedAt: new Date("2026-06-14T00:00:02.000Z"), input: deployObservationInput, sectionMap: buildDeployObservationTestSections({ smokeExitCode: 7, }), }); const deployObservationFailedSmoke = deployObservationFailedSmokeEvidence.details.smoke as { exitCode?: number | null; success?: boolean | null; }; if ( deployObservationFailedSmokeEvidence.status === "passed" || deployObservationFailedSmoke.exitCode !== 7 || deployObservationFailedSmoke.success !== false ) { throw new Error("deploy observation failed smoke self-check failed"); } const deployObservationReplicaLagEvidence = normalizeDeployObservationEvidence({ checkedAt: new Date("2026-06-14T00:00:02.000Z"), input: deployObservationInput, sectionMap: buildDeployObservationTestSections({ desiredReplicas: 2, readyReplicas: 1, updatedReplicas: 2, }), }); if (deployObservationReplicaLagEvidence.status === "passed") { throw new Error("deploy observation replica lag self-check failed"); } const deployObservationDesiredReplicaLagEvidence = normalizeDeployObservationEvidence({ checkedAt: new Date("2026-06-14T00:00:02.000Z"), input: deployObservationInput, sectionMap: buildDeployObservationTestSections({ desiredReplicas: 2, readyReplicas: 1, updatedReplicas: 1, }), }); if (deployObservationDesiredReplicaLagEvidence.status === "passed") { throw new Error("deploy observation desired replica lag self-check failed"); } const deployObservationWrongContainerEvidence = normalizeDeployObservationEvidence({ checkedAt: new Date("2026-06-14T00:00:02.000Z"), input: deployObservationInput, sectionMap: buildDeployObservationTestSections({ containerName: "sidecar", podContainerName: "sidecar", }), }); if (deployObservationWrongContainerEvidence.status === "passed") { throw new Error("deploy observation wrong container self-check failed"); } const deployObservationNoSmokeEvidence = normalizeDeployObservationEvidence({ checkedAt: new Date("2026-06-14T00:00:02.000Z"), input: { ...deployObservationInput, smoke: undefined }, sectionMap: buildDeployObservationTestSections({ omitSmoke: true, }), }); const deployObservationNoSmoke = deployObservationNoSmokeEvidence.details .smoke as { exitCode?: number | null; success?: boolean | null; }; if ( deployObservationNoSmokeEvidence.status === "passed" || deployObservationNoSmoke.exitCode !== null || deployObservationNoSmoke.success !== false ) { throw new Error("deploy observation no-smoke self-check failed"); } const deployObservationDryRun = await buildDeployObservation({ deployment: "kt-template-online-api", execute: false, imageTag: "abc1234", jobName: "kt-template-online-api", namespace: "kt-prod", project: "api", smoke: "curl -fsS --max-time 8 http://127.0.0.1:48085/health/runtime", }); const deployObservationCommandText = deployObservationDryRun.commands .map((item) => item.command) .join("\n"); if ( deployObservationDryRun.execute !== false || !deployObservationCommandText.includes("tr -d '\\015' | bash -s") || !deployObservationCommandText.includes("kubectl") || !deployObservationCommandText.includes( "http://127.0.0.1:48085/health/runtime", ) || !deployObservationCommandText.includes("curl -fsS --max-time 8") || deployObservationDryRun.evidence ) { throw new Error("deploy observation dry-run self-check failed"); } for (const expected of [ "KUBECONFIG_ACTIVE", "docker port k3d-kt-nas-serverlb 6443/tcp", "/tmp/kt-deploy-observation-kubeconfig-$$.yaml", "127.0.0.1:${K3D_API_PORT}", 'rm -f "$KUBECONFIG_TEMP"', 'kubectl --kubeconfig "$KUBECONFIG_ACTIVE"', "section jenkins_build_xml", ]) { if (!deployObservationCommandText.includes(expected)) { throw new Error(`deploy observation kubeconfig self-check failed: ${expected}`); } } if ( deployObservationCommandText.includes('sed -E "s#.*:([0-9]+)$#') || !deployObservationCommandText.includes("rev | cut -d: -f1 | rev") ) { throw new Error("deploy observation docker port parser self-check failed"); } const deployObservationDefaultDryRun = await buildDeployObservation({ deployment: "kt-template-online-api", execute: false, namespace: "kt-prod", project: "api", }); const deployObservationDefaultCommandText = deployObservationDefaultDryRun.commands.map((item) => item.command).join("\n"); if ( !deployObservationDefaultCommandText.includes( "/vol1/docker/jenkins/jenkins_home", ) || !deployObservationDefaultCommandText.includes( "JOB_NAME='KT-Template/KT-Template-API/main'", ) ) { throw new Error("deploy observation Jenkins defaults self-check failed"); } for (const expected of [ 'JOB_DIR="$JENKINS_HOME/jobs/$JOB_NAME/builds"', 'jobs/$JOB_FOLDER/jobs/$JOB_PIPELINE/branches/$JOB_BRANCH/builds', 'find "$JENKINS_HOME/jobs"', ]) { if (!deployObservationDefaultCommandText.includes(expected)) { throw new Error( `deploy observation Jenkins path self-check failed: ${expected}`, ); } } const deployObservationExplicitJenkinsDryRun = await buildDeployObservation({ execute: false, jenkinsHome: "/custom/jenkins", jobName: "custom-job", }); const deployObservationExplicitJenkinsCommandText = deployObservationExplicitJenkinsDryRun.commands .map((item) => item.command) .join("\n"); if ( !deployObservationExplicitJenkinsCommandText.includes( "JENKINS_HOME='/custom/jenkins'", ) || !deployObservationExplicitJenkinsCommandText.includes("JOB_NAME='custom-job'") ) { throw new Error("deploy observation explicit Jenkins input self-check failed"); } const deployObservationExplicitHealthDryRun = await buildDeployObservation({ deployment: "kt-template-online-api", execute: false, healthUrl: "http://127.0.0.1:30085/health/runtime", imageTag: "abc1234", jobName: "kt-template-online-api", namespace: "kt-prod", project: "api", }); const deployObservationExplicitHealthCommandText = deployObservationExplicitHealthDryRun.commands .map((item) => item.command) .join("\n"); if ( !deployObservationExplicitHealthCommandText.includes( "http://127.0.0.1:30085/health/runtime", ) ) { throw new Error("deploy observation explicit health URL self-check failed"); } const deployObservationUnsafeProjectDryRun = await buildDeployObservation({ deployment: "kt-template-online-api", execute: false, healthUrl: "http://127.0.0.1:48085/health/runtime", imageTag: "abc1234", jobName: "kt-template-online-api", namespace: "kt-prod", project: "../escape", smoke: "curl -fsS --max-time 8 http://127.0.0.1:48085/health/runtime", }); if ( deployObservationUnsafeProjectDryRun.execute !== false || deployObservationUnsafeProjectDryRun.artifactPath || deployObservationUnsafeProjectDryRun.evidence || JSON.stringify(deployObservationUnsafeProjectDryRun.commands).includes( "../escape", ) ) { throw new Error("deploy observation project dry-run safety self-check failed"); } let unsupportedSmokeRejected = false; try { await buildDeployObservation({ execute: false, smoke: "curl -fsS --max-time 8 http://127.0.0.1:48085/health/runtime; rm -rf /", }); } catch (error) { unsupportedSmokeRejected = String(error).includes( "Unsupported deploy observation smoke command", ); } if (!unsupportedSmokeRejected) { throw new Error("deploy observation unsafe smoke self-check failed"); } let unsafeHealthUrlRejected = false; try { await buildDeployObservation({ execute: false, healthUrl: "http://user:pass@127.0.0.1:48085/health/runtime", }); } catch (error) { unsafeHealthUrlRejected = String(error).includes( "Unsupported deploy observation healthUrl", ); } if (!unsafeHealthUrlRejected) { throw new Error("deploy observation unsafe health URL self-check failed"); } let unsafeSshTargetRejected = false; try { await buildDeployObservation({ execute: true, sshTarget: "root@unexpected-host.example", }); } catch (error) { unsafeSshTargetRejected = String(error).includes( 'Unsafe deploy observation input "sshTarget"', ); } if (!unsafeSshTargetRejected) { throw new Error("deploy observation unsafe ssh target self-check failed"); } let unsafeInputRejected = false; try { await buildDeployObservation({ execute: false, jobName: "bad\njob", }); } catch (error) { unsafeInputRejected = String(error).includes( "Unsafe deploy observation input", ); } if (!unsafeInputRejected) { throw new Error("deploy observation unsafe input self-check failed"); } const refactorGuardrails = buildGuardrails({ project: "mcp", taskType: "refactor", userRequest: "重构 KT 工作流护栏", }); const refactorGuardrailOk = refactorGuardrails.guardrails.beforeEdit.some((item) => item.includes("重构前写清楚"), ) && refactorGuardrails.guardrails.verification.notes.some((item) => item.includes("重构验证必须覆盖原行为"), ); if (!refactorGuardrailOk) { throw new Error("refactor guardrail self-check failed"); } const apiVerification = buildVerificationPlan({ changeType: "api", project: "api", }); const apiVerificationCommands = apiVerification.commands.join("\n"); if ( !apiVerificationCommands.includes("pnpm exec jest --runInBand") || apiVerificationCommands.includes("passWithNoTests") ) { throw new Error("API Jest verification command self-check failed"); } const timeSerializationRisk = { bad: hasEntityLoadTimeSerializationMutation( "import { AfterLoad } from 'typeorm';\nexport const FormatDateTime = () => {}; formatDateTimeFields(this);", ), good: hasEntityLoadTimeSerializationMutation( "import { Column } from 'typeorm';\nexport class KtDateTime extends Date {}\nexport const KtDateTimeColumn = () => Column({ transformer: { from: (value) => new KtDateTime(value), to: (value) => value } });", ), }; if (!timeSerializationRisk.bad || timeSerializationRisk.good) { throw new Error("time serialization mutation risk self-check failed"); } const databaseVerification = buildVerificationPlan({ changeType: "database", project: "api", }); const databaseVerificationNotes = databaseVerification.notes.join("\n"); if ( !databaseVerificationNotes.includes("备份目标表") || !databaseVerificationNotes.includes("SHOW COLUMNS") || !databaseVerificationNotes.includes("真实接口 smoke") ) { throw new Error("database migration verification self-check failed"); } const closeoutNeedsUpgrade = buildWorkstreamCloseout({ cleanupEvidence: ["cleanup-history dry-run deleted=0。"], docSyncEvidence: ["kt_change_doc_sync requiredDocs 已处理。"], reviewEvidence: ["global-review findings=0。"], problemRecords: ["Jenkins 状态需要结合日志和 K8s 状态确认。"], reusablePatterns: ["deploy-observation"], stableSolutions: [ "收尾时固定观测 build、commit、镜像、Deployment、Pod 和 smoke。", ], superpowersReviewEvidence: ["Superpowers reviewer completed; Important findings handled。"], title: "发布闭环", verificationEvidence: ["Jenkins #132 SUCCESS,K8s Pod Running。"], }); if ((closeoutNeedsUpgrade.canReportComplete as boolean) !== false) { throw new Error("workstream closeout upgrade gate self-check failed"); } const closeoutComplete = buildWorkstreamCloseout({ cleanupEvidence: ["cleanup-history dry-run deleted=0。"], docSyncEvidence: ["无需文档更新,已记录原因。"], ktWorkflowUpdated: true, problemRecords: ["无新卡点。"], reviewEvidence: ["global-review findings=0。"], reusablePatterns: ["none"], stableSolutions: ["无新增稳定解法。"], superpowersReviewEvidence: ["Superpowers reviewer completed; no Critical/Important findings。"], title: "文档收尾", verificationEvidence: ["typecheck/self-test/global-review 通过。"], }); if ((closeoutComplete.canReportComplete as boolean) !== true) { throw new Error("workstream closeout complete self-check failed"); } const closeoutInvalidCleanup = buildWorkstreamCloseout({ cleanupEvidence: ["cleanup-history dry-run deleted=3。"], docSyncEvidence: ["kt_change_doc_sync requiredDocs 已处理。"], problemRecords: ["无新卡点。"], reviewEvidence: ["global-review findings=0。"], reusablePatterns: ["none"], stableSolutions: ["无新增稳定解法。"], superpowersReviewEvidence: ["Superpowers reviewer completed。"], title: "无效清理证据", verificationEvidence: ["typecheck 通过。"], }); if ((closeoutInvalidCleanup.canReportComplete as boolean) !== false) { throw new Error("workstream closeout cleanup evidence self-check failed"); } const closeoutMissingSuperpowersReview = buildWorkstreamCloseout({ cleanupFinalDeleted: 0, docSyncEvidence: ["无需文档更新,已记录原因。"], problemRecords: ["无新卡点。"], reviewEvidence: ["global-review findings=0。"], reusablePatterns: ["none"], stableSolutions: ["无新增稳定解法。"], title: "缺 Superpowers 审查证据", verificationEvidence: ["typecheck 通过。"], }); if ((closeoutMissingSuperpowersReview.canReportComplete as boolean) !== false) { throw new Error("workstream closeout Superpowers review gate self-check failed"); } const obsidianContext = readObsidianContext({ maxDocuments: 6, module: "ktWorkflow", }); if ( (obsidianContext.summary as { returnedDocuments?: number }) .returnedDocuments === 0 ) { throw new Error("obsidian context self-check failed"); } if ((obsidianContext.summary as { codeAnchors?: number }).codeAnchors === 0) { throw new Error("obsidian code anchor self-check failed"); } const obsidianValidation = validateObsidianVault({ includeStaleReferences: false, }); if ((obsidianValidation.summary as { errors?: number }).errors !== 0) { throw new Error("obsidian validation self-check failed"); } const cleanupCliParser = { defaultDryRun: parseCliCleanupArgs(["node", "server", "--cleanup-history"]) .dryRun, executeDryRun: parseCliCleanupArgs([ "node", "server", "--cleanup-history", "--execute", ]).dryRun, }; if (!cleanupCliParser.defaultDryRun || cleanupCliParser.executeDryRun) { throw new Error("cleanup CLI dry-run parser self-check failed"); } const cleanupPreview = cleanupHistoryArtifacts({ dryRun: true, keep: 3, roots: [".kt-workspace/test-artifacts", ".kt-workspace"], }); const cleanupRoots = cleanupPreview.roots as Array<{ preservedNames: string[]; root: string; }>; const testArtifactsRoot = cleanupRoots.find( (item) => item.root === ".kt-workspace/test-artifacts", ); const workspaceHistoryRoot = cleanupRoots.find( (item) => item.root === ".kt-workspace", ); if (!testArtifactsRoot?.preservedNames.includes("_templates")) { throw new Error("cleanup template preservation self-check failed"); } if (!workspaceHistoryRoot?.preservedNames.includes("test-artifacts")) { throw new Error("cleanup workspace root preservation self-check failed"); } const docSyncBangDream = await buildChangeDocSync({ changedFiles: [ "src/qqbot/plugins/bangDream/application/bangdream-application.service.ts", ], project: "api", }); const docSyncRequiredDocs = docSyncBangDream.requiredDocs as Array<{ path: string; }>; if ( !docSyncRequiredDocs.some( (item) => item.path === "Node/kt-template-online-api/API.md", ) ) { throw new Error("doc sync API self-check failed"); } const loopAuditComplete = buildWorkflowLoopAudit({ cleanupExecuted: true, cleanupFinalDeleted: 0, cleanupPreviewDeleted: 3, docSyncEvidence: ["kt_change_doc_sync requiredDocs 已处理。"], ktWorkflowUpdated: true, problemRecords: ["测试历史清理流程未自动执行。"], reusablePattern: true, reviewEvidence: ["global-review findings=0"], stableSolutions: [ "cleanup-history 默认 dry-run,显式 --execute 后复验 deleted=0。", ], superpowersReviewEvidence: ["Superpowers reviewer completed; no Critical/Important findings"], taskTitle: "自动化闭环审计", testCases: ["cleanup-history dry-run/execute 回归测试"], verificationEvidence: ["typecheck/self-test/cleanup dry-run 通过"], }); if ((loopAuditComplete.canReportComplete as boolean) !== true) { throw new Error("workflow loop complete self-check failed"); } const loopAuditBlocked = buildWorkflowLoopAudit({ cleanupPreviewDeleted: 1, taskTitle: "缺证据闭环", }); if ((loopAuditBlocked.canReportComplete as boolean) !== false) { throw new Error("workflow loop blocked self-check failed"); } const loopAuditMissingCleanup = buildWorkflowLoopAudit({ docSyncEvidence: ["kt_change_doc_sync requiredDocs 已处理。"], reviewEvidence: ["global-review findings=0。"], superpowersReviewEvidence: ["Superpowers reviewer completed。"], taskTitle: "缺清理证据闭环", testCases: ["self-test"], verificationEvidence: ["typecheck 通过。"], }); if ((loopAuditMissingCleanup.canReportComplete as boolean) !== false) { throw new Error("workflow loop missing cleanup evidence self-check failed"); } const dbSyncPlan = buildDbSyncPlan({}); const dbSyncCommands = (dbSyncPlan.commands as string[]).join("\n"); const backupName = dbSyncPlan.backupName as string; if ( !backupName.includes("_") || !dbSyncCommands.includes(".kt-workspace/db-sync") || dbSyncCommands.includes("CREATE DATABASE IF NOT EXISTS") ) { throw new Error("DB sync plan safety self-check failed"); } const data = { context: readWorkflowContext({ taskRecordCount: 2 }), guardrails: buildGuardrails({ project: "mcp", taskType: "mcp", userRequest: "扩展 KT 工作区 MCP 可复用能力", }), cleanupCliParser, cleanupPreview, docSyncBangDream, loopAuditBlocked, loopAuditComplete, historyCleanup: cleanupHistoryArtifacts({ dryRun: true, keep: 3, }), inspectAdmin: await inspectProject({ includeGit: false, project: "admin" }), inspectKnife4j: await inspectProject({ includeGit: false, project: "knife4j", }), prepareTask: await prepareTask({ includeGit: false, project: "mcp", taskType: "mcp", userRequest: "扩展 KT 工作区 MCP 可复用能力", }), review: await buildGlobalCodeReview({ includeContentScan: true, includeRootScan: true, maxFindingsPerProject: 10, projects: ["mcp"], }), reviewCliParser, reviewClassifier, refactorGuardrails, apiVerification, businessTestPlan: buildBusinessTestPlan({ flow: "system-log-visualization", }), closeoutComplete, closeoutNeedsUpgrade, obsidianContext, obsidianSync: syncObsidianWorkflow({ includeStaleReferences: false, }), obsidianValidation, blockerResolution: buildBlockerResolution({ attempts: 2, evidence: "self-test", problem: "重复超时", project: "mcp", solution: "改用固定脚本和 timeout 后再验证", }), commitPlan: await buildCommitPlan({ projects: ["mcp"], }), componentWorkflow: buildComponentWorkflow({ target: "SystemLog", }), dbSyncPlan, finishTask: await buildFinishTask({ cleanupHistory: false, projects: ["mcp"], runValidation: false, }), pushPlan: await buildPushPlan({ projects: ["mcp"], }), remoteHealthCheck: await buildRemoteHealthCheck({ execute: false, services: ["ssh", "docker"], }), verification: buildVerificationPlan({ changeType: "mcp", project: "mcp", }), }; const outputDir = resolveInsideRoot(".kt-workspace/verify/ktWorkflow"); mkdirSync(outputDir, { recursive: true }); writeFileSync( path.join(outputDir, "self-test.json"), JSON.stringify(data, null, 2), "utf8", ); console.log( JSON.stringify( { ok: true, outputDir, tools: registeredToolNames.length }, null, 2, ), ); }