import assert from "node:assert/strict";
import { execFile } from "node:child_process";
import { mkdir, readFile, writeFile } from "node:fs/promises";
import path from "node:path";
import { promisify } from "node:util";
import { fileURLToPath } from "node:url";

import {
  DEV_DB_ENV_CONTRACT,
  buildDbHealthContract,
  summarizeDbContract
} from "../../internal/cloud/db-contract.ts";
import {
  DEV_CODE_AGENT_PROVIDER_CONTRACT,
  codeAgentSecretRefPlaceholder
} from "../../internal/cloud/code-agent-contract.ts";
import { activeReportLifecycle } from "../../internal/dev-report-lifecycle.mjs";
import { DEV_ENDPOINT, ENVIRONMENT_DEV, SERVICE_IDS } from "../../internal/protocol/index.mjs";
import { probeRegistryCapabilities } from "./registry-capabilities.mjs";
import { ensureNotRepoReportsPath, tempReportPath } from "./report-paths.mjs";
import { readStructuredFile } from "./structured-config.mjs";

const execFileAsync = promisify(execFile);
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");
const defaultReportPath = tempReportPath("dev-preflight-report.json");
const defaultArtifactReportPath = tempReportPath("dev-artifacts.json");
const defaultEdgeHealthReportPath = tempReportPath("dev-edge-health.json");
const issue = "pikasTech/HWLAB#34";
const supports = ["#7", "#12", "#14", "#22", "#23", "#29", "#30", "#31", "#33", "#35", "#39", "#43", "#48", "#49", "#57", "#66", "#143", "#164"].map(
  (id) => `pikasTech/HWLAB${id}`
);
const forbiddenActions = [
  "prod-deploy",
  "secret-material-read",
  "unidesk-runtime-substitute",
  "heavy-e2e",
  "browser-e2e",
  "force-push",
  "runtime-restart"
];
const blockerTypes = new Set([
  "contract_blocker",
  "environment_blocker",
  "network_blocker",
  "runtime_blocker",
  "agent_blocker",
  "observability_blocker",
  "safety_blocker"
]);
const digestPattern = /^sha256:[a-f0-9]{64}$/;
const forbiddenDeployArtifactFields = new Set(["commitId", "image", "imageTag", "digest", "sourceCommitId", "repositoryDigest", "publishState"]);
const forbiddenDeployArtifactEnv = new Set([
  "HWLAB_COMMIT_ID",
  "HWLAB_IMAGE",
  "HWLAB_IMAGE_TAG",
  "HWLAB_IMAGE_DIGEST",
  "HWLAB_REVISION",
  "HWLAB_BUILD_CREATED_AT",
  "HWLAB_BUILD_SOURCE",
  "HWLAB_SKILLS_COMMIT_ID"
]);
const artifactBuildInputPrefixes = [
  "cmd/",
  "internal/",
  "skills/",
  "tools/",
  "web/"
];
const artifactBuildInputFiles = new Set([
  "package.json",
  "package-lock.json",
  "npm-shrinkwrap.json",
  "scripts/artifact-publish.mjs",
  "scripts/src/dev-artifact-services.mjs"
]);

function parseArgs(argv) {
  const args = {
    targetRef: "origin/main",
    reportPath: defaultReportPath,
    timeoutMs: 5000,
    writeReport: true,
    failOnBlocked: false
  };

  for (let index = 0; index < argv.length; index += 1) {
    const arg = argv[index];
    if (arg === "--target-ref") {
      args.targetRef = argv[++index];
    } else if (arg === "--report") {
      args.reportPath = argv[++index];
    } else if (arg === "--timeout-ms") {
      args.timeoutMs = Number.parseInt(argv[++index], 10);
    } else if (arg === "--no-write") {
      args.writeReport = false;
    } else if (arg === "--fail-on-blocked") {
      args.failOnBlocked = true;
    } else if (arg === "--help") {
      args.help = true;
    } else {
      throw new Error(`unknown argument ${arg}`);
    }
  }

  assert.ok(Number.isInteger(args.timeoutMs) && args.timeoutMs > 0, "--timeout-ms must be positive");
  return args;
}

function usage() {
  return `Usage: node scripts/dev-gate-preflight.mjs [--target-ref origin/main] [--report ${defaultReportPath}] [--timeout-ms 5000] [--no-write] [--fail-on-blocked]`;
}

function commandLine(command, args) {
  return [command, ...args].join(" ");
}

async function run(command, args = [], options = {}) {
  const commandText = commandLine(command, args);
  try {
    const result = await execFileAsync(command, args, {
      cwd: repoRoot,
      timeout: options.timeoutMs ?? 5000,
      maxBuffer: 1024 * 1024
    });
    return {
      ok: true,
      command: commandText,
      exitCode: 0,
      stdout: result.stdout.trim(),
      stderr: result.stderr.trim()
    };
  } catch (error) {
    return {
      ok: false,
      command: commandText,
      exitCode: typeof error.code === "number" ? error.code : 1,
      stdout: String(error.stdout ?? "").trim(),
      stderr: String(error.stderr ?? "").trim(),
      error: error.message
    };
  }
}

async function commandExists(command) {
  return (await run("which", [command])).ok;
}

async function readJson(relativePath) {
  return readStructuredFile(repoRoot, relativePath);
}

async function readOptionalJson(relativePath) {
  try {
    return await readJson(relativePath);
  } catch (error) {
    if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") {
      return null;
    }
    throw error;
  }
}

async function parseToml(relativePath) {
  const absolutePath = path.join(repoRoot, relativePath);
  const code = [
    "import json,sys,tomllib",
    "with open(sys.argv[1], 'rb') as handle:",
    "    print(json.dumps(tomllib.load(handle), sort_keys=True))"
  ].join("\n");
  const result = await run("python3", ["-c", code, absolutePath]);
  if (!result.ok) {
    throw new Error(`could not parse ${relativePath}: ${result.stderr || result.error}`);
  }
  return JSON.parse(result.stdout);
}

function fetchErrorMessage(error) {
  if (!(error instanceof Error)) {
    return String(error);
  }
  const cause = error.cause;
  if (cause && typeof cause === "object") {
    const code = "code" in cause ? String(cause.code) : "";
    const address = "address" in cause ? String(cause.address) : "";
    const port = "port" in cause ? String(cause.port) : "";
    return [error.message, code, address, port].filter(Boolean).join(" ");
  }
  return error.message;
}

async function httpProbe(url, { method = "GET", timeoutMs = 5000, headers = {} } = {}) {
  const controller = new AbortController();
  const timer = setTimeout(() => controller.abort(), timeoutMs);
  try {
    const response = await fetch(url, {
      method,
      headers,
      signal: controller.signal
    });
    const body = method === "HEAD" ? "" : await response.text();
    return {
      ok: response.ok,
      url,
      method,
      status: response.status,
      statusText: response.statusText,
      body: body.slice(0, 500)
    };
  } catch (error) {
    return {
      ok: false,
      url,
      method,
      error: fetchErrorMessage(error)
    };
  } finally {
    clearTimeout(timer);
  }
}

function unique(values) {
  return new Set(values).size === values.length;
}

function addBlocker(blockers, blocker) {
  assert.ok(blockerTypes.has(blocker.type), `unknown blocker type ${blocker.type}`);
  const key = `${blocker.type}:${blocker.scope}`;
  if (!blockers.some((item) => `${item.type}:${item.scope}` === key)) {
    blockers.push({ status: "open", ...blocker });
  }
}

