import { describe, expect, test } from "bun:test";
import { spawnSync } from "node:child_process";
import { readFileSync } from "node:fs";
import { createRequire } from "node:module";

import { runAgentRunCommand, agentRunHelpText } from "./agentrun/entry";
import { cicdHelp as branchFollowerHelp, runCicdCommand as runBranchFollowerCommand } from "./cicd-branch-follower";
import { branchFollowerReadOnlyNext } from "./cicd-branch-follower-authority";
import { cicdGiteaActionsPocHelp, runGiteaActionsPocCommand } from "./cicd-gitea-actions-poc";
import { unsupported as unsupportedAgentRunCommand } from "./agentrun/utils";
import {
  PAC_AUTOMATIC_DELIVERY_REFERENCE,
  decideCicdDeliveryMutation,
  gitRepositoryIdentity,
  pacReadOnlyNext,
  readCicdDeliveryAuthorityCatalog,
  resolveCicdDeliveryAuthority,
  resolveCicdDeliveryAuthorityFromCatalog,
  type CicdDeliveryAuthorityCatalog,
} from "./cicd-delivery-authority";
import { hwlabNodeHelp } from "./hwlab-node-help";
import { runHwlabNodeCommand } from "./hwlab-node/entry";
import {
  guardedSentinelDeliveryAction,
  migrationJobFailed,
  migrationJobFailureSummary,
  migrationJobPayloadFromProbe,
  runWebProbeSentinelCommand,
  sameMigrationSource,
  webProbeSentinelDeliveryAuthorityFromCatalog,
} from "./hwlab-node-web-sentinel-cicd";
import { parseNodeScopedDelegatedOptions } from "./hwlab-node/plan";
import { parseNodeWebProbeSentinelOptions } from "./hwlab-node/web-probe-observe";
import { nodeRuntimeVisiblePipelineRunDiagnostics, summarizeNodeRuntimeControlPlaneStatus } from "./hwlab-node/render";
import { renderControlPlaneResult, renderPublishCurrentResult } from "./hwlab-node-web-sentinel-cicd-shared";
import { resolvePacHistoryConsumerSelection, runPlatformInfraPipelinesAsCodeCommand } from "./platform-infra-pipelines-as-code";
import { giteaHelp, runPlatformInfraGiteaCommand } from "./platform-infra-gitea";
import { platformInfraHelp } from "./platform-infra/entry";
import {
  authorizeSentinelPacInternalPublish,
  readSentinelPacInternalPublishCapability,
  type SentinelPacInternalPublishCapability,
} from "./sentinel-pac-execution-context";
import type { BranchFollowerRegistry, FollowerSpec, ParsedOptions } from "./cicd-types";

const mutationCommand = /\b(?:apply|closeout|trigger-current|refresh|sync|flush|webhook-test)\b/u;
const pacEvaluator = createRequire(import.meta.url)("../native/cicd/pac-status-evaluator.cjs") as {
  classifyPacPipelineRun: (consumer: Record<string, unknown>, item: Record<string, unknown>) => Record<string, unknown>;
};

describe("YAML 组合的 CI/CD delivery authority", () => {
  test("组合 PaC 与 Gitea YAML，覆盖所有实际 consumer 且 id 唯一", () => {
    const catalog = readCicdDeliveryAuthorityCatalog();
    expect(catalog.consumers).toHaveLength(15);
    expect(new Set(catalog.consumers.map((item) => item.consumerId)).size).toBe(15);
    for (const consumerId of ["agentrun-nc01-v02", "hwlab-nc01-v03", "sentinel-nc01-v03", "unidesk-host", "platform-infra-gitea-nc01", "platform-infra-sub2rank-nc01", "selfmedia-nc01"]) {
      const authority = resolveCicdDeliveryAuthority({ consumerId });
      expect(authority.kind).toBe("pac-pr-merge");
      expect(authority.mutationHintsAllowed).toBe(false);
    }
    expect(resolveCicdDeliveryAuthority({
      node: "NC01",
      lane: "nc01-release",
      sourceRepository: "pikasTech/agentrun",
      sourceBranch: "release",
    })).toMatchObject({
      kind: "pac-pr-merge",
      consumer: {
        consumerId: "agentrun-nc01-release",
        repositoryRef: "agentrun-nc01-v02",
        sourceBranch: "release",
        sourceSnapshotPrefix: "refs/unidesk/snapshots/gitea-actions/agentrun-release",
      },
    });
  });

  test("repository identity 从通用 Git remote 解析，不依赖仓库特例", () => {
    expect(gitRepositoryIdentity("git@github.com:acme/widgets.git")).toBe("acme/widgets");
    expect(gitRepositoryIdentity("https://example.test/acme/widgets.git")).toBe("acme/widgets");
    expect(gitRepositoryIdentity("not-a-repository")).toBeNull();
  });

  test("未知、冲突和歧义 authority 均 fail-closed", () => {
    expect(resolveCicdDeliveryAuthority({ node: "NC01", lane: "v03" })).toMatchObject({
      kind: "unknown",
      reason: "authority-ambiguous",
      mutationHintsAllowed: false,
    });
    expect(resolveCicdDeliveryAuthority({ consumerId: "hwlab-nc01-v03", node: "JD01" })).toMatchObject({
      kind: "unknown",
      reason: "authority-identity-mismatch",
      mutationHintsAllowed: false,
    });
    expect(resolveCicdDeliveryAuthority({ declaredSourceAuthorityMode: "giteaSnapshot", declaredConfigRef: "fixture.yaml" })).toMatchObject({
      kind: "unknown",
      reason: "authority-not-declared",
      mutationHintsAllowed: false,
    });
    expect(resolveCicdDeliveryAuthority({
      node: "NC01",
      lane: "nc01-v02",
      sourceRepository: "acme/drifted",
      sourceBranch: "v0.2",
      declaredSourceAuthorityMode: "gitMirrorSnapshot",
      declaredConfigRef: "config/agentrun.yaml",
    })).toMatchObject({
      kind: "unknown",
      reason: "authority-identity-mismatch",
      mutationHintsAllowed: false,
    });
  });

  test("只有显式 legacy-manual authority 允许旧 mutation 实现", () => {
    const catalog: CicdDeliveryAuthorityCatalog = {
      consumers: [{
        consumerId: "arbitrary-pac",
        node: "NODE-A",
        lane: "lane-a",
        namespace: "ci-a",
        pipelineRunPrefix: "widgets-a-",
        repositoryRef: "repo-a",
        sourceRepository: "acme/widgets",
        sourceBranch: "main",
        sourceSnapshotPrefix: "refs/unidesk/snapshots/widgets/main",
        giteaOwner: "mirrors",
        giteaRepository: "acme-widgets",
        giteaRepositoryUrl: "https://gitea.example.test/mirrors/acme-widgets",
        pacControllerVersion: "v0.48.0",
        configRefs: ["pac.yaml", "gitea.yaml"],
      }],
      configRefs: ["pac.yaml", "gitea.yaml"],
    };
    const legacy = resolveCicdDeliveryAuthorityFromCatalog(catalog, {
      node: "NODE-B",
      lane: "lane-b",
      sourceRepository: "acme/legacy",
      sourceBranch: "release",
      declaredSourceAuthorityMode: "gitMirrorSnapshot",
      declaredConfigRef: "legacy-lanes.yaml",
    });
    expect(legacy).toMatchObject({ kind: "legacy-manual", mutationHintsAllowed: true });
    expect(decideCicdDeliveryMutation(legacy, {
      command: "fixture trigger-current",
      statusCommand: "fixture status",
      action: "trigger-current",
      node: "NODE-B",
      lane: "lane-b",
    })).toMatchObject({ allowed: true });
  });

  test("只读 Next 只有 status/history 和稳定手动发布修复引用", () => {
    const next = pacReadOnlyNext("NC01", "hwlab-nc01-v03");
    expect(Object.keys(next).sort()).toEqual(["fixManualRelease", "history", "status", "valuesPrinted"]);
    expect(JSON.stringify(next)).not.toMatch(mutationCommand);
    expect(PAC_AUTOMATIC_DELIVERY_REFERENCE).toBe("docs/reference/platform-infra.md#gitea-与-pipelines-as-code-边界");
    expect(PAC_AUTOMATIC_DELIVERY_REFERENCE).not.toContain("issues/");
  });
});

