import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs"; import path from "node:path"; import { parseNasCodexBootstrapCliArgs, parseDeployObservationCliArgs, parseGlobalReviewCliArgs, parseNapcatRemoteDevHandoffCliArgs, parseNapcatRuntimeReleaseReadinessCliArgs, parseNapcatSyncCandidateReviewCliArgs, parseNapcatUpstreamAuditCliArgs, 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, findQqbotPluginPackageProductionPathFindings, findQqbotPluginSmokeImportFindings, findQqbotStatusBoundaryFindings, findTaskRecordGovernanceFindings, isBenignCredentialReviewValue, } from "./tools/review.js"; import { napcatAutomationReasonCodes, parseNapcatAutomationClassification, upstreamAuditOutputSchema, } from "./tools/napcatAutomation.types.js"; import { buildNapcatCodexExecCommand, buildNapcatRemoteDevHandoff, buildNapcatRuntimeReleaseReadiness, buildNapcatSyncCandidateReview, buildNapcatUpstreamAudit, buildNasCodexBootstrapPlan, classifyNapcatUpstreamAudit, napcatAutomationDefaults, } from "./tools/napcatAutomation.js"; import { registerTools } from "./registerTools.js"; import { loadNapcatAutomationPrompt, type NapcatAutomationPromptName, napcatAutomationPromptNames, } from "./tools/napcatAutomation.prompts.js"; import { buildBusinessTestPlan, buildNapcatDeviceProfileCheck, } 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"; /** * Runs ktWorkflow guardrail smoke checks from the CLI self-test entry; writes local evidence under the KT workspace and throws on any broken workflow contract. * @returns A promise that resolves after all contract checks and evidence writing complete. */ export async function runSelfTest(): Promise { const napcatAuditSchemaSmoke = upstreamAuditOutputSchema.safeParse({ classification: "manual-review", reasonCodes: ["HOT_ZONE_CHANGED"], recommendedAction: "request-human-review", summary: "Login hot-zone changed.", }); if (!napcatAuditSchemaSmoke.success) { throw new Error("NapCat upstream audit schema self-check failed"); } const napcatAuditExtraPropertySmoke = upstreamAuditOutputSchema.safeParse({ classification: "manual-review", reasonCodes: ["HOT_ZONE_CHANGED"], recommendedAction: "request-human-review", summary: "Login hot-zone changed.", unexpectedField: "Codex output schema rejects this.", }); if (napcatAuditExtraPropertySmoke.success) { throw new Error("NapCat upstream audit schema strict self-check failed"); } if (!napcatAutomationReasonCodes.includes("HOT_ZONE_CHANGED")) { throw new Error("NapCat reason-code list self-check failed"); } const napcatCli = parseNapcatUpstreamAuditCliArgs([ "node", "server", "--napcat-upstream-audit", "--execute", "--use-codex", "--artifact-root", "/vol1/docker/kt-codex/artifacts/napcat-upstream-sync", ]); if (!napcatCli.execute || !napcatCli.useCodex) { throw new Error("NapCat upstream audit CLI parser self-check failed"); } if ( napcatCli.artifactRoot !== "/vol1/docker/kt-codex/artifacts/napcat-upstream-sync" ) { throw new Error("NapCat upstream audit artifactRoot CLI parser self-check failed"); } const napcatCandidateCli = parseNapcatSyncCandidateReviewCliArgs([ "node", "server", "--napcat-sync-candidate-review", ]); const napcatRuntimeCli = parseNapcatRuntimeReleaseReadinessCliArgs([ "node", "server", "--napcat-runtime-release-readiness", ]); const napcatRemoteHandoffCli = parseNapcatRemoteDevHandoffCliArgs([ "node", "server", "--napcat-remote-dev-handoff", ]); const nasBootstrapCli = parseNasCodexBootstrapCliArgs([ "node", "server", "--nas-codex-bootstrap", ]); if ( napcatCandidateCli.execute || napcatRuntimeCli.execute || napcatRemoteHandoffCli.execute || nasBootstrapCli.execute ) { throw new Error("NapCat auxiliary CLI dry-run default self-check failed"); } const napcatCandidateDefaults = buildNapcatSyncCandidateReview({}).context; const napcatRuntimeDefaults = buildNapcatRuntimeReleaseReadiness({}).context; const napcatRemoteDefaults = buildNapcatRemoteDevHandoff({}).context; const nasBootstrapDefaults = buildNasCodexBootstrapPlan({}).context; if ( napcatCandidateDefaults.candidateBranch !== napcatAutomationDefaults.candidateBranch || napcatCandidateDefaults.forkBranch !== napcatAutomationDefaults.forkBranch || napcatCandidateDefaults.forkRepo !== napcatAutomationDefaults.forkRepo || napcatCandidateDefaults.lastAcceptedUpstreamBase !== napcatAutomationDefaults.lastAcceptedUpstreamBase || napcatCandidateDefaults.upstreamReleaseTag !== napcatAutomationDefaults.upstreamReleaseRef || napcatCandidateDefaults.upstreamRepo !== napcatAutomationDefaults.upstreamRepo || napcatCandidateDefaults.workspaceRoot !== napcatAutomationDefaults.workspaceRoot ) { throw new Error("NapCat candidate review default alignment self-check failed"); } if ( napcatRuntimeDefaults.apiImageTag !== napcatAutomationDefaults.apiImageTag || napcatRuntimeDefaults.candidateBranch !== napcatAutomationDefaults.candidateBranch || napcatRuntimeDefaults.forkBranch !== napcatAutomationDefaults.forkBranch || napcatRuntimeDefaults.forkRepo !== napcatAutomationDefaults.forkRepo || napcatRuntimeDefaults.napcatImageTag !== napcatAutomationDefaults.runtimeImageTag || napcatRuntimeDefaults.profile !== napcatAutomationDefaults.runtimeProfile || napcatRuntimeDefaults.upstreamReleaseTag !== napcatAutomationDefaults.upstreamReleaseRef || napcatRuntimeDefaults.workspaceRoot !== napcatAutomationDefaults.workspaceRoot ) { throw new Error("NapCat runtime readiness default alignment self-check failed"); } if ( napcatRemoteDefaults.candidateBranch !== napcatAutomationDefaults.candidateBranch || napcatRemoteDefaults.forkBranch !== napcatAutomationDefaults.forkBranch || napcatRemoteDefaults.forkRepo !== napcatAutomationDefaults.forkRepo || napcatRemoteDefaults.targetHost !== napcatAutomationDefaults.targetHost || napcatRemoteDefaults.upstreamReleaseTag !== napcatAutomationDefaults.upstreamReleaseRef || napcatRemoteDefaults.workspaceRoot !== napcatAutomationDefaults.workspaceRoot ) { throw new Error("NapCat remote handoff default alignment self-check failed"); } if ( nasBootstrapDefaults.codexHome !== napcatAutomationDefaults.codexHome || nasBootstrapDefaults.serviceName !== napcatAutomationDefaults.bootstrapServiceName || nasBootstrapDefaults.workspaceRoot !== napcatAutomationDefaults.workspaceRoot ) { throw new Error("NAS Codex bootstrap default alignment self-check failed"); } const bootstrap = buildNasCodexBootstrapPlan({ execute: false }); const bootstrapCommandText = bootstrap.commands .map((item) => item.command) .join("\n"); if (!bootstrapCommandText.includes("/vol1/docker/kt-codex/home/.codex")) { throw new Error("NAS Codex bootstrap CODEX_HOME self-check failed"); } if (bootstrapCommandText.includes("C:\\Users")) { throw new Error("NAS Codex bootstrap Windows path guard self-check failed"); } for (const expected of [ "node --version", "corepack --version", "pnpm --version", "git --version", "docker --version", "codex --version", "test -d /vol1/docker/kt-codex/workspace/KT", "test -d /vol1/docker/kt-codex/home/.codex", ]) { if (!bootstrapCommandText.includes(expected)) { throw new Error(`NAS Codex bootstrap dry-run command missing: ${expected}`); } } for (const command of bootstrap.commands) { if (!command.readOnly) { throw new Error("NAS Codex bootstrap dry-run command must be read-only"); } } const executeBootstrap = buildNasCodexBootstrapPlan({ execute: true }); const executeBootstrapCommandText = executeBootstrap.commands .map((item) => item.command) .join("\n"); if ( executeBootstrap.execute !== true || executeBootstrap.commands.every((command) => command.readOnly) ) { throw new Error("NAS Codex bootstrap execute plan self-check failed"); } if ( !executeBootstrapCommandText.includes("install -d -m 700 /vol1/docker/kt-codex/home/.codex") || !executeBootstrapCommandText.includes("npm install -g @openai/codex") ) { throw new Error("NAS Codex bootstrap execute package plan self-check failed"); } for (const forbidden of [ "git push", "git merge", "docker build", "docker pull", "kubectl", "systemctl enable", "systemctl start", "cp ~/.codex", "scp ~/.codex", "C:\\Users", ]) { if (executeBootstrapCommandText.includes(forbidden)) { throw new Error(`NAS Codex bootstrap forbidden command leaked: ${forbidden}`); } } let windowsBootstrapPathRejected = false; try { buildNasCodexBootstrapPlan({ codexHome: "C:\\Users\\example\\.codex", execute: false, }); } catch (error) { windowsBootstrapPathRejected = String(error).includes( "Unsafe NAS Codex bootstrap path", ); } if (!windowsBootstrapPathRejected) { throw new Error("NAS Codex bootstrap unsafe Windows path self-check failed"); } const napcatCandidateOverride = buildNapcatSyncCandidateReview({ candidateBranch: "custom-candidate", lastAcceptedUpstreamBase: "custom-base", }); const napcatCandidateOverrideCommandText = napcatCandidateOverride.commands.map((item) => item.command).join("\n"); if ( !napcatCandidateOverrideCommandText.includes("custom-candidate") || !napcatCandidateOverrideCommandText.includes("custom-base") || napcatCandidateOverrideCommandText.includes( napcatAutomationDefaults.candidateBranch, ) || napcatCandidateOverrideCommandText.includes( napcatAutomationDefaults.lastAcceptedUpstreamBase, ) ) { throw new Error("NapCat candidate command override self-check failed"); } const napcatRuntimeOverride = buildNapcatRuntimeReleaseReadiness({ napcatImageTag: "custom-image", }); const napcatRuntimeOverrideCommandText = napcatRuntimeOverride.commands.map((item) => item.command).join("\n"); if ( !napcatRuntimeOverrideCommandText.includes("custom-image") || napcatRuntimeOverrideCommandText.includes( napcatAutomationDefaults.runtimeImageTag, ) ) { throw new Error("NapCat runtime command override self-check failed"); } const napcatAuditOverride = buildNapcatUpstreamAudit({ dryMergeConflict: false, forkBranch: "custom-fork", forkPatchFiles: [], lastAcceptedUpstreamBase: "custom-base", upstreamChangedFiles: [], upstreamReleaseTag: "custom-upstream", }); const napcatAuditOverrideCommandText = napcatAuditOverride.commands.map((item) => item.command).join("\n"); if ( !napcatAuditOverrideCommandText.includes("custom-fork") || !napcatAuditOverrideCommandText.includes("custom-base") || !napcatAuditOverrideCommandText.includes("custom-upstream") || napcatAuditOverrideCommandText.includes(napcatAutomationDefaults.forkBranch) || napcatAuditOverrideCommandText.includes( napcatAutomationDefaults.lastAcceptedUpstreamBase, ) || napcatAuditOverrideCommandText.includes( napcatAutomationDefaults.upstreamReleaseRef, ) ) { throw new Error("NapCat upstream audit command override self-check failed"); } const packageJson = JSON.parse( readFileSync(resolveInsideRoot("mcp/ktWorkflow/package.json"), "utf8"), ) as { scripts?: Record }; if (!packageJson.scripts?.["napcat-remote-dev-handoff"]) { throw new Error("NapCat remote handoff npm script self-check failed"); } const registeredByFakeServer: string[] = []; const fakeMcpServer = { /** * Records registered MCP tool names so self-test proves the actual registerTools surface, not only the constants list. * @param name - MCP tool name passed by `registerTools`. * @returns Nothing; the name is appended to `registeredByFakeServer`. */ registerTool(name: string): void { registeredByFakeServer.push(name); }, }; registerTools(fakeMcpServer as never); for (const toolName of [ "kt_napcat_upstream_audit", "kt_napcat_sync_candidate_review", "kt_napcat_runtime_release_readiness", "kt_napcat_remote_dev_handoff", "kt_nas_codex_bootstrap_plan", ] as const) { if (!registeredToolNames.includes(toolName)) { throw new Error(`NapCat MCP tool registry self-check failed: ${toolName}`); } if (!registeredByFakeServer.includes(toolName)) { throw new Error(`NapCat MCP registerTools self-check failed: ${toolName}`); } } if (parseNapcatAutomationClassification("manual-review") !== "manual-review") { throw new Error("NapCat classification parser self-check failed"); } const hotZoneAudit = classifyNapcatUpstreamAudit({ dryMergeConflict: false, forkPatchFiles: ["packages/napcat-core/login/runtime.ts"], upstreamChangedFiles: ["packages/napcat-core/login/runtime.ts"], }); if (hotZoneAudit.classification !== "manual-review") { throw new Error("NapCat hot-zone audit classification self-check failed"); } if (!hotZoneAudit.reasonCodes.includes("HOT_ZONE_CHANGED")) { throw new Error("NapCat hot-zone reason-code self-check failed"); } const dryRunAudit = buildNapcatUpstreamAudit({ dryMergeConflict: false, forkPatchFiles: ["packages/napcat-core/runtime/base.ts"], upstreamChangedFiles: ["packages/napcat-core/runtime/upstream.ts"], }); if (dryRunAudit.execute !== false || dryRunAudit.artifacts.length === 0) { throw new Error( "NapCat upstream audit dry-run artifact plan self-check failed", ); } const dryRunAuditDefaultCommands = dryRunAudit.commands.map( (item) => item.command, ); if ( dryRunAuditDefaultCommands.some((command) => command.includes("FORK_BRANCH:-HEAD"), ) || !dryRunAuditDefaultCommands.some((command) => command.includes(`FORK_BRANCH:-${napcatAutomationDefaults.forkBranch}`), ) || !dryRunAuditDefaultCommands.some((command) => command.includes( `UPSTREAM_RELEASE_REF:-${napcatAutomationDefaults.upstreamReleaseRef}`, ), ) ) { throw new Error("NapCat upstream audit command default alignment self-check failed"); } const codexCommand = buildNapcatCodexExecCommand({ artifactRoot: "/vol1/docker/kt-codex/artifacts/smoke", contextPacketPath: "/vol1/docker/kt-codex/artifacts/smoke/context-packet.md", outputSchemaPath: "/vol1/docker/kt-codex/workspace/KT/mcp/ktWorkflow/prompts/napcat/schemas/upstream-audit.schema.json", promptName: "upstream-audit", workspaceRoot: "/vol1/docker/kt-codex/workspace/KT", }); if (!codexCommand.includes("--ask-for-approval never")) { throw new Error("NapCat Codex runner approval policy self-check failed"); } if (!codexCommand.includes("codex --ask-for-approval never exec")) { throw new Error("NapCat Codex runner top-level approval order self-check failed"); } if (codexCommand.includes("codex exec") || codexCommand.includes("$(cat")) { throw new Error("NapCat Codex runner stdin safety self-check failed"); } for (const expected of [ "CODEX_HOME='/vol1/docker/kt-codex/home/.codex'", "--cd '/vol1/docker/kt-codex/workspace/KT'", "--profile-v2 automation", "--sandbox workspace-write", "--json", "--ephemeral", "--output-schema '/vol1/docker/kt-codex/workspace/KT/mcp/ktWorkflow/prompts/napcat/schemas/upstream-audit.schema.json'", "--output-last-message '/vol1/docker/kt-codex/artifacts/smoke/codex-upstream-audit.md'", "- < '/vol1/docker/kt-codex/artifacts/smoke/context-packet.md'", ]) { if (!codexCommand.includes(expected)) { throw new Error(`NapCat Codex runner command missing: ${expected}`); } } if (codexCommand.includes("git push") || codexCommand.includes("kubectl")) { throw new Error("NapCat Codex runner mutation guard self-check failed"); } const codexExecutionArtifactRoot = resolveInsideRoot( ".kt-workspace/test-artifacts/napcat-codex-runner-self-test", ); const codexInvalidSchemaAudit = buildNapcatUpstreamAudit({ artifactRoot: codexExecutionArtifactRoot, codexExecutor: () => ({ ok: true, stderr: "schema warning", stdout: "{\"classification\":\"surprising\"}\n", }), dryMergeConflict: false, execute: true, forkPatchFiles: [], upstreamChangedFiles: [], useCodex: true, }); if ( codexInvalidSchemaAudit.classification !== "blocked" || !codexInvalidSchemaAudit.reasonCodes.includes("CODEX_SCHEMA_INVALID") ) { throw new Error("NapCat Codex invalid schema fallback self-check failed"); } if ( !existsSync( path.join(codexExecutionArtifactRoot, "codex-upstream-audit.jsonl"), ) || !existsSync(path.join(codexExecutionArtifactRoot, "context-packet.md")) || readFileSync( path.join(codexExecutionArtifactRoot, "codex-upstream-audit.stderr.log"), "utf8", ) !== "schema warning" ) { throw new Error("NapCat Codex artifact capture self-check failed"); } const codexItemTextArtifactRoot = resolveInsideRoot( ".kt-workspace/test-artifacts/napcat-codex-runner-success-self-test", ); const codexItemTextAudit = buildNapcatUpstreamAudit({ artifactRoot: codexItemTextArtifactRoot, codexExecutor: () => ({ ok: true, stderr: "", stdout: `${JSON.stringify({ item: { text: JSON.stringify({ classification: "safe-candidate", reasonCodes: ["NO_UPSTREAM_CHANGE"], recommendedAction: "create-candidate-branch", summary: "ok", }), type: "agent_message", }, type: "item.completed", })}\n`, }), dryMergeConflict: false, execute: true, forkPatchFiles: [], upstreamChangedFiles: [], useCodex: true, }); if ( codexItemTextAudit.classification !== "safe-candidate" || !codexItemTextAudit.reasonCodes.includes("NO_UPSTREAM_CHANGE") || codexItemTextAudit.recommendedAction !== "create-candidate-branch" ) { throw new Error("NapCat Codex item.text JSONL success self-check failed"); } const codexThrowArtifactRoot = resolveInsideRoot( ".kt-workspace/test-artifacts/napcat-codex-runner-throw-self-test", ); const codexThrowAudit = buildNapcatUpstreamAudit({ artifactRoot: codexThrowArtifactRoot, codexExecutor: () => { throw new Error("codex timed out"); }, dryMergeConflict: false, execute: true, forkPatchFiles: [], upstreamChangedFiles: [], useCodex: true, }); if ( codexThrowAudit.classification !== "blocked" || !readFileSync( path.join(codexThrowArtifactRoot, "codex-upstream-audit.stderr.log"), "utf8", ).includes("codex timed out") ) { throw new Error("NapCat Codex thrown executor stderr self-check failed"); } const unsafeArtifactRootPath = path.resolve("/tmp/not-kt"); const unsafeArtifactRootExisted = existsSync(unsafeArtifactRootPath); let unsafeArtifactRootRejected = false; let unsafeArtifactExecutorCalled = false; try { buildNapcatUpstreamAudit({ artifactRoot: "/tmp/not-kt", codexExecutor: () => { unsafeArtifactExecutorCalled = true; return { ok: true, stderr: "", stdout: "{}\n" }; }, dryMergeConflict: false, execute: true, forkPatchFiles: [], upstreamChangedFiles: [], useCodex: true, }); } catch (error) { unsafeArtifactRootRejected = String(error).includes( "Unsafe NapCat Codex artifactRoot", ); } finally { if (!unsafeArtifactRootExisted && existsSync(unsafeArtifactRootPath)) { rmSync(unsafeArtifactRootPath, { force: true, recursive: true }); } } if (!unsafeArtifactRootRejected || unsafeArtifactExecutorCalled) { throw new Error("NapCat Codex unsafe artifact root self-check failed"); } const dryRunAuditCommands: string[] = []; for (const command of dryRunAudit.commands) { if (!command.readOnly) { throw new Error("NapCat upstream audit dry-run read-only flag failed"); } dryRunAuditCommands.push(command.command); } const dryRunAuditCommandText = dryRunAuditCommands.join("\n"); for (const expected of [ "git status --short --branch", "git fetch --dry-run upstream --tags --prune", "git fetch --dry-run origin --prune", "git diff --name-only", "git merge-tree", ]) { if (!dryRunAuditCommandText.includes(expected)) { throw new Error( `NapCat upstream audit dry-run command missing: ${expected}`, ); } } for (const command of dryRunAudit.commands) { if ( command.readOnly && /^git\s+fetch(?:\s|$)/.test(command.command) && !/\s--dry-run(?:\s|$)/.test(command.command) ) { throw new Error( "NapCat upstream audit read-only fetch must use --dry-run", ); } } for (const forbidden of [ { label: "git merge", regex: /(^|\n)\s*git\s+merge(?:\s|$)/ }, { label: "git push", regex: /(^|\n)\s*git\s+push(?:\s|$)/ }, { label: "docker build", regex: /(^|\n)\s*docker\s+build(?:\s|$)/ }, { label: "kubectl", regex: /(^|\n)\s*kubectl(?:\s|$)/ }, { label: "deploy", regex: /(^|\n)\s*deploy(?:\s|$)/ }, ]) { if (forbidden.regex.test(dryRunAuditCommandText)) { throw new Error( `NapCat upstream audit dry-run command is not read-only: ${forbidden.label}`, ); } } for (const invalidPathCase of [ { forkPatchFiles: [], name: "Windows absolute path", upstreamChangedFiles: ["D:/repo/packages/napcat-core/login/runtime.ts"], }, { forkPatchFiles: [], name: "Unix absolute path", upstreamChangedFiles: ["/repo/packages/napcat-core/login/runtime.ts"], }, { forkPatchFiles: [], name: "UNC path", upstreamChangedFiles: [ "\\\\server\\share\\packages\\napcat-core\\login\\runtime.ts", ], }, { forkPatchFiles: [], name: "Windows rooted path", upstreamChangedFiles: [ "\\packages\\napcat-core\\login\\runtime.ts", ], }, { forkPatchFiles: [], name: "Windows drive-relative path", upstreamChangedFiles: ["C:packages/napcat-core/login/runtime.ts"], }, { forkPatchFiles: [], name: "parent segment path", upstreamChangedFiles: ["packages/../napcat-core/login/runtime.ts"], }, { forkPatchFiles: ["packages/../napcat-core/login/runtime.ts"], name: "fork patch parent segment path", upstreamChangedFiles: [], }, ]) { const invalidPathAudit = classifyNapcatUpstreamAudit({ dryMergeConflict: false, forkPatchFiles: invalidPathCase.forkPatchFiles, upstreamChangedFiles: invalidPathCase.upstreamChangedFiles, }); if ( invalidPathAudit.classification !== "blocked" || invalidPathAudit.recommendedAction !== "block" || !invalidPathAudit.reasonCodes.includes("METADATA_UNAVAILABLE") ) { throw new Error( `NapCat invalid audit path self-check failed: ${invalidPathCase.name}`, ); } } for (const promptName of napcatAutomationPromptNames) { const promptText = loadNapcatAutomationPrompt(promptName); if (!promptText.includes("Do not edit files")) { throw new Error(`NapCat prompt safety contract missing: ${promptName}`); } } let traversalPromptRejected = false; try { loadNapcatAutomationPrompt("../../README" as NapcatAutomationPromptName); } catch (error) { traversalPromptRejected = String(error).includes( "Unsupported NapCat automation prompt", ); } if (!traversalPromptRejected) { throw new Error("NapCat prompt traversal self-check failed"); } 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 qqbotPluginSmokeImportFindings = findQqbotPluginSmokeImportFindings( [ "import { QqbotBangDreamPluginService } from '../../../../src/modules/qqbot/plugins/bangDream/qqbot-bangdream.plugin';", ], { file: "test/modules/qqbot/plugins/plugin-controller-http-smoke.spec.ts", project: "Node/kt-template-online-api", }, ); if ( !qqbotPluginSmokeImportFindings.some( (item) => item.category === "qqbot-plugin-smoke-heavy-import", ) ) { throw new Error("QQBot plugin smoke import governance self-check failed"); } const qqbotPluginSmokeMockedFindings = findQqbotPluginSmokeImportFindings( [ "jest.mock(", " '@/modules/qqbot/plugins/bangDream/qqbot-bangdream.plugin',", " () => ({}),", ");", "import { QqbotBangDreamPluginService } from '../../../../src/modules/qqbot/plugins/bangDream/qqbot-bangdream.plugin';", ], { file: "test/modules/qqbot/plugins/plugin-controller-http-smoke.spec.ts", project: "Node/kt-template-online-api", }, ); if (qqbotPluginSmokeMockedFindings.length > 0) { throw new Error( "QQBot plugin smoke import mocked self-check produced false positive", ); } const qqbotPluginProductionPathFindings = findQqbotPluginPackageProductionPathFindings( [ "const DEFAULT_BUILTIN_PACKAGE_ROOT_SEGMENTS = [", " ['src', 'modules', 'qqbot', 'plugins'],", "];", "private resolveEntryFile(entryFile: string) {", " return entryFile;", "}", ], { file: "src/modules/qqbot/plugin-platform/infrastructure/integration/package/plugin-package-path-policy.service.ts", project: "Node/kt-template-online-api", }, ); if ( !qqbotPluginProductionPathFindings.some( (item) => item.category === "qqbot-plugin-production-dist-root-missing", ) || !qqbotPluginProductionPathFindings.some( (item) => item.category === "qqbot-plugin-compiled-entry-fallback-missing", ) ) { throw new Error("QQBot plugin production path governance self-check failed"); } const qqbotPluginProductionPathValidFindings = findQqbotPluginPackageProductionPathFindings( [ "const DEFAULT_BUILTIN_PACKAGE_ROOT_SEGMENTS = [", " ['src', 'modules', 'qqbot', 'plugins'],", " ['dist', 'modules', 'qqbot', 'plugins'],", "];", "private resolveCompiledEntryFile(entryFile: string): string {", " if (entryFile.endsWith('.ts')) {", " return `${entryFile.slice(0, -3)}.js`;", " }", " return entryFile;", "}", ], { file: "src/modules/qqbot/plugin-platform/infrastructure/integration/package/plugin-package-path-policy.service.ts", project: "Node/kt-template-online-api", }, ); if (qqbotPluginProductionPathValidFindings.length > 0) { throw new Error( "QQBot plugin production path governance valid self-check false positive", ); } 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.startNewDeviceVerification(session, container, captchaResult);", "}", "private async startNewDeviceVerification(session, container, captchaResult) {", " session.deviceVerifyUrl = captchaResult.jumpUrl;", " session.captchaUrl = undefined;", " session.newDeviceQrcode = await this.client.GetNewDeviceQRCode();", " session.newDeviceStatus = 'qr-pending';", "}", "private async pollNewDeviceVerification(session) {", " await this.client.PollNewDeviceQR();", " await this.client.NewDeviceLogin();", "}", "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" || item.category === "qqbot-napcat-new-device-drops-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; logFinishedStatus?: string; logCommit?: string; omitFinishedStatus?: boolean; 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: ${overrides.logFinishedStatus ?? "SUCCESS"}` : `Checking out Revision ${overrides.logCommit ?? "abc1234"}`, ...(overrides.omitFinishedStatus ? [] : [`Finished: ${overrides.logFinishedStatus ?? "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 deployObservationLogFailureBeatsXmlEvidence = normalizeDeployObservationEvidence({ checkedAt: new Date("2026-06-14T00:00:02.000Z"), input: deployObservationInput, sectionMap: buildDeployObservationTestSections({ logFinishedStatus: "FAILURE", }), }); if ( deployObservationLogFailureBeatsXmlEvidence.status === "passed" || deployObservationLogFailureBeatsXmlEvidence.details.jenkins .finishedStatus !== "FAILURE" ) { throw new Error("deploy observation log failure precedence self-check failed"); } const deployObservationRunningLogWithoutFinalStatusEvidence = normalizeDeployObservationEvidence({ checkedAt: new Date("2026-06-14T00:00:02.000Z"), input: deployObservationInput, sectionMap: buildDeployObservationTestSections({ omitFinishedStatus: true, }), }); if ( deployObservationRunningLogWithoutFinalStatusEvidence.status === "passed" || deployObservationRunningLogWithoutFinalStatusEvidence.details.jenkins .finishedStatus !== null ) { throw new Error("deploy observation running log status 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 qqbotAccountScanPlan = buildBusinessTestPlan({ flow: "qqbot-account-scan", }); const qqbotAccountScanText = JSON.stringify(qqbotAccountScanPlan); if ( !qqbotAccountScanText.includes("pending sessionId") || !qqbotAccountScanText.includes("第一轮 Docker run") || !qqbotAccountScanText.includes("不能造脏数据") ) { throw new Error("QQBot account scan non-dirty smoke 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 napcatDeviceProfileCheck = buildNapcatDeviceProfileCheck({ project: "api", }); if ((napcatDeviceProfileCheck.ok as boolean) !== true) { throw new Error("NapCat device profile guardrail 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", }), napcatDeviceProfileCheck, 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, ), ); }