function imageToManifestUrl(image) {
  const match = image.match(/^ghcr\.io\/pikastech\/([^:@]+):([^:@]+)$/);
  if (match) return `https://ghcr.io/v2/pikastech/${match[1]}/manifests/${match[2]}`;

  const localMatch = image.match(/^(127\.0\.0\.1:5000|localhost:5000)\/hwlab\/([^:@]+):([^:@]+)$/);
  if (localMatch) return `http://${localMatch[1]}/v2/hwlab/${localMatch[2]}/manifests/${localMatch[3]}`;
  return null;
}

function matchesTargetCommit(value, targetCommit, targetShortCommit) {
  return value === targetCommit || value === targetShortCommit;
}

function isArtifactBuildInputPath(relativePath) {
  return artifactBuildInputFiles.has(relativePath) ||
    artifactBuildInputPrefixes.some((prefix) => relativePath.startsWith(prefix));
}

async function resolveCommit(ref) {
  if (!ref) return null;
  const result = await run("git", ["rev-parse", `${ref}^{commit}`]);
  if (!result.ok || !/^[a-f0-9]{40}$/u.test(result.stdout)) return null;
  return result.stdout;
}

async function sourceCoverageFor({ catalog, artifactReport, targetCommit, targetShortCommit, targetRef }) {
  const reportSource = artifactReport?.artifactPublish?.sourceCommitId ?? artifactReport?.commitId ?? null;
  const sourceRef = reportSource ?? catalog.commitId;
  const sourceKind = reportSource ? "artifact-publish-report" : "artifact-catalog";
  const sourceCommit = await resolveCommit(sourceRef);

  if (!sourceCommit) {
    return {
      sourceKind,
      sourceRef,
      sourceCommitId: null,
      sourceShortCommitId: null,
      targetRef,
      targetCommitId: targetCommit,
      targetShortCommitId: targetShortCommit,
      mode: "unresolved-source",
      covered: false,
      artifactBuildInputChanged: true,
      changedPathCount: 0,
      changedArtifactInputPaths: [],
      changedNonArtifactPathCount: 0,
      reason: `artifact source ${sourceRef ?? "unknown"} does not resolve to a local git commit`
    };
  }

  if (matchesTargetCommit(sourceCommit, targetCommit, targetShortCommit)) {
    return {
      sourceKind,
      sourceRef,
      sourceCommitId: sourceCommit,
      sourceShortCommitId: sourceCommit.slice(0, 7),
      targetRef,
      targetCommitId: targetCommit,
      targetShortCommitId: targetShortCommit,
      mode: "exact-target",
      covered: true,
      artifactBuildInputChanged: false,
      changedPathCount: 0,
      changedArtifactInputPaths: [],
      changedNonArtifactPathCount: 0,
      reason: "artifact source commit exactly matches the target ref"
    };
  }

  const ancestor = await run("git", ["merge-base", "--is-ancestor", sourceCommit, targetCommit]);
  if (!ancestor.ok) {
    return {
      sourceKind,
      sourceRef,
      sourceCommitId: sourceCommit,
      sourceShortCommitId: sourceCommit.slice(0, 7),
      targetRef,
      targetCommitId: targetCommit,
      targetShortCommitId: targetShortCommit,
      mode: "non-ancestor-source",
      covered: false,
      artifactBuildInputChanged: true,
      changedPathCount: 0,
      changedArtifactInputPaths: [],
      changedNonArtifactPathCount: 0,
      reason: `artifact source ${sourceCommit.slice(0, 12)} is not an ancestor of ${targetRef} ${targetShortCommit}`
    };
  }

  const diff = await run("git", ["diff", "--name-only", `${sourceCommit}..${targetCommit}`]);
  if (!diff.ok) {
    return {
      sourceKind,
      sourceRef,
      sourceCommitId: sourceCommit,
      sourceShortCommitId: sourceCommit.slice(0, 7),
      targetRef,
      targetCommitId: targetCommit,
      targetShortCommitId: targetShortCommit,
      mode: "diff-unavailable",
      covered: false,
      artifactBuildInputChanged: true,
      changedPathCount: 0,
      changedArtifactInputPaths: [],
      changedNonArtifactPathCount: 0,
      reason: diff.stderr || diff.error || "git diff failed"
    };
  }

  const changedPaths = diff.stdout.split("\n").map((line) => line.trim()).filter(Boolean);
  const changedArtifactInputPaths = changedPaths.filter(isArtifactBuildInputPath);
  const covered = changedArtifactInputPaths.length === 0;
  return {
    sourceKind,
    sourceRef,
    sourceCommitId: sourceCommit,
    sourceShortCommitId: sourceCommit.slice(0, 7),
    targetRef,
    targetCommitId: targetCommit,
    targetShortCommitId: targetShortCommit,
    mode: covered ? "report-only-target" : "artifact-input-drift",
    covered,
    artifactBuildInputChanged: changedArtifactInputPaths.length > 0,
    changedPathCount: changedPaths.length,
    changedArtifactInputPaths,
    changedNonArtifactPathCount: changedPaths.length - changedArtifactInputPaths.length,
    reason: covered
      ? "target changes since the artifact source do not touch artifact build inputs"
      : "target changes since the artifact source touch artifact build inputs"
  };
}

function uniqueSorted(values) {
  return [...new Set(values)].sort();
}

function catalogDigestCounts(catalog) {
  const counts = {
    sha256: 0,
    notPublished: 0,
    invalid: 0
  };

  for (const service of catalog.services ?? []) {
    if (digestPattern.test(service.digest)) counts.sha256 += 1;
    else if (service.digest === "not_published") counts.notPublished += 1;
    else counts.invalid += 1;
  }

  return counts;
}

function requiredCatalogServices(catalog) {
  return (catalog.services ?? []).filter((service) => service.artifactRequired !== false);
}

function disabledCatalogServices(catalog) {
  return (catalog.services ?? []).filter((service) => service.artifactRequired === false);
}

async function artifactIdentityFor({ deploy, catalog, artifactReport, targetCommit, targetShortCommit, targetRef }) {
  const targetCoverage = await sourceCoverageFor({
    catalog,
    artifactReport,
    targetCommit,
    targetShortCommit,
    targetRef
  });
  const artifactSourceCommit = targetCoverage.sourceCommitId ?? targetCommit;
  const artifactSourceShortCommit = targetCoverage.sourceShortCommitId ?? targetShortCommit;
  const serviceCommitIds = uniqueSorted((catalog.services ?? []).map((service) => service.commitId));
  const digestCounts = catalogDigestCounts(catalog);
  const requiredServices = requiredCatalogServices(catalog);
  const disabledServices = disabledCatalogServices(catalog);
  const catalogMatchesSource = matchesTargetCommit(catalog.commitId, artifactSourceCommit, artifactSourceShortCommit);
  const servicesMatchSource = serviceCommitIds.length === 1 &&
    matchesTargetCommit(serviceCommitIds[0], artifactSourceCommit, artifactSourceShortCommit);
  const allRequiredDigestsPublished = requiredServices.length > 0 &&
    requiredServices.every((service) => digestPattern.test(service.digest) && service.publishState === "published") &&
    disabledServices.every((service) => service.digest === "not_published" && service.publishState === "skeleton-only") &&
    digestCounts.invalid === 0;
  const publishVerified = catalog.publish?.ciPublished === true &&
    catalog.publish?.registryVerified === true &&
    catalog.artifactState === "published" &&
    allRequiredDigestsPublished;

  return {
    source: {
      ref: targetRef,
      commitId: targetCommit,
      shortCommitId: targetShortCommit
    },
    artifactSource: {
      source: targetCoverage.sourceKind,
      ref: targetCoverage.sourceRef,
      commitId: targetCoverage.sourceCommitId,
      shortCommitId: targetCoverage.sourceShortCommitId
    },
    targetCoverage,
    deployManifest: {
      path: "deploy/deploy.yaml",
      identitySource: "human-authored-config",
      artifactIdentity: false,
      matchesSource: true,
      matchesTarget: true
    },
    artifactCatalog: {
      path: "deploy/artifact-catalog.dev.json",
      commitId: catalog.commitId,
      artifactState: catalog.artifactState,
      ciPublished: catalog.publish?.ciPublished === true,
      registryVerified: catalog.publish?.registryVerified === true,
      provenance: catalog.publish?.provenance ?? "unknown",
      digestCounts,
      requiredServiceCount: requiredServices.length,
      disabledServiceCount: disabledServices.length,
      matchesSource: catalogMatchesSource,
      matchesTarget: matchesTargetCommit(catalog.commitId, targetCommit, targetShortCommit)
    },
    services: (catalog.services ?? []).map((service) => ({
      serviceId: service.serviceId,
      commitId: service.commitId,
      matchesSource: matchesTargetCommit(service.commitId, artifactSourceCommit, artifactSourceShortCommit),
      matchesTarget: matchesTargetCommit(service.commitId, targetCommit, targetShortCommit),
      image: service.image,
      imageTag: service.imageTag,
      digest: service.digest,
      publishState: service.publishState,
      artifactRequired: service.artifactRequired !== false,
      notPublishedReason: service.notPublishedReason ?? null
    })),
    serviceCommitIds,
    matchesSource: targetCoverage.covered && catalogMatchesSource && servicesMatchSource,
    publishVerified,
    refreshCommands: {
      blocked: `node scripts/refresh-artifact-catalog.mjs --target-ref ${targetRef} --blocked --no-write`,
      published: `node scripts/refresh-artifact-catalog.mjs --target-ref ${targetRef} --publish-report ${defaultArtifactReportPath} --no-write`
    }
  };
}

