ktworkflow-mcp/src/selfTest.ts

1963 lines
68 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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<void> {
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<string, string> };
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: [
"<jenkins.scm.api.SCMRevisionAction>",
"<revision>",
"<hash>abc1234</hash>",
"</revision>",
"</jenkins.scm.api.SCMRevisionAction>",
].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 SUCCESSK8s 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,
),
);
}