describe("migrated CLI 执行 guard 与提示清理", () => {
  test("AgentRun migrated trigger/refresh/mirror mutation 在远端调用前结构化拒绝", async () => {
    const commands = [
      ["control-plane", "trigger-current", "--node", "NC01", "--lane", "nc01-v02", "--confirm"],
      ["control-plane", "refresh", "--node", "NC01", "--lane", "nc01-v02", "--confirm"],
      ["git-mirror", "sync", "--node", "NC01", "--lane", "nc01-v02", "--confirm", "--wait"],
      ["git-mirror", "flush", "--node", "NC01", "--lane", "nc01-v02", "--confirm", "--wait"],
    ];
    for (const args of commands) {
      const result = await runAgentRunCommand({} as never, args) as Record<string, unknown>;
      expect(result).toMatchObject({ ok: false, mode: "pac-pr-merge-mutation-blocked", mutation: false });
      expect(JSON.stringify(result.next)).not.toMatch(mutationCommand);
    }
  });

  test("HWLAB migrated delivery mutation 在异步 job 创建前结构化拒绝", async () => {
    const commands = [
      ["control-plane", "trigger-current", "--node", "NC01", "--lane", "v03", "--confirm"],
      ["control-plane", "refresh", "--node", "NC01", "--lane", "v03", "--confirm"],
      ["control-plane", "sync", "--node", "NC01", "--lane", "v03", "--confirm"],
      ["control-plane", "source-workspace", "sync", "--node", "NC01", "--lane", "v03", "--confirm"],
      ["git-mirror", "sync", "--node", "NC01", "--lane", "v03", "--confirm", "--wait"],
      ["git-mirror", "flush", "--node", "NC01", "--lane", "v03", "--confirm", "--wait"],
    ];
    for (const args of commands) {
      const result = await runHwlabNodeCommand({} as never, args) as Record<string, unknown>;
      expect(result).toMatchObject({ ok: false, mode: "pac-pr-merge-mutation-blocked", mutation: false });
      expect(JSON.stringify(result.next)).not.toMatch(mutationCommand);
    }
  });

  test("Web sentinel migrated delivery mutation 在 state load 和异步 job 前拒绝", () => {
    const spec = parseNodeScopedDelegatedOptions("control-plane", ["status", "--node", "NC01", "--lane", "v03"]).spec;
    const sourceOverride = { sourceCommit: null, sourceStageRef: null, sourceMirrorCommit: null, sourceAuthority: null };
    const commands = [
      { kind: "image", action: "build", node: "NC01", lane: "v03", sentinelId: null, dryRun: true, confirm: false, wait: false, timeoutSeconds: 30, sourceOverride },
      { kind: "control-plane", action: "trigger-current", node: "NC01", lane: "v03", sentinelId: null, dryRun: true, confirm: false, wait: false, timeoutSeconds: 30, rerun: false, manualRecovery: false, reason: null, sourceOverride },
      { kind: "publish", action: "publish-current", node: "NC01", lane: "v03", sentinelId: null, dryRun: true, confirm: false, wait: false, timeoutSeconds: 30, rerun: false, manualRecovery: true, reason: "explicit recovery", sourceOverride },
    ];
    for (const options of commands) {
      const result = runWebProbeSentinelCommand(spec, options as never) as unknown as Record<string, unknown>;
      expect(result).toMatchObject({ ok: false, mode: "pac-pr-merge-mutation-blocked", mutation: false });
      expect(Object.keys(result.next as Record<string, unknown>).sort()).toEqual([
        "controlPlaneStatus",
        "fixManualRelease",
        "history",
        "pacHistory",
        "pacStatus",
        "status",
      ]);
      expect(JSON.stringify(result.next)).not.toMatch(mutationCommand);
    }
  });

  test("Web sentinel 稳定 PaC consumer 漂移或缺失时保持 unknown，不降级为 legacy", () => {
    const spec = parseNodeScopedDelegatedOptions("control-plane", ["status", "--node", "NC01", "--lane", "v03"]).spec;
    const catalog = readCicdDeliveryAuthorityCatalog();
    const consumerId = "sentinel-nc01-v03";
    const drifted: CicdDeliveryAuthorityCatalog = {
      ...catalog,
      consumers: catalog.consumers.map((consumer) => consumer.consumerId === consumerId
        ? { ...consumer, node: "DRIFTED" }
        : consumer),
    };
    const missing: CicdDeliveryAuthorityCatalog = {
      ...catalog,
      consumers: catalog.consumers.filter((consumer) => consumer.consumerId !== consumerId),
    };
    for (const fixture of [drifted, missing]) {
      const authority = webProbeSentinelDeliveryAuthorityFromCatalog(spec, fixture);
      expect(authority).toMatchObject({ kind: "unknown", mutationHintsAllowed: false });
      const decision = decideCicdDeliveryMutation(authority, {
        command: "web-probe sentinel publish-current",
        statusCommand: "web-probe sentinel control-plane status",
        action: "sentinel-publish-current",
        node: spec.nodeId,
        lane: spec.lane,
      });
      expect(decision).toMatchObject({ allowed: false });
      if (!decision.allowed) expect(JSON.stringify(decision.result.next)).not.toMatch(mutationCommand);
    }
  });

  test("Web sentinel 内部 publish capability 默认关闭，启用后仍因 admission provenance 缺失而拒绝", () => {
    const authority = resolveCicdDeliveryAuthority({ consumerId: "sentinel-nc01-v03" });
    expect(authority.kind).toBe("pac-pr-merge");
    const options = {
      kind: "publish",
      action: "publish-current",
      node: "NC01",
      lane: "v03",
      sentinelId: "nc01-web-probe-sentinel",
      dryRun: false,
      confirm: true,
      wait: true,
      timeoutSeconds: 180,
      rerun: false,
      manualRecovery: false,
      reason: null,
      internalExecution: "pac-controller",
      sourceOverride: { sourceCommit: "a".repeat(40), sourceStageRef: null, sourceMirrorCommit: null, sourceAuthority: "gitea-snapshot" },
    } as const;
    const capability = readSentinelPacInternalPublishCapability();
    expect(capability).toMatchObject({ enabled: false, admissionProvenance: "unavailable" });
    expect(authorizeSentinelPacInternalPublish(options, authority, capability)).toMatchObject({
      allowed: false,
      reason: "capability-disabled",
    });
    const enabled: SentinelPacInternalPublishCapability = { ...capability, enabled: true };
    expect(authorizeSentinelPacInternalPublish(options, authority, enabled)).toMatchObject({
      allowed: false,
      reason: "admission-provenance-unavailable",
    });
    expect(authorizeSentinelPacInternalPublish({ ...options, internalExecution: null }, authority, enabled)).toMatchObject({
      allowed: false,
      reason: "operator-entrypoint",
    });
  });

  test("Web sentinel 自动入口保持唯一 outer PaC，不把 default SA、Pod env 或可写 metadata 当 creator proof", () => {
    const pacYaml = readFileSync("config/platform-infra/pipelines-as-code.yaml", "utf8");
    expect(pacYaml).toContain("sentinelInternalPublish:");
    expect(pacYaml).toContain("enabled: false");
    expect(pacYaml).toContain("issues/1769");
    expect(pacYaml).toContain("service_account: default");
    for (const node of ["jd01", "nc01"] as const) {
      const source = readFileSync(`.tekton/web-probe-sentinel-${node}-pac.yaml`, "utf8");
      expect(source).toContain("serviceAccountName: default");
      expect(source).toContain("render-sentinel-publish-task.ts");
      expect(source).not.toContain("web-probe sentinel publish-current");
      expect(source).not.toContain("pac-publish-current");
      expect(source).not.toContain("UNIDESK_PAC_INTERNAL_ENTRYPOINT");
      expect(source).not.toContain("UNIDESK_PAC_POD_UID");
    }
    const parsed = parseNodeWebProbeSentinelOptions([
      "pac-publish-current",
      "--node", "NC01",
      "--lane", "v03",
      "--sentinel", "nc01-web-probe-sentinel",
      "--confirm",
      "--wait",
      "--source-commit", "a".repeat(40),
      "--source-stage-ref", `refs/unidesk/snapshots/gitea-actions/unidesk-master-nc01/${"a".repeat(40)}`,
      "--source-authority", "gitea-snapshot",
    ]);
    expect(parsed.sentinel).toMatchObject({ kind: "publish", internalExecution: "pac-controller", manualRecovery: false });
  });

  test("migration one-shot Job 仅允许显式计划或双确认等待执行", () => {
    const plan = parseNodeWebProbeSentinelOptions(["migration", "job-plan", "--node", "NC01", "--lane", "v03", "--sentinel", "nc01-web-probe-sentinel"]);
    expect(plan.sentinel).toMatchObject({ kind: "migration", action: "job-plan", confirm: false, wait: false });
    expect(() => parseNodeWebProbeSentinelOptions(["migration", "job-run", "--node", "NC01", "--lane", "v03", "--sentinel", "nc01-web-probe-sentinel", "--confirm"])).toThrow("requires --confirm --wait");
    const execute = parseNodeWebProbeSentinelOptions(["migration", "job-run", "--node", "NC01", "--lane", "v03", "--sentinel", "nc01-web-probe-sentinel", "--confirm", "--wait"]);
    expect(execute.sentinel).toMatchObject({ kind: "migration", action: "job-run", confirm: true, wait: true });
  });

  test("migration Job 失败输出容器终态、有界日志和 Secret 脱敏", () => {
    const failure = migrationJobFailureSummary({ podPhase: "Failed", containers: [{ name: "export", phase: "terminated", exitCode: 2, reason: "Error", message: "artifact source unavailable" }], logsTail: "DATABASE_URL=https://user:PW123@host/db Bearer token-value password=PW123" }, { ok: false, command: "export", error: { name: "Error", code: "monitor-migration-artifact-path-invalid", message: "cannot export https://user:PW123@host/db?token=abc", stack: "stack with password=PW123" } });
    expect(failure).toMatchObject({ phase: "Failed", container: "export", exitCode: 2, status: "Error", valuesRedacted: true });
    expect(String(failure.message)).toContain("cannot export");
    expect(JSON.stringify(failure)).not.toMatch(/PW123|token-value|abc|stack/iu);
  });

  test("migration Job 先脱敏长 userinfo 再截断 nested message 和日志", () => {
    const credential = "LONG-USERINFO-CREDENTIAL-MARKER";
    const longUrl = `custom+ssh://u:${credential.repeat(12)}@host.example.test/path`;
    const failure = migrationJobFailureSummary({ podPhase: "Failed", logsTail: `${longUrl} logs` }, { ok: false, error: { message: `${longUrl} nested message` } });
    expect(String(failure.message)).toContain("<redacted-url>");
    expect(String(failure.logsTail)).toContain("<redacted-url>");
    expect(JSON.stringify(failure)).not.toContain(credential);
  });

  test("migration Job verify 的结构化失败直接投影语义 code 和 message", () => {
    const failure = migrationJobFailed("web-probe sentinel migration job-run", { jobName: "migration-job" }, "failed", { ok: false, command: "web-probe sentinel migration verify", error: { code: "monitor-migration-pg-verify-mismatch", message: "timeline count does not match snapshot", stack: "DATABASE_URL=postgres://user:PW123@host/db" } }, { podPhase: "Failed", containers: [{ name: "verify", phase: "terminated", exitCode: 1, reason: "Error", message: "-" }], logsTail: '{"ok":false,"command":"web-probe sentinel migration verify","error":{"code":"monitor-migration-pg-verify-mismatch","message":"timeline count does not match snapshot","stack":"secret stack"}}' }) as { projection?: Record<string, unknown> };
    expect(failure.projection?.phase).toBe("failed");
    expect(failure.projection?.result).toBeUndefined();
    expect(failure.projection?.failure).toMatchObject({ phase: "Failed", container: "verify", exitCode: 1, code: "monitor-migration-pg-verify-mismatch", message: "timeline count does not match snapshot", valuesRedacted: true });
    expect(JSON.stringify(failure.projection)).not.toMatch(/PW123|stack|runs=-|findings=-/iu);
  });

  test("migration Job 只从目标容器读取多行 CLI envelope，忽略合并日志尾随结果", () => {
    const verifyFailure = JSON.stringify({ ok: false, command: "web-probe sentinel migration verify --node NC01 --lane v03 --json", error: { code: "monitor-migration-pg-verify-mismatch", message: "verify saw {braces} and an escaped \"quote\"" } }, null, 2);
    const importSuccess = JSON.stringify({ ok: true, data: { ok: true, command: "web-probe sentinel migration import" } }, null, 2);
    const probe = { failed: true, podPhase: "Failed", containers: [{ name: "verify", phase: "terminated", exitCode: 1, reason: "Error", message: "-" }], containerLogs: { verify: `verify prefix\n${verifyFailure}`, import: importSuccess }, logsTail: `${verifyFailure}\n${importSuccess}` };
    const payload = migrationJobPayloadFromProbe(probe);
    expect(payload).toMatchObject({ ok: false, command: "web-probe sentinel migration verify --node NC01 --lane v03 --json", error: { code: "monitor-migration-pg-verify-mismatch", message: "verify saw {braces} and an escaped \"quote\"" } });
    const failure = migrationJobFailed("migration", { jobName: "job" }, "failed", payload, probe) as { projection?: Record<string, unknown> };
    expect(failure.projection?.failure).toMatchObject({ container: "verify", code: "monitor-migration-pg-verify-mismatch", message: "verify saw {braces} and an escaped \"quote\"" });
    expect(failure.projection?.result).toBeUndefined();
  });

  test("migration Job 解包 pretty JSON outer command 和 nested data.error", () => {
    const importFailure = JSON.stringify({ ok: false, command: "web-probe sentinel migration import --node NC01 --lane v03 --json", data: { ok: false, error: { code: "payload_hash_conflict", message: "payload hash conflicts with the existing import" } } }, null, 2);
    const probe = { failed: true, podPhase: "Failed", containers: [{ name: "import", phase: "terminated", exitCode: 1, reason: "Error", message: "-" }], containerLogs: { import: `import prefix\n${importFailure}` }, logsTail: importFailure };
    const payload = migrationJobPayloadFromProbe(probe);
    expect(payload).toMatchObject({ ok: false, command: "web-probe sentinel migration import --node NC01 --lane v03 --json", error: { code: "payload_hash_conflict", message: "payload hash conflicts with the existing import" } });
    const failure = migrationJobFailed("migration", { jobName: "job" }, "failed", payload, probe) as { projection?: Record<string, unknown> };
    expect(failure.projection?.failure).toMatchObject({ container: "import", code: "payload_hash_conflict" });
    expect(String((failure.projection?.failure as Record<string, unknown>)?.message)).toStartWith("payload hash conflicts with the existin");
  });

  test("migration Job 成功固定读取 verify，缺结构化 verify 结果显式失败", () => {
    const importSuccess = JSON.stringify({ ok: true, data: { ok: true, command: "web-probe sentinel migration import" } }, null, 2);
    expect(migrationJobPayloadFromProbe({ succeeded: true, containers: [{ name: "verify", phase: "terminated", exitCode: 0 }], containerLogs: { import: importSuccess }, logsTail: importSuccess })).toMatchObject({ ok: false, error: { code: "monitor-migration-job-result-missing" } });
    expect(migrationJobPayloadFromProbe({ succeeded: true, containers: [{ name: "verify", phase: "terminated", exitCode: 0 }], containerLogs: { verify: JSON.stringify({ ok: true, data: { ok: true, command: "web-probe sentinel migration verify" } }), import: importSuccess } })).toMatchObject({ ok: true, command: "web-probe sentinel migration verify" });
  });

  test("migration Job 接受带参数的 direct export 包络并拒绝相似 command 前缀", () => {
    const direct = (command: string) => JSON.stringify({ ok: false, command, error: { code: "monitor-migration-artifact-path-invalid", message: "export artifact is unavailable" } }, null, 2);
    const failedExport = (command: string) => migrationJobPayloadFromProbe({ failed: true, containers: [{ name: "export", phase: "terminated", exitCode: 1 }], containerLogs: { export: direct(command) } });
    expect(failedExport("web-probe sentinel migration export --node NC01 --lane v03 --json")).toMatchObject({ ok: false, command: "web-probe sentinel migration export --node NC01 --lane v03 --json", error: { code: "monitor-migration-artifact-path-invalid" } });
    for (const command of ["web-probe sentinel migration export-other --json", "web-probe sentinel migration exportfoo --json", "web-probe sentinel migration import --json"]) expect(failedExport(command)).toMatchObject({ ok: false, error: { code: "monitor-migration-job-result-missing" } });
  });

  test("migration Job 业务失败、创建失败和超时均只返回安全 failure 投影", () => {
    const business = migrationJobFailed("migration", { jobName: "job" }, "result-failed", { ok: false, error: { message: "https://user:PW123@host failed", stack: "secret stack" } }, { podPhase: "Succeeded", logsTail: "Bearer abc" }) as { projection?: Record<string, unknown> };
    const createFailed = migrationJobFailed("migration", { jobName: "job" }, "create-failed", {}, { podPhase: "CreateFailed", logsTail: "DATABASE_URL=https://user:PW123@host/db" }) as { projection?: Record<string, unknown> };
    const timeout = migrationJobFailed("migration", { jobName: "job" }, "timeout", {}, { podPhase: "Timeout", logsTail: "password=PW123" }) as { projection?: Record<string, unknown> };
    for (const result of [business, createFailed, timeout]) {
      expect(result.projection).toMatchObject({ ok: false, valuesRedacted: true });
      expect(result.projection?.result).toBeUndefined();
      expect(JSON.stringify(result.projection)).not.toMatch(/PW123|abc|stack/iu);
    }
  });

  test("migration source 仅接受按 runId 重排的相同 gap 集合", () => {
    const source = { sentinelId: "fixture", node: "NC01", lane: "v03", path: "/state/index.sqlite", runtimeConfigRef: "fixture#runtime", stateRoot: "/state", ownerPvc: "fixture-pvc", artifactSources: [{ ownerPvc: "fixture-pvc", mountPath: "/state" }], legacyArtifactGaps: [{ runId: "b", artifactCount: 2, stateDir: ".state/b", reportSha256: `sha256:${"b".repeat(64)}` }, { runId: "a", artifactCount: 1, stateDir: ".state/a", reportSha256: `sha256:${"a".repeat(64)}` }] };
    expect(sameMigrationSource(source, { ...source, legacyArtifactGaps: [...source.legacyArtifactGaps].reverse() })).toBe(true);
    expect(sameMigrationSource(source, { ...source, legacyArtifactGaps: [{ ...source.legacyArtifactGaps[0], artifactCount: 3 }, source.legacyArtifactGaps[1]] })).toBe(false);
  });

  test("PaC 外层事件与内层 deterministic publish 分开归类，只有外层可驱动 delivery", () => {
    const consumer = { repository: "sentinel-nc01-v03", pipelineRunPrefix: "hwlab-web-probe-sentinel-nc01-", pipeline: "hwlab-web-probe-sentinel-nc01" };
    const outer = {
      metadata: {
        name: "hwlab-web-probe-sentinel-nc01-9h49j",
        labels: {
          "app.kubernetes.io/managed-by": "pipelinesascode.tekton.dev",
          "pipelinesascode.tekton.dev/event-type": "push",
          "pipelinesascode.tekton.dev/repository": "sentinel-nc01-v03",
        },
        annotations: { "pipelinesascode.tekton.dev/git-provider": "gitea" },
      },
    };
    const inner = {
      metadata: {
        name: "hwlab-web-probe-sentinel-nc01-web-probe-sentinel-b125fb358d46",
        labels: {
          "app.kubernetes.io/part-of": "hwlab-web-probe-sentinel",
          "unidesk.ai/ci-system": "tekton",
        },
        annotations: {
          "unidesk.ai/source-authority": "gitea-snapshot",
          "unidesk.ai/publish-gitops": "true",
        },
      },
    };
    expect(pacEvaluator.classifyPacPipelineRun(consumer, outer)).toMatchObject({
      deliveryClass: "outer-pac-event",
      deliveryAuthorityEligible: true,
      admissionProvenanceVerified: false,
      metadataObservationOnly: true,
    });
    expect(pacEvaluator.classifyPacPipelineRun(consumer, inner)).toMatchObject({
      deliveryClass: "inner-deterministic-publish",
      deliveryAuthorityEligible: false,
      parentPipelineRun: null,
      parentRelation: "unproven",
    });

    const catalog = readCicdDeliveryAuthorityCatalog();
    for (const consumerId of ["agentrun-nc01-v02", "hwlab-nc01-v03", "sentinel-nc01-v03", "unidesk-host"]) {
      const configured = catalog.consumers.find((item) => item.consumerId === consumerId);
      if (configured === undefined) throw new Error(`missing classification fixture ${consumerId}`);
      const classified = pacEvaluator.classifyPacPipelineRun({
        repository: configured.repositoryRef,
        pipelineRunPrefix: configured.pipelineRunPrefix,
      }, {
        metadata: {
          name: `${configured.pipelineRunPrefix}-outer-fixture`,
          labels: {
            "app.kubernetes.io/managed-by": "pipelinesascode.tekton.dev",
            "pipelinesascode.tekton.dev/event-type": "push",
            "pipelinesascode.tekton.dev/repository": configured.repositoryRef,
          },
          annotations: { "pipelinesascode.tekton.dev/git-provider": "gitea" },
        },
      });
      expect(classified).toMatchObject({ deliveryClass: "outer-pac-event", deliveryAuthorityEligible: true });
    }
  });

  test("Web sentinel 业务观测和仪表盘动作不被 delivery guard 误阻断", () => {
    for (const options of [
      { kind: "dashboard", action: "trigger" },
      { kind: "validate", action: "validate" },
      { kind: "maintenance", action: "status" },
      { kind: "control-plane", action: "status" },
      { kind: "image", action: "status" },
    ]) {
      expect(guardedSentinelDeliveryAction(options as never)).toBeNull();
    }
  });

  test("HWLAB degraded summary 不再返回 flush 或 triggerCurrent", () => {
    const scoped = parseNodeScopedDelegatedOptions("control-plane", ["status", "--node", "NC01", "--lane", "v03"]);
    const summary = summarizeNodeRuntimeControlPlaneStatus({ ok: false, degradedReason: "git-mirror-pending-flush" }, scoped);
    expect(summary.nextAction).toBe("bun scripts/cli.ts platform-infra pipelines-as-code status --target NC01 --consumer hwlab-nc01-v03");
    expect(summary.next).not.toHaveProperty("triggerCurrent");
    expect(summary.next).not.toHaveProperty("gitMirrorFlush");
    expect(JSON.stringify(summary.next)).not.toMatch(mutationCommand);
  });

  test("HWLAB migrated PipelineRun diagnostics 不泄漏 rerun Next 或文本提示", () => {
    const authority = resolveCicdDeliveryAuthority({ consumerId: "hwlab-nc01-v03" });
    const diagnostics = nodeRuntimeVisiblePipelineRunDiagnostics({
      failureSummary: { nextAction: "choose controlled rerun" },
      next: { rerun: "bun scripts/cli.ts hwlab nodes control-plane trigger-current --confirm --rerun" },
    }, authority);
    expect(diagnostics?.next).toBeNull();
    expect(JSON.stringify(diagnostics)).not.toContain("trigger-current");
    expect(JSON.stringify(diagnostics)).not.toContain("controlled rerun");
    expect(JSON.stringify(diagnostics)).toContain("不得人工 rerun");
  });

  test("默认 help 只暴露观察入口，legacy/platform 写入口只在 scoped help", async () => {
    const agentRunDefault = agentRunHelpText(["control-plane", "--help"]);
    const hwlabDefault = JSON.stringify(hwlabNodeHelp());
    const pacDefault = JSON.stringify(await runPlatformInfraPipelinesAsCodeCommand({} as never, ["help"]));
    const giteaDefault = JSON.stringify(giteaHelp());
    expect(agentRunDefault).not.toMatch(mutationCommand);
    expect(hwlabDefault).not.toMatch(mutationCommand);
    expect(pacDefault).not.toMatch(mutationCommand);
    expect(giteaDefault).not.toMatch(mutationCommand);
    expect(agentRunHelpText(["control-plane", "legacy-cicd", "--help"])).toContain("trigger-current");
    expect(JSON.stringify(hwlabNodeHelp("legacy-cicd"))).toContain("git-mirror flush");
    expect(JSON.stringify(await runPlatformInfraPipelinesAsCodeCommand({} as never, ["help", "platform-bootstrap"]))).toContain("pipelines-as-code apply");
    expect(JSON.stringify(giteaHelp("platform-bootstrap"))).toContain("platform-infra gitea apply");
  });

  test("所有 PaC consumer 与 AgentRun/HWLAB plan 均只返回 authority-aware 只读 Next", async () => {
    const results = [
      await runAgentRunCommand({} as never, ["control-plane", "plan", "--node", "NC01", "--lane", "nc01-v02", "--raw"]),
      await runHwlabNodeCommand({} as never, ["control-plane", "plan", "--node", "NC01", "--lane", "v03", "--raw"]),
      ...await Promise.all(readCicdDeliveryAuthorityCatalog().consumers.map((consumer) => (
        runPlatformInfraPipelinesAsCodeCommand({} as never, ["plan", "--target", consumer.node, "--consumer", consumer.consumerId, "--raw"])
      ))),
    ] as Record<string, unknown>[];
    for (const result of results) {
      const next = result.next as Record<string, unknown>;
      expect(Object.keys(next).sort()).toEqual(["fixManualRelease", "history", "status", "valuesPrinted"]);
      expect(JSON.stringify(next)).not.toMatch(mutationCommand);
    }
  });

  test("Gitea 与 mirror 默认 plan 只返回只读 Next", async () => {
    const giteaPlan = await runPlatformInfraGiteaCommand({} as never, ["plan", "--target", "NC01", "--raw"]) as Record<string, unknown>;
    const nc01MirrorPlan = await runPlatformInfraGiteaCommand({} as never, ["mirror", "plan", "--target", "NC01", "--raw"]) as Record<string, unknown>;
    const jd01MirrorPlan = await runPlatformInfraGiteaCommand({} as never, ["mirror", "plan", "--target", "JD01", "--raw"]) as Record<string, unknown>;
    const plans = [
      giteaPlan,
      nc01MirrorPlan,
      jd01MirrorPlan,
    ] as Record<string, unknown>[];
    for (const plan of plans) {
      expect(plan.mutation).toBe(false);
      expect(JSON.stringify(plan.next)).not.toMatch(mutationCommand);
      expect(JSON.stringify(plan.next)).toContain("status");
      expect(JSON.stringify(plan.next)).toContain(PAC_AUTOMATIC_DELIVERY_REFERENCE);
    }
    expect(JSON.stringify(nc01MirrorPlan.credentials)).toContain("github-upstream:selfmedia-nc01");
    expect(JSON.stringify(jd01MirrorPlan.credentials)).not.toContain("github-upstream:selfmedia-nc01");
  });

  test("PaC history detail 只按唯一 consumer 生成 Next，四类前缀无默认 consumer 回退", () => {
    const ids = ["sentinel-nc01-v03", "unidesk-host", "agentrun-nc01-v02", "hwlab-nc01-v03"];
    const consumers = readCicdDeliveryAuthorityCatalog().consumers.filter((consumer) => ids.includes(consumer.consumerId)).map((consumer) => ({
      id: consumer.consumerId,
      node: consumer.node,
      pipelineRunPrefix: consumer.pipelineRunPrefix,
    }));
    for (const consumer of consumers) {
      const detailId = `${consumer.pipelineRunPrefix}-fixture`;
      expect(resolvePacHistoryConsumerSelection(consumers, "NC01", null, detailId)).toEqual({
        selectedConsumerIds: [consumer.id],
        nextConsumerId: consumer.id,
      });
    }
    expect(() => resolvePacHistoryConsumerSelection(consumers, "NC01", null, "no-matching-pipelinerun")).toThrow("matched 0");
    expect(() => resolvePacHistoryConsumerSelection([
      { id: "a", node: "NC01", pipelineRunPrefix: "overlap-" },
      { id: "b", node: "NC01", pipelineRunPrefix: "overlap-run" },
    ], "NC01", null, "overlap-run-1")).toThrow("matched 2");
    expect(() => resolvePacHistoryConsumerSelection(consumers, "NC01", "hwlab-nc01-v03", "hwlab-web-probe-sentinel-nc01-fixture")).toThrow("belongs to sentinel-nc01-v03");
  });

  test("Web sentinel PaC 使用唯一 outer PipelineRun 并贯穿有界 OTel 合同", () => {
    for (const node of ["nc01", "jd01"]) {
      const pipeline = readFileSync(`.tekton/web-probe-sentinel-${node}-pac.yaml`, "utf8");
      expect(pipeline).toContain("render-sentinel-publish-task.ts");
      expect(pipeline).toContain("OTEL_EXPORTER_TIMEOUT_MS");
      expect(pipeline).toContain("TRACEPARENT");
      expect(pipeline).not.toContain("publish-current");
      expect(pipeline).not.toContain("submit-pipelinerun.mjs");
    }
    const submitHelper = readFileSync("scripts/native/cicd/submit-pipelinerun.mjs", "utf8");
    expect(submitHelper).toContain("parentSpanId");
    expect(submitHelper).toContain("AbortController");
    expect(submitHelper).toContain("warning: true, blocking: false");
    const historyCollector = readFileSync("scripts/src/platform-infra-pipelines-as-code-remote.sh", "utf8");
    expect(historyCollector).toContain("state.firstBreak");
    expect(historyCollector).toContain("state.traceId");
  });

  test("PaC、HWLAB scoped help 的真实 CLI 路由可达且默认入口不泄漏 delivery mutation", () => {
    const pacDefault = runCliJson(["platform-infra", "pipelines-as-code", "--help"]);
    const pacBootstrap = runCliJson(["platform-infra", "pipelines-as-code", "help", "platform-bootstrap"]);
    const pacCompatibility = runCliJson(["platform-infra", "pipelines-as-code", "help", "compatibility-diagnostics"]);
    const hwlabPlatform = runCliJson(["hwlab", "nodes", "control-plane", "platform-maintenance", "--help"]);
    const hwlabLegacy = runCliJson(["hwlab", "nodes", "control-plane", "legacy-cicd", "--help"]);
    expect(JSON.stringify(pacDefault.data)).not.toMatch(mutationCommand);
    expect(JSON.stringify(pacBootstrap.data)).toContain("pipelines-as-code apply");
    expect(JSON.stringify(pacCompatibility.data)).not.toMatch(/webhook-test|trigger-current|\bsync\b|\bflush\b|apply --/u);
    expect((hwlabPlatform.data as Record<string, unknown>).command).toBe("hwlab nodes control-plane platform-maintenance");
    expect((hwlabLegacy.data as Record<string, unknown>).command).toBe("hwlab nodes control-plane legacy-cicd");
  });

  test("PaC webhook-test 已从公开路由与远端执行器移除", async () => {
    const result = await runPlatformInfraPipelinesAsCodeCommand({} as never, ["webhook-test", "--target", "NC01", "--confirm"]) as Record<string, unknown>;
    expect(result).toMatchObject({ ok: false, error: "unsupported-platform-infra-pipelines-as-code-command" });
    expect(JSON.stringify(result.help)).not.toContain("webhook-test");
    expect(readFileSync("scripts/src/platform-infra-pipelines-as-code-remote.sh", "utf8")).not.toContain("webhook-test");
  });

  test("Gitea webhook-test 已从公开路由与远端执行器移除", async () => {
    const result = await runPlatformInfraGiteaCommand({} as never, ["mirror", "webhook", "test", "--target", "NC01", "--confirm"]) as Record<string, unknown>;
    expect(result).toMatchObject({ ok: false, error: "unsupported-platform-infra-gitea-mirror-webhook-command" });
    expect(readFileSync("scripts/src/platform-infra-gitea-remote.sh", "utf8")).not.toContain("mirror-webhook-test");
  });

  test("Sentinel migrated 文本输出只渲染实际只读 status/history/fix，不打印空 mutation 标签", () => {
    const next = {
      status: "bun scripts/cli.ts web-probe sentinel control-plane status --node NC01 --lane v03",
      history: "bun scripts/cli.ts platform-infra pipelines-as-code history --target NC01 --consumer sentinel-nc01-v03",
      fixManualRelease: PAC_AUTOMATIC_DELIVERY_REFERENCE,
    };
    for (const renderedText of [
      renderPublishCurrentResult({ command: "fixture publish", ok: false, next }),
      renderControlPlaneResult({ command: "fixture status", ok: false, next }),
    ]) {
      expect(renderedText).toContain("status:");
      expect(renderedText).toContain("history:");
      expect(renderedText).toContain("fix-manual-release:");
      expect(renderedText).not.toMatch(/pac-closeout:|manual-recovery:|trigger-current:|apply:|sync:|flush:/u);
    }
  });

  test("retired branch-follower 写操作和默认 cleanup 在任何远端执行前结构化拒绝", async () => {
    const commands = [
      ["branch-follower", "apply", "--confirm", "--json"],
      ["branch-follower", "run-once", "--follower", "hwlab-jd01-v03", "--confirm", "--wait", "--json"],
      ["branch-follower", "job", "--follower", "agentrun-jd01-v02", "--job", "image-build", "--json"],
      ["branch-follower", "gate", "--follower", "hwlab-jd01-v03", "--gate", "runtime-closeout", "--confirm", "--json"],
      ["branch-follower", "debug-step", "--follower", "web-probe-sentinel-master", "--step", "state-write", "--confirm", "--json"],
      ["branch-follower", "cleanup-state", "--all", "--confirm", "--json"],
    ];
    for (const args of commands) {
      const rendered = await runBranchFollowerCommand(null, args);
      const payload = JSON.parse(rendered.renderedText) as Record<string, unknown>;
      expect(payload).toMatchObject({ ok: false, mutation: false });
      expect(JSON.stringify(payload.next)).not.toMatch(mutationCommand);
    }
    const forged = await runBranchFollowerCommand(null, ["branch-follower", "legacy-cicd", "apply", "--config", "/tmp/forged-legacy.yaml", "--confirm", "--json"]);
    expect(JSON.parse(forged.renderedText)).toMatchObject({
      ok: false,
      mutation: false,
      mode: "noncanonical-branch-follower-config-mutation-blocked",
    });
    expect(Object.keys((JSON.parse(forged.renderedText) as { next: Record<string, unknown> }).next).sort()).toEqual([
      "fixManualRelease",
      "status",
      "valuesPrinted",
    ]);
    await expect(runBranchFollowerCommand(null, ["branch-follower", "plan", "--json"])).rejects.toThrow("default surface is read-only");
  });

  test("branch-follower unknown 与多 node scope 不猜测 JD01 PaC 入口", () => {
    const options = { followerId: null } as ParsedOptions;
    const unknownRegistry = {
      followers: [{
        id: "unknown-fixture",
        deliveryAuthority: "unknown",
        source: { repository: "acme/unknown", branch: "main" },
        target: { node: "NODE-X", lane: "lane-x" },
      } as FollowerSpec],
    } as BranchFollowerRegistry;
    const unknownNext = branchFollowerReadOnlyNext(unknownRegistry, options);
    expect(Object.keys(unknownNext).sort()).toEqual(["events", "fixManualRelease", "logs", "status", "valuesPrinted"]);
    expect(JSON.stringify(unknownNext)).not.toContain("JD01");

    const catalog = readCicdDeliveryAuthorityCatalog();
    const consumers = ["hwlab-jd01-v03", "hwlab-nc01-v03"].map((id) => {
      const consumer = catalog.consumers.find((item) => item.consumerId === id);
      if (consumer === undefined) throw new Error(`missing fixture consumer ${id}`);
      return {
        id: `${id}-retired-follower`,
        deliveryAuthority: "retired-readonly",
        source: { repository: consumer.sourceRepository, branch: consumer.sourceBranch },
        target: { node: consumer.node, lane: consumer.lane },
      } as FollowerSpec;
    });
    const multiNodeNext = branchFollowerReadOnlyNext({ followers: consumers } as BranchFollowerRegistry, options);
    expect(Object.keys(multiNodeNext).sort()).toEqual(["events", "fixManualRelease", "logs", "status", "valuesPrinted"]);
    expect(JSON.stringify(multiNodeNext)).not.toContain("--target JD01");
  });

  test("retired branch-follower 与 Gitea Actions POC 默认帮助只读", async () => {
    expect(JSON.stringify(branchFollowerHelp())).not.toMatch(mutationCommand);
    expect(JSON.stringify(cicdGiteaActionsPocHelp())).not.toMatch(mutationCommand);
    await expect(runGiteaActionsPocCommand(null, ["apply"])).rejects.toThrow("intentionally unavailable");
  });

  test("platform-infra 默认 delivery 帮助和 AgentRun 失败帮助不暴露人工推进", () => {
    const platformHelp = platformInfraHelp() as { usage: string[] };
    const deliveryUsage = platformHelp.usage.filter((line) => line.includes("gitea") || line.includes("pipelines-as-code"));
    expect(JSON.stringify(deliveryUsage)).not.toMatch(mutationCommand);
    expect(deliveryUsage).toContain("bun scripts/cli.ts platform-infra gitea mirror webhook status --target NC01");
    expect(deliveryUsage).toContain("bun scripts/cli.ts platform-infra pipelines-as-code help platform-bootstrap");
    const agentRunOperations = unsupportedAgentRunCommand(["unexpected"]).renderedText.split("Operations:")[1] ?? "";
    expect(agentRunOperations).not.toMatch(mutationCommand);
    const root = runCliJson(["--help"]);
    expect(JSON.stringify(root)).not.toMatch(/platform-infra gitea apply|platform-infra gitea mirror sync/u);
  });
});

function runCliJson(args: string[]): { ok: boolean; command: string; data: unknown } {
  const result = spawnSync("bun", ["scripts/cli.ts", ...args], {
    cwd: process.cwd(),
    encoding: "utf8",
    timeout: 15_000,
  });
  if (result.status !== 0) throw new Error(`CLI ${args.join(" ")} failed: ${result.stderr}`);
  return JSON.parse(result.stdout) as { ok: boolean; command: string; data: unknown };
}