function assertStaticContract(deploy, catalog) {
  assert.equal(deploy.environment, ENVIRONMENT_DEV, "deploy environment must be dev");
  assert.equal(deploy.namespace, "hwlab-dev", "deploy namespace must be hwlab-dev");
  assert.equal(deploy.endpoint, DEV_ENDPOINT, "deploy endpoint must stay frozen");
  assert.equal(deploy.profiles.dev.enabled, true, "dev profile must be enabled");
  assert.equal(deploy.profiles.prod.enabled, false, "prod profile must stay disabled");
  assert.deepEqual(deploy.services.map((service) => service.serviceId), SERVICE_IDS);
  for (const field of Object.keys(deploy)) {
    assert.equal(forbiddenDeployArtifactFields.has(field), false, `deploy ${field} must stay in artifact catalog`);
  }
  for (const service of deploy.services) {
    for (const field of Object.keys(service)) {
      assert.equal(forbiddenDeployArtifactFields.has(field), false, `deploy service ${service.serviceId}.${field} must stay in artifact catalog`);
    }
    for (const envName of Object.keys(service.env ?? {})) {
      assert.equal(forbiddenDeployArtifactEnv.has(envName), false, `deploy service ${service.serviceId}.env.${envName} must be rendered from artifact catalog`);
    }
  }

  assert.equal(catalog.environment, ENVIRONMENT_DEV, "catalog environment must be dev");
  assert.equal(catalog.namespace, "hwlab-dev", "catalog namespace must be hwlab-dev");
  assert.equal(catalog.endpoint, DEV_ENDPOINT, "catalog endpoint must stay frozen");
  assert.deepEqual(catalog.allowedProfiles, [ENVIRONMENT_DEV]);
  assert.ok(catalog.forbiddenProfiles.includes("prod"), "catalog must forbid prod");
  assert.deepEqual(catalog.services.map((service) => service.serviceId), SERVICE_IDS);
  assert.ok(unique(catalog.services.map((service) => service.serviceId)), "catalog service IDs must be unique");

  const deployByService = new Map(deploy.services.map((service) => [service.serviceId, service]));
  for (const service of catalog.services) {
    const deployService = deployByService.get(service.serviceId);
    assert.ok(deployService, `${service.serviceId} missing from deploy manifest`);
    assert.equal(service.namespace, "hwlab-dev", `${service.serviceId} namespace mismatch`);
    assert.equal(service.profile, ENVIRONMENT_DEV, `${service.serviceId} profile mismatch`);
    assert.equal(service.healthPath, "/health/live", `${service.serviceId} health path mismatch`);
  }
}

function assertK8sStatic(namespace, workloads, services, devKustomization, healthContract) {
  assert.equal(namespace.kind, "Namespace");
  assert.equal(namespace.metadata.name, "hwlab-dev");
  assert.deepEqual(devKustomization.resources, ["../base", "health-contract.yaml"]);
  assert.equal(devKustomization.namespace, "hwlab-dev");
  assert.equal(healthContract.kind, "ConfigMap");
  assert.equal(healthContract.metadata.namespace, "hwlab-dev");
  assert.equal(healthContract.data.endpoint, DEV_ENDPOINT);
  assert.ok(healthContract.data["runtime-substitute-policy"].includes("Do not replace HWLAB runtime"));

  const resources = [...workloads.items, ...services.items, healthContract];
  assert.ok(resources.length > SERVICE_IDS.length, "k8s resources must cover DEV services");
  for (const resource of resources) {
    assert.notEqual(resource.kind, "Secret", "preflight contract must not require Secret resources");
    if (resource.metadata?.namespace) {
      assert.equal(resource.metadata.namespace, "hwlab-dev", `${resource.kind}/${resource.metadata.name} namespace`);
    }
  }
}

function assertFrpStatic(frpc, frps, masterEdge) {
  assert.equal(frpc.serverAddr, "74.48.78.17");
  assert.equal(frpc.serverPort, 7000);
  assert.equal(frps.bindPort, 7000);
  assert.equal(Object.hasOwn(frps, "vhostHTTPPort"), false, "frps must not bind vhostHTTPPort because 16667 is reserved for the TCP proxy");
  assert.ok(frpc.proxies.some((proxy) => proxy.name === "hwlab-dev-edge-proxy" && proxy.remotePort === 16667));
  assert.ok(frpc.proxies.some((proxy) => proxy.name === "hwlab-dev-cloud-web" && proxy.remotePort === 16666));
  assert.ok(frps.allowPorts.some((port) => port.start === 16667 && port.end === 16667));
  assert.ok(frps.allowPorts.some((port) => port.start === 16666 && port.end === 16666));
  assert.equal(masterEdge.environment, ENVIRONMENT_DEV);
  assert.equal(masterEdge.endpoint, DEV_ENDPOINT);
  assert.equal(masterEdge.reverseLink.mode, "frp");
  assert.equal(masterEdge.reverseLink.direction, "d601-to-master");
  assert.equal(masterEdge.prodAcceptance, false);
}

async function loadContracts() {
  return Promise.all([
    readJson("deploy/deploy.yaml"),
    readJson("deploy/artifact-catalog.dev.json"),
    readJson("deploy/k8s/base/namespace.yaml"),
    readJson("deploy/k8s/base/workloads.yaml"),
    readJson("deploy/k8s/base/services.yaml"),
    readJson("deploy/k8s/dev/kustomization.yaml"),
    readJson("deploy/k8s/dev/health-contract.yaml"),
    readJson("deploy/master-edge/health-contract.json")
  ]);
}

async function loadOptionalReports() {
  return {
    artifactPublish: await readOptionalJson(defaultArtifactReportPath),
    edgeHealth: await readOptionalJson(defaultEdgeHealthReportPath)
  };
}

function makeReporter() {
  const checks = [];
  const blockers = [];
  return {
    checks,
    blockers,
    check(id, category, status, summary, evidence = []) {
      checks.push({ id, category, status, summary, evidence });
    },
    block(blocker) {
      addBlocker(blockers, blocker);
    }
  };
}

async function validateLocalContracts(reporter, contracts, optionalReports, targetShortCommit, targetCommit, targetRef) {
  const [deploy, catalog, namespace, workloads, services, devKustomization, healthContract, masterEdge] = contracts;
  const artifactIdentity = await artifactIdentityFor({
    deploy,
    catalog,
    artifactReport: optionalReports.artifactPublish,
    targetCommit,
    targetShortCommit,
    targetRef
  });

  try {
    assertStaticContract(deploy, catalog);
    reporter.check("source-contract-static", "contract", "pass", "Local deploy manifest and artifact catalog are internally consistent.");
  } catch (error) {
    reporter.check("source-contract-static", "contract", "blocked", error.message);
    reporter.block({
      type: "contract_blocker",
      scope: "source-contract",
      summary: error.message,
      nextTask: "Repair the local DEV deploy manifest and artifact catalog contract before any live deploy."
    });
  }

  if (artifactIdentity.matchesSource) {
    const coverage = artifactIdentity.targetCoverage;
    const coverageSuffix = coverage.mode === "exact-target"
      ? "exact target commit"
      : `${coverage.mode}; ${coverage.changedPathCount} changed path(s), ${coverage.changedArtifactInputPaths.length} artifact input change(s)`;
    reporter.check(
      "target-commit-pinning",
      "contract",
      "pass",
      `Source ${targetRef} ${targetShortCommit} is covered by artifact source ${artifactIdentity.artifactSource.shortCommitId}; ${coverageSuffix}.`,
      [artifactIdentity]
    );
  } else {
    const coverage = artifactIdentity.targetCoverage;
    reporter.check(
      "target-commit-pinning",
      "contract",
      "blocked",
      `Source ${targetRef} ${targetShortCommit} is not covered by artifact source ${artifactIdentity.artifactSource.shortCommitId ?? "unknown"}: ${coverage.reason}.`,
      [artifactIdentity]
    );
    reporter.block({
      type: "contract_blocker",
      scope: "artifact-source-commit",
      summary: `source commit ${targetRef} ${targetShortCommit} is not covered by artifact source ${artifactIdentity.artifactSource.shortCommitId ?? "unknown"}; ${coverage.reason}.`,
      nextTask: `Refresh without fake digests using \`${artifactIdentity.refreshCommands.blocked}\`, or after a successful publish run \`${artifactIdentity.refreshCommands.published}\`.`
    });
  }

  if (artifactIdentity.publishVerified) {
    reporter.check(
      "artifact-catalog-publish-state",
      "registry",
      "pass",
      "Artifact catalog records CI publish, registry verification, and immutable service digests.",
      [artifactIdentity.artifactCatalog]
    );
  } else {
    reporter.check(
      "artifact-catalog-publish-state",
      "registry",
      "blocked",
      `Artifact catalog publish state is ${catalog.artifactState}; ciPublished=${artifactIdentity.artifactCatalog.ciPublished}, registryVerified=${artifactIdentity.artifactCatalog.registryVerified}, digests sha256=${artifactIdentity.artifactCatalog.digestCounts.sha256}, not_published=${artifactIdentity.artifactCatalog.digestCounts.notPublished}, invalid=${artifactIdentity.artifactCatalog.digestCounts.invalid}.`,
      [artifactIdentity.artifactCatalog]
    );
    reporter.block({
      type: "runtime_blocker",
      scope: "artifact-catalog",
      summary: `deploy/artifact-catalog.dev.json does not prove published artifacts for ${targetRef} ${targetShortCommit}; ciPublished=${artifactIdentity.artifactCatalog.ciPublished}, registryVerified=${artifactIdentity.artifactCatalog.registryVerified}, not_published=${artifactIdentity.artifactCatalog.digestCounts.notPublished}.`,
      nextTask: `Run the DEV artifact publish workflow, then record only real sha256 digests with \`${artifactIdentity.refreshCommands.published}\`; if publish is still blocked, keep not_published via \`${artifactIdentity.refreshCommands.blocked}\`.`
    });
  }

  try {
    assertK8sStatic(namespace, workloads, services, devKustomization, healthContract);
    reporter.check("k8s-manifest-static", "k3s", "pass", "DEV k3s manifest files parse and stay scoped to hwlab-dev.");
  } catch (error) {
    reporter.check("k8s-manifest-static", "k3s", "blocked", error.message);
    reporter.block({
      type: "contract_blocker",
      scope: "deploy/k8s/dev",
      summary: error.message,
      nextTask: "Repair the DEV k3s manifests so they parse and target only hwlab-dev."
    });
  }

  return { deploy, catalog, masterEdge, artifactIdentity };
}

function validateArtifactPublishReport(reporter, artifactReport, artifactIdentity, targetShortCommit, targetCommit, targetRef) {
  if (!artifactReport) {
    reporter.check("artifact-publish-report", "registry", "blocked", "No node artifact publish report is present.");
    reporter.block({
      type: "runtime_blocker",
      scope: "artifact-publish",
      summary: `${defaultArtifactReportPath} is missing, so the preflight has no publish evidence for HWLAB runtime artifacts.`,
      nextTask: `Run the DEV artifact publish workflow and keep its temporary JSON at ${defaultArtifactReportPath}; do not commit report output.`
    });
    return;
  }

  const artifactPublish = artifactReport.artifactPublish;
  const services = artifactPublish?.services ?? [];
  const sourcePresentCount = services.filter((service) => service.sourceState === "source-present").length;
  const intentionallyDisabledCount = services.filter((service) => service.sourceState === "intentionally-disabled").length;
  const sourcePresenceResolved =
    services.length === SERVICE_IDS.length &&
    services.every((service) => service.sourceState === "source-present" || service.sourceState === "intentionally-disabled");
  const requiredServices = services.filter((service) => service.artifactRequired !== false);
  const disabledServices = services.filter((service) => service.artifactRequired === false);
  const allServicesAccountedFor = services.length === SERVICE_IDS.length &&
    [...services].map((service) => service.serviceId).sort().join("\n") === [...SERVICE_IDS].sort().join("\n");
  const allRequiredServicesPublished = requiredServices.length > 0 &&
    requiredServices.every((service) => service.status === "published" && /^sha256:[a-f0-9]{64}$/.test(service.digest));
  const disabledServicesNotPublished = disabledServices.every(
    (service) => service.digest === "not_published" && service.notPublishedReason?.startsWith("disabled_")
  );
  const sourceMatchesTarget = [targetCommit, targetShortCommit].includes(artifactPublish?.sourceCommitId) ||
    [targetCommit, targetShortCommit].includes(artifactReport.commitId);
  const sourceCoversTarget = artifactIdentity?.targetCoverage?.covered === true &&
    artifactIdentity?.artifactSource?.commitId &&
    (
      artifactPublish?.sourceCommitId === artifactIdentity.artifactSource.commitId ||
      artifactPublish?.sourceCommitId === artifactIdentity.artifactSource.shortCommitId ||
      artifactReport.commitId === artifactIdentity.artifactSource.commitId ||
      artifactReport.commitId === artifactIdentity.artifactSource.shortCommitId
    );
  const publishReady = artifactPublish?.status === "published" &&
    artifactPublish.publishedCount === requiredServices.length &&
    allServicesAccountedFor &&
    allRequiredServicesPublished &&
    disabledServicesNotPublished &&
    (sourceMatchesTarget || sourceCoversTarget);

  if (publishReady) {
    const summary = sourceMatchesTarget
      ? "DEV artifact publish report covers all required enabled service IDs with immutable digests for the target commit, and records disabled service reasons."
      : `DEV artifact publish report covers artifact source ${artifactIdentity.artifactSource.shortCommitId}; target ${targetRef} ${targetShortCommit} has no artifact build input changes since that source.`;
    reporter.check("artifact-publish-report", "registry", "pass", summary, [artifactIdentity?.targetCoverage].filter(Boolean));
    return;
  }

  const publishedCount = artifactPublish?.publishedCount ?? 0;
  const serviceCount = artifactPublish?.serviceCount ?? SERVICE_IDS.length;
  const sourceSummary = sourcePresenceResolved
    ? `source states resolved: ${sourcePresentCount} source-present, ${intentionallyDisabledCount} intentionally-disabled`
    : "source states are not fully resolved";
  reporter.check(
    "artifact-publish-report",
    "registry",
    "blocked",
    `DEV artifact publish report status is ${artifactPublish?.status ?? "missing"} with ${publishedCount}/${requiredServices.length || serviceCount} required services published for source ${artifactPublish?.sourceCommitId ?? artifactReport.commitId ?? "unknown"}; ${sourceSummary}.`
  );
  reporter.block({
    type: "runtime_blocker",
    scope: "artifact-publish",
    summary: `${defaultArtifactReportPath} does not prove all required HWLAB service artifacts for ${targetRef} ${targetShortCommit}; current status is ${artifactPublish?.status ?? "missing"} with ${publishedCount}/${requiredServices.length || serviceCount} required services published and ${sourceSummary}.`,
    nextTask: "Complete DEV artifact publishing for every enabled required HWLAB service at the artifact source commit, or prove the target commit has no artifact build input changes; keep disabled services marked not_published with reasons."
  });
}

function validateRegistryCapabilities(reporter, registryCapabilities) {
  for (const dimension of Object.values(registryCapabilities.dimensions)) {
    reporter.check(
      `registry-capability-${dimension.id}`,
      "registry",
      dimension.status,
      dimension.summary,
      [dimension]
    );
  }

  reporter.check(
    "registry-capability-classification",
    "registry",
    registryCapabilities.classification,
    `Registry capability classification is ${registryCapabilities.classification}: BuildKit publish registry API, legacy Docker diagnostic, and k3s pull path are reported separately.`,
    [registryCapabilities]
  );

  const dockerDaemonPushAccess = registryCapabilities.dimensions.dockerDaemonPushAccess;
  if (dockerDaemonPushAccess.requiredForPublish && dockerDaemonPushAccess.status === "blocked") {
    reporter.block({
      type: "network_blocker",
      scope: "docker-daemon-push-access",
      summary: dockerDaemonPushAccess.summary,
      nextTask: "Restore the required publish-path registry access before claiming DEV artifact publish readiness."
    });
  }

  const k3sPullAccess = registryCapabilities.dimensions.k3sPullAccess;
  if (k3sPullAccess.status === "blocked") {
    reporter.block({
      type: "network_blocker",
      scope: "k3s-pull-access",
      summary: k3sPullAccess.summary,
      nextTask: "Repair read-only hwlab-dev k3s access or image pull configuration before claiming deploy-path registry readiness."
    });
  }
}

function manifestReadStatus(registryEvidence) {
  if (registryEvidence.every((probe) => probe.ok)) return "pass";
  if (registryEvidence.some((probe) => probe.error || probe.status === 401 || probe.status === 403 || probe.status === 404)) {
    return "degraded";
  }
  return "blocked";
}

function validateEdgeHealthReport(reporter, edgeReport) {
  if (!edgeReport) {
    reporter.check("dev-edge-health-report", "edge", "blocked", "No DEV edge health report is present.");
    reporter.block({
      type: "network_blocker",
      scope: "dev-edge-health-report",
      summary: `${defaultEdgeHealthReportPath} is missing, so the preflight has no temporary edge/frp health JSON.`,
      nextTask: `Run the read-only DEV edge health smoke and keep its temporary JSON at ${defaultEdgeHealthReportPath}; do not commit report output.`
    });
    return;
  }

  const edgeHealth = edgeReport.edgeHealth;
  const pass = edgeReport.status === "pass" && edgeHealth?.status === "pass";
  const evidence = [
    {
      report: defaultEdgeHealthReportPath,
      status: edgeReport.status,
      classification: edgeHealth?.classification ?? "unknown",
      publicTcp: edgeHealth?.publicTcp ?? [],
      kubernetesObservable: edgeHealth?.kubernetes?.observable ?? false
    }
  ];

  if (pass) {
    reporter.check("dev-edge-health-report", "edge", "pass", "Committed DEV edge health report shows public edge health passing.", evidence);
    return;
  }

  const classification = edgeHealth?.classification ?? "unknown";
  const summary = edgeHealth?.blocker ?? edgeReport.devPreconditions?.summary ?? "DEV edge health report is not passing.";
  reporter.check("dev-edge-health-report", "edge", "blocked", `Committed DEV edge health report is ${edgeReport.status} with classification ${classification}.`, evidence);
  reporter.block({
    type: "network_blocker",
    scope: edgeHealth?.diagnosis?.likelyLayer ?? "dev-edge-health",
    summary,
    nextTask: edgeHealth?.diagnosis?.nextTask ?? "Repair the master frps/public DEV edge route and rerun the read-only DEV edge health smoke before real deployment."
  });
}

function validateCloudApiDbContract(reporter, deploy, workloads, services) {
  const staticContract = inspectCloudApiDbStaticContract(deploy, workloads, services);
  const healthContract = summarizeDbContract(buildDbHealthContract());
  const evidence = [
    {
      staticContract,
      healthContract,
      fixtureEvidence: false,
      liveDbEvidence: false
    }
  ];

  if (staticContract.ready) {
    reporter.check(
      "cloud-api-db-env-contract",
      "db",
      "pass",
      "cloud-api DEV DB manifest declares the required env names and redacted Secret reference; optional cloud-api-db alias is not a readiness gate.",
      evidence
    );
  } else {
    const missing = [
      ...staticContract.missingDeployEnv,
      ...staticContract.missingK8sEnv,
      ...staticContract.missingSecretRefs,
      ...staticContract.mismatchedNonSecretEnv,
      ...staticContract.missingDnsContract
    ];
    reporter.check(
      "cloud-api-db-env-contract",
      "db",
      "blocked",
      `cloud-api DEV DB manifest contract is blocked by ${missing.join(", ")}.`,
      evidence
    );
    reporter.block({
      type: "contract_blocker",
      scope: "cloud-api-db-env-contract",
      summary: `cloud-api DEV DB contract is incomplete; blocked by ${missing.join(", ")}.`,
      nextTask: "Repair deploy/deploy.yaml and deploy/k8s/base/workloads.yaml so they expose the required DB env names, redacted Secret reference, and HWLAB_CLOUD_DB_SSL_MODE=disable. Do not make cloud-api-db a hard readiness gate unless this repo also owns its Service/Endpoint rollout contract."
    });
  }

  if (healthContract.ready) {
    reporter.check(
      "cloud-api-db-health-gate",
      "db",
      "pass",
      "cloud-api DB health gate reports required env presence with redacted values.",
      evidence
    );
    return;
  }

  reporter.check(
    "cloud-api-db-health-gate",
    "db",
    "blocked",
    `cloud-api DB health gate is missing ${healthContract.missingEnv.join(", ")}.`,
    evidence
  );
  reporter.block({
    type: "runtime_blocker",
    scope: "cloud-api-db-health-gate",
    summary: `cloud-api DB runtime env is not ready; missing ${healthContract.missingEnv.join(", ")}.`,
    nextTask: "Configure DEV hwlab-cloud-api with Secret hwlab-cloud-api-dev-db/database-url and HWLAB_CLOUD_DB_SSL_MODE=disable, then rerun health/preflight without printing the secret value."
  });
}

function inspectCloudApiDbStaticContract(deploy, workloads, services) {
  const cloudApi = deploy?.services?.find((service) => service.serviceId === "hwlab-cloud-api") ?? {};
  const deployEnv = cloudApi.env ?? {};
  const workloadEnv = getWorkloadEnvByServiceId(workloads, "hwlab-cloud-api");
  const secretEnvNames = new Set(DEV_DB_ENV_CONTRACT.secretRefs.map((ref) => ref.env));
  const fields = DEV_DB_ENV_CONTRACT.requiredEnv.map((name) => {
    const secretRef = DEV_DB_ENV_CONTRACT.secretRefs.find((ref) => ref.env === name) ?? null;
    const workloadEntry = workloadEnv.get(name) ?? null;
    const manifestPresent = Object.hasOwn(deployEnv, name);
    const k8sPresent = workloadEnv.has(name);
    const secretRefMatches = secretRef
      ? deployEnv[name] === `secretRef:${secretRef.secretName}/${secretRef.secretKey}` &&
        workloadEntry?.valueFrom?.secretKeyRef?.name === secretRef.secretName &&
        workloadEntry?.valueFrom?.secretKeyRef?.key === secretRef.secretKey
      : true;

    return {
      name,
      manifestPresent,
      k8sPresent,
      redacted: secretEnvNames.has(name),
      source: secretEnvNames.has(name) ? "k8s-secret-ref" : "runtime-env",
      secretRef: secretRef
        ? {
            secretName: secretRef.secretName,
            secretKey: secretRef.secretKey,
            present: secretRefMatches,
            redacted: true
          }
        : null
    };
  });
  const missingDeployEnv = fields.filter((field) => !field.manifestPresent).map((field) => field.name);
  const missingK8sEnv = fields.filter((field) => !field.k8sPresent).map((field) => field.name);
  const missingSecretRefs = fields
    .filter((field) => field.secretRef && !field.secretRef.present)
    .map((field) => `${field.secretRef.secretName}/${field.secretRef.secretKey}`);
  const expectedSslMode = DEV_DB_ENV_CONTRACT.nonSecretDefaults.HWLAB_CLOUD_DB_SSL_MODE;
  const sslModeMatches =
    deployEnv.HWLAB_CLOUD_DB_SSL_MODE === expectedSslMode &&
    workloadEnv.get("HWLAB_CLOUD_DB_SSL_MODE")?.value === expectedSslMode;
  const mismatchedNonSecretEnv = sslModeMatches
    ? []
    : [`HWLAB_CLOUD_DB_SSL_MODE must be ${expectedSslMode}`];
  const dnsContract = inspectCloudApiDbOptionalAliasContract(deployEnv, workloadEnv, services);

  return {
    contractVersion: DEV_DB_ENV_CONTRACT.contractVersion,
    environment: DEV_DB_ENV_CONTRACT.environment,
    ready:
      missingDeployEnv.length === 0 &&
      missingK8sEnv.length === 0 &&
      missingSecretRefs.length === 0 &&
      mismatchedNonSecretEnv.length === 0 &&
      dnsContract.ready,
    requiredEnv: fields,
    dns: dnsContract,
    missingDeployEnv,
    missingK8sEnv,
    missingSecretRefs,
    mismatchedNonSecretEnv,
    missingDnsContract: dnsContract.missing,
    secretMaterialRead: false,
    valuesRedacted: true,
    liveDbEvidence: false,
    fixtureEvidence: false
  };
}

function inspectCloudApiDbOptionalAliasContract(deployEnv, workloadEnv, services) {
  const alias = DEV_DB_ENV_CONTRACT.dns;
  const service = listManifestItems(services).find((item) => item?.metadata?.name === alias.serviceName) ?? null;
  const actual = {
    deployServiceName: deployEnv.HWLAB_CLOUD_DB_SERVICE_NAME,
    deployServiceNamespace: deployEnv.HWLAB_CLOUD_DB_SERVICE_NAMESPACE,
    deployHost: deployEnv.HWLAB_CLOUD_DB_HOST,
    deployPort: deployEnv.HWLAB_CLOUD_DB_PORT,
    workloadServiceName: workloadEnv.get("HWLAB_CLOUD_DB_SERVICE_NAME")?.value,
    workloadServiceNamespace: workloadEnv.get("HWLAB_CLOUD_DB_SERVICE_NAMESPACE")?.value,
    workloadHost: workloadEnv.get("HWLAB_CLOUD_DB_HOST")?.value,
    workloadPort: workloadEnv.get("HWLAB_CLOUD_DB_PORT")?.value,
    serviceName: service?.metadata?.name
  };
  const missing = [];
  for (const [name, value] of Object.entries(actual)) {
    if (value !== undefined) {
      missing.push(`${name} must be absent because ${alias.serviceName} is not the readiness authority`);
    }
  }

  return {
    ready: missing.length === 0,
    source: alias.source,
    serviceName: alias.serviceName,
    namespace: alias.namespace,
    port: alias.port,
    portName: alias.portName,
    requiredForReadiness: alias.requiredForReadiness,
    usedForProbe: alias.usedForProbe,
    servicePresent: Boolean(service),
    secretMaterialRead: false,
    liveDbEvidence: false,
    missing,
    actual
  };
}

function getWorkloadEnvByServiceId(workloads, serviceId) {
  const envByName = new Map();
  for (const item of listManifestItems(workloads)) {
    for (const container of item?.spec?.template?.spec?.containers ?? []) {
      const itemServiceId =
        item?.metadata?.labels?.["hwlab.pikastech.local/service-id"] ||
        item?.spec?.template?.metadata?.labels?.["hwlab.pikastech.local/service-id"] ||
        container?.name;
      if (itemServiceId !== serviceId) {
        continue;
      }
      for (const env of container.env ?? []) {
        envByName.set(env.name, env);
      }
    }
  }
  return envByName;
}

function listManifestItems(document) {
  if (!document) {
    return [];
  }
  return document.kind === "List" ? document.items ?? [] : [document];
}

async function validateEdgeContracts(reporter, masterEdge) {
  try {
    const [frpc, frps] = await Promise.all([
      parseToml("deploy/frp/frpc.dev.toml"),
      parseToml("deploy/frp/frps.dev.toml")
    ]);
    assertFrpStatic(frpc, frps, masterEdge);
    reporter.check("frp-master-edge-static", "edge", "pass", "FRP and master-edge contracts describe D601-to-master DEV on public ports 16666/16667.");
  } catch (error) {
    reporter.check("frp-master-edge-static", "edge", "blocked", error.message);
    reporter.block({
      type: "contract_blocker",
      scope: "deploy/frp",
      summary: error.message,
      nextTask: "Repair FRP and master-edge DEV contracts before live reachability checks."
    });
  }
}

async function validateLiveProbes(reporter, catalog, timeoutMs) {
  const kubectlExists = await commandExists("kubectl");
  if (!kubectlExists) {
    reporter.check("d601-k3s-read-access", "k3s", "blocked", "kubectl is not installed in this runner, so hwlab-dev live cluster evidence cannot be collected.");
    reporter.block({
      type: "environment_blocker",
      scope: "d601-k3s",
      summary: "D601 runner lacks kubectl and no default kubeconfig was used by this preflight.",
      nextTask: "Provide a read-only kubectl/kubeconfig path for the real D601 hwlab-dev k3s cluster, then rerun this preflight."
    });
  } else {
    const k3sEvidence = [];
    const nativeKubectl = (args) => ["KUBECONFIG=/etc/rancher/k3s/k3s.yaml", "kubectl", ...args];
    for (const probe of [
      ["env", nativeKubectl(["version", "--client=true"])],
      ["env", nativeKubectl(["config", "current-context"])],
      ["env", nativeKubectl(["get", "nodes", "-o", "jsonpath={.items[*].metadata.name}"])],
      ["env", nativeKubectl(["auth", "can-i", "get", "pods", "-n", "hwlab-dev"])],
      ["env", nativeKubectl(["get", "namespace", "hwlab-dev", "-o", "json"])],
      ["env", nativeKubectl(["-n", "hwlab-dev", "get", "deploy,svc,job,cm", "-o", "name"])]
    ]) {
      k3sEvidence.push(await run(probe[0], probe[1], { timeoutMs }));
    }
    const nodeProbe = k3sEvidence[2];
    const ok = k3sEvidence.every((probe) => probe.ok) && String(nodeProbe.stdout ?? "").split(/\s+/u).includes("d601");
    reporter.check("d601-k3s-read-access", "k3s", ok ? "pass" : "blocked", ok ? "Read-only kubectl probes reached hwlab-dev." : "At least one read-only kubectl probe failed.", k3sEvidence);
    if (!ok) {
      reporter.block({
        type: "environment_blocker",
        scope: "d601-k3s",
        summary: "Read-only kubectl probes could not prove hwlab-dev namespace access.",
        nextTask: "Fix read-only D601 k3s credentials and confirm kubectl can get namespace, pods, services, jobs, and ConfigMaps in hwlab-dev."
      });
    }
  }

  const edgeProbe = await httpProbe(`${DEV_ENDPOINT}/health/live`, { timeoutMs });
  if (edgeProbe.ok) {
    reporter.check("public-dev-edge-health", "edge", "pass", "Public DEV health endpoint responded successfully.", [edgeProbe]);
  } else {
    reporter.check("public-dev-edge-health", "edge", "blocked", "Public DEV health endpoint did not respond successfully.", [edgeProbe]);
    reporter.block({
      type: "network_blocker",
      scope: "dev-edge",
      summary: `${DEV_ENDPOINT}/health/live is not reachable from this runner.`,
      nextTask: "Bring up or repair the D601-to-master frp route and hwlab-edge-proxy, then rerun the health probe."
    });
  }

  const registryServices = requiredCatalogServices(catalog);
  const registryEvidence = await Promise.all(registryServices.map(async (service) => {
    const url = imageToManifestUrl(service.image);
    if (!url) {
      return { serviceId: service.serviceId, ok: false, image: service.image, error: "unsupported image format" };
    }
    const probe = await httpProbe(url, {
      method: "HEAD",
      timeoutMs,
      headers: {
        Accept: "application/vnd.oci.image.manifest.v1+json, application/vnd.docker.distribution.manifest.v2+json"
      }
    });
    return { serviceId: service.serviceId, image: service.image, ...probe };
  }));
  const registryStatus = manifestReadStatus(registryEvidence);
  reporter.check(
    "catalog-image-manifest-read",
    "artifact-catalog",
    registryStatus,
    registryStatus === "pass"
      ? "All required catalog images were visible through manifest HEAD probes."
      : "One or more required catalog images could not be verified through manifest HEAD probes; this is runner-process catalog evidence, not Docker daemon push access.",
    registryEvidence
  );
  if (registryStatus === "blocked") {
    reporter.block({
      type: "runtime_blocker",
      scope: "registry-manifest-read",
      summary: "This preflight could not verify catalog image manifests through the runner process path; this is a #66 registry reachability dimension, not evidence that publish digests are missing.",
      nextTask: "Resolve the #66 registry reachability path or provide a non-secret registry reachability artifact; do not rerun heavy publish solely for this manifest-read probe."
    });
  }
}

function validateRuntimeBoundary(reporter, catalog) {
  const substituteImages = (catalog.services ?? [])
    .map((service) => service.image)
    .filter((image) => /unidesk|provider-gateway|microservice-proxy/.test(image));
  if (substituteImages.length === 0) {
    reporter.check("runtime-substitution-boundary", "safety", "pass", "Catalog images do not point at UniDesk/provider-gateway/microservice-proxy substitutes.");
    return;
  }

  reporter.check("runtime-substitution-boundary", "safety", "blocked", `Forbidden substitute images: ${substituteImages.join(", ")}`);
  reporter.block({
    type: "safety_blocker",
    scope: "runtime-boundary",
    summary: "DEV artifact catalog references a forbidden non-HWLAB runtime substitute.",
    nextTask: "Replace substitute runtime references with HWLAB-owned service images."
  });
}

async function writeReport(report, reportPath) {
  const absoluteReportPath = ensureNotRepoReportsPath(repoRoot, reportPath, "--report");
  await mkdir(path.dirname(absoluteReportPath), { recursive: true });
  await writeFile(absoluteReportPath, `${JSON.stringify(report, null, 2)}\n`);
}

function makeReport(args, targetCommit, targetShortCommit, reporter, artifactIdentity, registryCapabilities) {
  const blockingCheckStatuses = new Set(["blocked", "failed"]);
  const conclusion = reporter.blockers.length === 0 &&
    reporter.checks.every((item) => !blockingCheckStatuses.has(item.status)) ? "ready" : "blocked";

  return {
    $schema: "https://hwlab.pikastech.local/schemas/dev-gate-preflight-report.schema.json",
    $id: "https://hwlab.pikastech.local/dev-gate/dev-preflight-report.json",
    reportVersion: "v1",
    reportKind: "dev-gate-preflight",
    issue,
    supports,
    target: {
      ref: args.targetRef,
      commitId: targetCommit,
      shortCommitId: targetShortCommit
    },
    generatedAt: new Date().toISOString(),
    mode: "read-only",
    devOnly: true,
    prodDisabled: true,
    reportLifecycle: activeReportLifecycle("Current read-only DEV preflight report; source, local, and dry-run checks are not DEV-LIVE acceptance."),
    forbiddenActions,
    validationCommands: [
      "node --check scripts/dev-gate-preflight.mjs",
      "node --check scripts/src/dev-gate-preflight.mjs",
      "node --check scripts/src/registry-capabilities.mjs",
      "node --check scripts/refresh-artifact-catalog.mjs",
      "node scripts/dev-gate-preflight.mjs",
      "node --check scripts/validate-dev-gate-report.mjs",
      "node scripts/validate-dev-gate-report.mjs"
    ],
    artifactIdentity,
    registryCapabilities,
    conclusion,
    checks: reporter.checks,
    blockers: reporter.blockers,
    notes: "No PROD action, secret read, UniDesk runtime substitution, heavy e2e, browser e2e, runtime restart, or force push was performed."
  };
}

function printSummary(args, report) {
  console.log(JSON.stringify({
    issue,
    targetRef: args.targetRef,
    targetCommit: report.target.commitId,
    targetShortCommit: report.target.shortCommitId,
    artifactCatalogCommit: report.artifactIdentity?.artifactCatalog?.commitId,
    catalogDigests: report.artifactIdentity?.artifactCatalog?.digestCounts,
    registryCapabilities: {
      classification: report.registryCapabilities?.classification,
      dimensions: Object.fromEntries(Object.entries(report.registryCapabilities?.dimensions ?? {}).map(([key, value]) => [
        key,
        value.status
      ]))
    },
    conclusion: report.conclusion,
    report: args.writeReport ? args.reportPath : null,
    checks: report.checks.reduce((counts, item) => {
      counts[item.status] = (counts[item.status] ?? 0) + 1;
      return counts;
    }, {}),
    blockers: report.blockers.map((blocker) => ({
      type: blocker.type,
      scope: blocker.scope,
      nextTask: blocker.nextTask
    }))
  }, null, 2));
}

export async function runPreflight(argv) {
  const args = parseArgs(argv);
  if (args.help) {
    console.log(usage());
    return;
  }

  const targetCommit = (await run("git", ["rev-parse", args.targetRef])).stdout;
  const targetShortCommit = (await run("git", ["rev-parse", "--short=7", args.targetRef])).stdout;
  assert.match(targetCommit, /^[a-f0-9]{40}$/, `target ref ${args.targetRef} must resolve to a full SHA`);

  const reporter = makeReporter();
  const contracts = await loadContracts();
  const optionalReports = await loadOptionalReports();
  const registryPrefix = optionalReports.artifactPublish?.artifactPublish?.registryPrefix ?? "127.0.0.1:5000/hwlab";
  const registryCapabilities = await probeRegistryCapabilities({
    registryPrefix,
    timeoutMs: args.timeoutMs
  });
  const { deploy, catalog, masterEdge, artifactIdentity } = await validateLocalContracts(
    reporter,
    contracts,
    optionalReports,
    targetShortCommit,
    targetCommit,
    args.targetRef
  );

  validateRegistryCapabilities(reporter, registryCapabilities);
  validateCloudApiDbContract(reporter, deploy, contracts[3], contracts[4]);
  validateCodeAgentProviderContract(reporter, deploy, contracts[3]);
  validateArtifactPublishReport(reporter, optionalReports.artifactPublish, artifactIdentity, targetShortCommit, targetCommit, args.targetRef);
  validateEdgeHealthReport(reporter, optionalReports.edgeHealth);
  await validateEdgeContracts(reporter, masterEdge);
  validateRuntimeBoundary(reporter, catalog);
  await validateLiveProbes(reporter, catalog, args.timeoutMs);

  const report = makeReport(args, targetCommit, targetShortCommit, reporter, artifactIdentity, registryCapabilities);
  if (args.writeReport) {
    await writeReport(report, args.reportPath);
  }
  printSummary(args, report);

  if (report.conclusion === "blocked" && args.failOnBlocked) {
    process.exitCode = 2;
  }
}

function validateCodeAgentProviderContract(reporter, deploy, workloads) {
  const staticContract = inspectCodeAgentProviderStaticContract(deploy, workloads);
  const evidence = [
    {
      staticContract,
      fixtureEvidence: false,
      providerConnected: false
    }
  ];

  if (staticContract.ready) {
    reporter.check(
      "code-agent-provider-env-contract",
      "agent",
      "pass",
      "cloud-api DEV Code Agent manifest declares the OpenAI provider Secret reference and DEV egress/proxy base URL without secret values.",
      evidence
    );
    return;
  }

  const missing = [
    ...staticContract.missingDeployEnv,
    ...staticContract.missingK8sEnv,
    ...staticContract.missingSecretRefs,
    ...staticContract.missingEgressContract
  ];
  reporter.check(
    "code-agent-provider-env-contract",
    "agent",
    "blocked",
    `cloud-api DEV Code Agent provider contract is missing ${missing.join(", ")}.`,
    evidence
  );
  reporter.block({
    type: "agent_blocker",
    scope: "code-agent-provider-env-contract",
    summary: `cloud-api DEV Code Agent provider contract is incomplete; missing ${missing.join(", ")}.`,
    nextTask: "Repair deploy/deploy.yaml and deploy/k8s/base/workloads.yaml so hwlab-cloud-api has OPENAI_API_KEY from hwlab-code-agent-provider/openai-api-key and HWLAB_CODE_AGENT_OPENAI_BASE_URL through DEV egress/proxy; do not print the Secret value."
  });
}

function inspectCodeAgentProviderStaticContract(deploy, workloads) {
  const contract = DEV_CODE_AGENT_PROVIDER_CONTRACT;
  const cloudApi = deploy?.services?.find((service) => service.serviceId === "hwlab-cloud-api") ?? {};
  const deployEnv = cloudApi.env ?? {};
  const workloadEnv = getWorkloadEnvByServiceId(workloads, "hwlab-cloud-api");
  const secretRef = contract.secretRefs[0];
  const expectedSecretRef = codeAgentSecretRefPlaceholder();
  const workloadSecretRef = workloadEnv.get(secretRef.env)?.valueFrom?.secretKeyRef;
  const fields = contract.requiredEnv.map((name) => ({
    name,
    manifestPresent: Object.hasOwn(deployEnv, name),
    k8sPresent: workloadEnv.has(name),
    redacted: name === secretRef.env,
    source: name === secretRef.env ? "k8s-secret-ref" : "runtime-env"
  }));
  const secretRefMatches =
    deployEnv[secretRef.env] === expectedSecretRef &&
    workloadSecretRef?.name === secretRef.secretName &&
    workloadSecretRef?.key === secretRef.secretKey;
  const deployBaseUrl = deployEnv[contract.egress.env];
  const workloadBaseUrl = workloadEnv.get(contract.egress.env)?.value;
  const egressChecks = [
    ["deployEnv.HWLAB_CODE_AGENT_PROVIDER", deployEnv.HWLAB_CODE_AGENT_PROVIDER, contract.codeAgentProvider],
    ["deployEnv.HWLAB_CODE_AGENT_MODEL", deployEnv.HWLAB_CODE_AGENT_MODEL, contract.model],
    ["deployEnv.HWLAB_CODE_AGENT_OPENAI_BASE_URL", deployBaseUrl, contract.egress.defaultBaseUrl],
    ["workloadEnv.HWLAB_CODE_AGENT_PROVIDER", workloadEnv.get("HWLAB_CODE_AGENT_PROVIDER")?.value, contract.codeAgentProvider],
    ["workloadEnv.HWLAB_CODE_AGENT_MODEL", workloadEnv.get("HWLAB_CODE_AGENT_MODEL")?.value, contract.model],
    ["workloadEnv.HWLAB_CODE_AGENT_OPENAI_BASE_URL", workloadBaseUrl, contract.egress.defaultBaseUrl]
  ];
  const missingEgressContract = egressChecks
    .filter(([, actualValue, expectedValue]) => !Object.is(actualValue, expectedValue))
    .map(([name, actualValue, expectedValue]) => `${name} expected ${expectedValue} got ${actualValue ?? "missing"}`);
  if (deployBaseUrl === contract.egress.forbiddenDirectBaseUrl || workloadBaseUrl === contract.egress.forbiddenDirectBaseUrl) {
    missingEgressContract.push("HWLAB_CODE_AGENT_OPENAI_BASE_URL must not point directly at public api.openai.com");
  }
  const missingDeployEnv = fields.filter((field) => !field.manifestPresent).map((field) => field.name);
  const missingK8sEnv = fields.filter((field) => !field.k8sPresent).map((field) => field.name);
  const missingSecretRefs = secretRefMatches ? [] : [`${secretRef.secretName}/${secretRef.secretKey}`];

  return {
    contractVersion: contract.contractVersion,
    environment: contract.environment,
    provider: contract.codeAgentProvider,
    runtimeProvider: contract.runtimeProvider,
    backend: contract.backend,
    model: contract.model,
    ready:
      missingDeployEnv.length === 0 &&
      missingK8sEnv.length === 0 &&
      missingSecretRefs.length === 0 &&
      missingEgressContract.length === 0,
    requiredEnv: fields,
    secretRef: {
      env: secretRef.env,
      secretName: secretRef.secretName,
      secretKey: secretRef.secretKey,
      present: secretRefMatches,
      redacted: true
    },
    egress: {
      env: contract.egress.env,
      target: contract.egress.target,
      deployPresent: Boolean(deployBaseUrl),
      workloadPresent: Boolean(workloadBaseUrl),
      directPublicOpenAi: deployBaseUrl === contract.egress.forbiddenDirectBaseUrl || workloadBaseUrl === contract.egress.forbiddenDirectBaseUrl,
      valueRedacted: true
    },
    missingDeployEnv,
    missingK8sEnv,
    missingSecretRefs,
    missingEgressContract,
    secretMaterialRead: false,
    valuesRedacted: true,
    providerConnected: false,
    fixtureEvidence: false
  };
}
