// SPEC: PJ2026-01060307 控制面模块化 draft-2026-06-25-p0. secrets module for scripts/src/agentrun.ts.

// Moved mechanically from scripts/src/agentrun.ts:4220-5176 for #903.

// SPEC: PJ2026-01020108 cancel lifecycle + PJ2026-01020205 AipodSpec binding + PJ2026-01020302 session policy + PJ2026-01020305 cancel control + PJ2026-01060305/06 YAML execution policy and bounded output draft-2026-06-25-p0.
// Exposes AgentRun lane-scoped policy, AipodSpec SecretRef binding, cancel lifecycle, and bounded default output in the UniDesk CLI.
import { chmodSync, copyFileSync, existsSync, readFileSync, statSync, writeFileSync } from "node:fs";
import { join } from "node:path";
import { spawnSync } from "node:child_process";
import { rootPath, type UniDeskConfig } from "../config";
import type { RenderedCliResult } from "../output";
import { applyLocalCaddyManagedSite } from "../pk01-caddy";
import { runSshCommandCapture, type SshCaptureResult } from "../ssh";
import { runRemoteSshCommandCapture } from "../remote";
import { startJob } from "../jobs";
import {
  AGENTRUN_CONFIG_PATH,
  agentRunLaneSummary,
  agentRunPipelineRunName,
  agentRunProviderCredentialRefs,
  agentRunSourceSnapshotRef,
  agentRunSourceSnapshotStageRefPrefix,
  resolveAgentRunLaneTarget,
  type AgentRunCodexConfigSpec,
  type AgentRunCancelLifecycleSpec,
  type AgentRunLaneSpec,
} from "../agentrun-lanes";
import {
  agentRunImageArtifact,
  placeholderAgentRunImage,
  renderedFilesDigest,
  renderedObjectsDigest,
  renderAgentRunControlPlaneManifests,
  renderAgentRunGitopsFiles,
  type AgentRunArtifactService,
} from "../agentrun-manifests";
import {
  agentRunManagedRepositoryDesiredConfig,
  agentRunManagedRepositoryRenderHash,
} from "../agentrun-managed-repository-reconciler";
import { sha256Fingerprint } from "../platform-infra-ops-library";

import { capture, captureJsonPayload, compactCapture, progressEvent, record, shQuote, sleep, stringOrNull } from "./utils";

export function yamlLaneGitopsPublishPayloadFromProbe(probePayload: Record<string, unknown>): Record<string, unknown> {
  const logsTail = stringOrNull(probePayload.logsTail);
  if (logsTail === null) return {};
  const lines = logsTail.split(/\r?\n/u).map((line) => line.trim()).filter(Boolean);
  for (let index = lines.length - 1; index >= 0; index -= 1) {
    const line = lines[index];
    if (!line.startsWith("{") || !line.endsWith("}")) continue;
    try {
      const parsed = JSON.parse(line) as unknown;
      const payload = record(parsed);
      if (payload.ok === true || payload.ok === false) return payload;
    } catch {
      continue;
    }
  }
  return {};
}

export function yamlLaneGitopsPublishJobManifest(spec: AgentRunLaneSpec, files: readonly { path: string; content: string }[], name: string): Record<string, unknown> {
  return {
    apiVersion: "batch/v1",
    kind: "Job",
    metadata: {
      name,
      namespace: spec.gitMirror.namespace,
      labels: {
        "app.kubernetes.io/name": "gitops-publish",
        "app.kubernetes.io/part-of": "agentrun",
        "agentrun.pikastech.local/lane": spec.version,
        "agentrun.pikastech.local/node": spec.nodeId,
        "agentrun.pikastech.local/component": "gitops-publish",
      },
    },
    spec: {
      backoffLimit: 0,
      activeDeadlineSeconds: 600,
      ttlSecondsAfterFinished: 3600,
      template: {
        metadata: {
          labels: {
            "app.kubernetes.io/name": "gitops-publish",
            "app.kubernetes.io/part-of": "agentrun",
            "agentrun.pikastech.local/lane": spec.version,
            "agentrun.pikastech.local/node": spec.nodeId,
            "agentrun.pikastech.local/component": "gitops-publish",
          },
        },
        spec: {
          restartPolicy: "Never",
          volumes: [yamlLaneGitMirrorCacheVolume(spec)],
          containers: [{
            name: "publish",
            image: spec.gitMirror.toolsImage,
            imagePullPolicy: "IfNotPresent",
            command: ["/bin/sh", "-ec", yamlLaneGitopsPublishShell(spec, files, name)],
            volumeMounts: [{ name: "cache", mountPath: "/cache" }],
          }],
        },
      },
    },
  };
}

export function yamlLaneGitopsPublishShell(spec: AgentRunLaneSpec, files: readonly { path: string; content: string }[], jobName: string): string {
  const filesB64 = Buffer.from(JSON.stringify(files.map((file) => ({
    path: file.path,
    contentBase64: Buffer.from(file.content, "utf8").toString("base64"),
  }))), "utf8").toString("base64");
  return [
    "set -eu",
    `repository=${shQuote(spec.source.repository)}`,
    `gitops_branch=${shQuote(spec.gitops.branch)}`,
    `gitops_root=${shQuote(spec.deployment.gitopsRoot)}`,
    `artifact_catalog=${shQuote(spec.deployment.artifactCatalogPath)}`,
    `files_b64=${shQuote(filesB64)}`,
    `job_name=${shQuote(jobName)}`,
    "remote=\"/cache/${repository}.git\"",
    "remote_mode=git-mirror-cache-volume",
    "publish_worktree=\"/tmp/$job_name\"",
    "write_failed_status() { code=$?; if [ \"$code\" -ne 0 ]; then printf '%s\\n' \"{\\\"ok\\\":false,\\\"status\\\":\\\"failed\\\",\\\"exitCode\\\":$code,\\\"jobName\\\":\\\"$job_name\\\",\\\"publishWorkspace\\\":\\\"$publish_worktree\\\",\\\"gitopsBranch\\\":\\\"$gitops_branch\\\",\\\"publishRemoteMode\\\":\\\"$remote_mode\\\",\\\"valuesPrinted\\\":false}\"; fi; exit \"$code\"; }",
    "trap write_failed_status EXIT",
    "rm -rf \"$publish_worktree\"",
    "git clone --no-checkout \"$remote\" \"$publish_worktree\"",
    "cd \"$publish_worktree\"",
    "git fetch origin \"$gitops_branch\" || true",
    "if git rev-parse --verify \"refs/remotes/origin/$gitops_branch^{commit}\" >/dev/null 2>&1; then",
    "  git checkout -B \"$gitops_branch\" \"refs/remotes/origin/$gitops_branch\"",
    "else",
    "  git checkout --orphan \"$gitops_branch\"",
    "  git rm -rf . >/dev/null 2>&1 || true",
    "fi",
    "git rm -rf --ignore-unmatch \"$gitops_root\" \"$artifact_catalog\" source.json >/dev/null 2>&1 || true",
    "rm -rf \"$gitops_root\" \"$artifact_catalog\" source.json",
    "FILES_B64=\"$files_b64\" node <<'NODE'",
    "const fs = require('node:fs');",
    "const path = require('node:path');",
    "const files = JSON.parse(Buffer.from(process.env.FILES_B64 || '', 'base64').toString('utf8'));",
    "for (const file of files) {",
    "  const target = path.resolve(process.cwd(), file.path);",
    "  if (!target.startsWith(process.cwd() + path.sep)) throw new Error(`refuse path outside workspace: ${file.path}`);",
    "  fs.mkdirSync(path.dirname(target), { recursive: true });",
    "  fs.writeFileSync(target, Buffer.from(file.contentBase64, 'base64'));",
    "}",
    "NODE",
    "git add source.json \"$artifact_catalog\" \"$gitops_root\"",
    "if git diff --quiet --cached; then changed=false; else changed=true; git -c user.email=agentrun@unidesk.local -c user.name='UniDesk AgentRun Ops' commit -m \"deploy: render AgentRun ${gitops_branch} from UniDesk YAML\"; fi",
    "git push -u origin \"$gitops_branch\"",
    "gitops_commit=$(git rev-parse HEAD)",
    "CHANGED=\"$changed\" GITOPS_BRANCH=\"$gitops_branch\" GITOPS_COMMIT=\"$gitops_commit\" FILE_COUNT=\"" + String(files.length) + "\" JOB_NAME=\"$job_name\" PUBLISH_REMOTE_MODE=\"$remote_mode\" node <<'NODE'",
    "console.log(JSON.stringify({ ok: true, status: 'succeeded', jobName: process.env.JOB_NAME, changed: process.env.CHANGED === 'true', gitopsBranch: process.env.GITOPS_BRANCH, gitopsCommit: process.env.GITOPS_COMMIT, publishRemoteMode: process.env.PUBLISH_REMOTE_MODE || null, fileCount: Number(process.env.FILE_COUNT || 0), valuesPrinted: false }));",
    "NODE",
    "trap - EXIT",
  ].join("\n");
}

export const AGENTRUN_GIT_MIRROR_RETRY_MAX_ATTEMPTS = 5;

export const AGENTRUN_GIT_MIRROR_RETRYABLE_PATTERN =
  /(kex_exchange_identification|connection closed by remote host|connection closed by unknown|could not read from remote repository|ssh\.github\.com|github\.com|fetch-pack|early eof|econnreset|read econnreset|proxy-connect|tunnel socket error)/i;

export function agentRunGitMirrorRetryDelayMs(attempt: number): number {
  return Math.min(15_000, 1_000 * Math.pow(2, Math.max(0, attempt - 1)));
}

export function agentRunGitMirrorFailureText(value: unknown): string {
  try {
    return JSON.stringify(value);
  } catch {
    return String(value);
  }
}

export function agentRunGitMirrorRetryableFailure(
  spec: AgentRunLaneSpec,
  action: "sync" | "flush",
  attempt: number,
  value: unknown,
): Record<string, unknown> | null {
  const text = agentRunGitMirrorFailureText(value);
  if (!AGENTRUN_GIT_MIRROR_RETRYABLE_PATTERN.test(text)) return null;
  const retryExhausted = attempt >= AGENTRUN_GIT_MIRROR_RETRY_MAX_ATTEMPTS;
  const retryBackoffMs = retryExhausted ? 0 : agentRunGitMirrorRetryDelayMs(attempt);
  return {
    retryable: true,
    retryAttempt: attempt,
    retryMaxAttempts: AGENTRUN_GIT_MIRROR_RETRY_MAX_ATTEMPTS,
    retryLabel: `${attempt}/${AGENTRUN_GIT_MIRROR_RETRY_MAX_ATTEMPTS}`,
    retryExhausted,
    retryBackoffMs,
    backoffPolicy: "exponential",
    failureKind: "git-mirror-upstream-transient",
    upstream: "git-mirror-ssh",
    action,
    node: spec.nodeId,
    lane: spec.lane,
    message: retryExhausted
      ? "AgentRun git-mirror upstream transient failure exhausted retries; stopping instead of continuing silently."
      : "AgentRun git-mirror upstream transient failure; retrying with exponential backoff.",
    next: retryExhausted
      ? { stopped: true, reason: "retry-exhausted" }
      : {
          retry: `bun scripts/cli.ts agentrun git-mirror ${action} --node ${spec.nodeId} --lane ${spec.lane} --confirm --wait`,
          delayMs: retryBackoffMs,
        },
    valuesPrinted: false,
  };
}

export function agentRunGitMirrorRetrySummary(attempts: Record<string, unknown>[]): Record<string, unknown> {
  const last = attempts.length > 0 ? record(attempts[attempts.length - 1]) : {};
  return {
    policy: "exponential",
    maxAttempts: AGENTRUN_GIT_MIRROR_RETRY_MAX_ATTEMPTS,
    attempts,
    exhausted: last.retryExhausted === true,
    stopped: last.retryExhausted === true,
    valuesPrinted: false,
  };
}

export async function runYamlLaneGitMirrorSyncJob(config: UniDeskConfig, spec: AgentRunLaneSpec): Promise<Record<string, unknown>> {
  return await runYamlLaneGitMirrorJob(config, spec, "sync");
}

export async function runYamlLaneGitMirrorFlushJob(config: UniDeskConfig, spec: AgentRunLaneSpec): Promise<Record<string, unknown>> {
  return await runYamlLaneGitMirrorJob(config, spec, "flush");
}

export async function runYamlLaneGitMirrorJob(config: UniDeskConfig, spec: AgentRunLaneSpec, action: "sync" | "flush"): Promise<Record<string, unknown>> {
  const attempts: Record<string, unknown>[] = [];
  for (let attempt = 1; attempt <= AGENTRUN_GIT_MIRROR_RETRY_MAX_ATTEMPTS; attempt += 1) {
    const jobPrefix = action === "sync" ? spec.gitMirror.syncJobPrefix : spec.gitMirror.flushJobPrefix;
    const jobName = `${jobPrefix}-${Date.now().toString(36)}-${attempt}`.slice(0, 63);
    const manifest = yamlLaneGitMirrorJobManifest(spec, action, jobName);
    const created = await capture(config, spec.nodeKubeRoute, ["sh", "--", createYamlLaneJobScript(spec.gitMirror.namespace, jobName, manifest)]);
    if (created.exitCode !== 0) {
      const result = { ok: false, phase: "create-job", jobName, capture: compactCapture(created, { full: true, stdoutTailChars: 4000, stderrTailChars: 4000 }), valuesPrinted: false };
      const retryableFailure = agentRunGitMirrorRetryableFailure(spec, action, attempt, result);
      attempts.push({ attempt, retryLabel: `${attempt}/${AGENTRUN_GIT_MIRROR_RETRY_MAX_ATTEMPTS}`, jobName, ok: false, phase: "create-job", retryableFailure, retryExhausted: retryableFailure !== null && record(retryableFailure).retryExhausted === true });
      if (retryableFailure !== null && record(retryableFailure).retryExhausted !== true) {
        progressEvent("agentrun.yaml-lane.git-mirror.retry", retryableFailure);
        await sleep(agentRunGitMirrorRetryDelayMs(attempt));
        continue;
      }
      return { ...result, retryableFailure, retry: agentRunGitMirrorRetrySummary(attempts) };
    }
    const startedAt = Date.now();
    let polls = 0;
    let lastProbe: SshCaptureResult | null = null;
    while (Date.now() - startedAt < 300_000) {
      polls += 1;
      lastProbe = await capture(config, spec.nodeKubeRoute, ["sh", "--", yamlLaneJobProbeScript(spec.gitMirror.namespace, jobName)]);
      const payload = captureJsonPayload(lastProbe);
      progressEvent("agentrun.yaml-lane.git-mirror.progress", {
        node: spec.nodeId,
        lane: spec.lane,
        action,
        jobName,
        polls,
        retryAttempt: attempt,
        retryMaxAttempts: AGENTRUN_GIT_MIRROR_RETRY_MAX_ATTEMPTS,
        retryLabel: `${attempt}/${AGENTRUN_GIT_MIRROR_RETRY_MAX_ATTEMPTS}`,
        succeeded: payload.succeeded === true,
        failed: payload.failed === true,
        elapsedMs: Date.now() - startedAt,
      });
      if (payload.succeeded === true) {
        attempts.push({ attempt, retryLabel: `${attempt}/${AGENTRUN_GIT_MIRROR_RETRY_MAX_ATTEMPTS}`, jobName, ok: true, polls, elapsedMs: Date.now() - startedAt });
        return { ok: true, jobName, polls, elapsedMs: Date.now() - startedAt, result: payload, retry: agentRunGitMirrorRetrySummary(attempts), valuesPrinted: false };
      }
      if (payload.failed === true) {
        const result = { ok: false, jobName, polls, elapsedMs: Date.now() - startedAt, result: payload, capture: compactCapture(lastProbe, { full: true, stdoutTailChars: 5000, stderrTailChars: 3000 }), valuesPrinted: false };
        const retryableFailure = agentRunGitMirrorRetryableFailure(spec, action, attempt, result);
        attempts.push({ attempt, retryLabel: `${attempt}/${AGENTRUN_GIT_MIRROR_RETRY_MAX_ATTEMPTS}`, jobName, ok: false, polls, elapsedMs: Date.now() - startedAt, retryableFailure, retryExhausted: retryableFailure !== null && record(retryableFailure).retryExhausted === true });
        if (retryableFailure !== null && record(retryableFailure).retryExhausted !== true) {
          progressEvent("agentrun.yaml-lane.git-mirror.retry", retryableFailure);
          await sleep(agentRunGitMirrorRetryDelayMs(attempt));
          break;
        }
        return { ...result, retryableFailure, retry: agentRunGitMirrorRetrySummary(attempts) };
      }
      await sleep(5_000);
    }
    if (attempts.length > 0 && record(attempts[attempts.length - 1]).jobName === jobName) continue;
    const degradedReason = `git-mirror-${action}-job-timeout`;
    const result = { ok: false, jobName, polls, elapsedMs: Date.now() - startedAt, degradedReason, capture: lastProbe === null ? null : compactCapture(lastProbe, { full: true, stdoutTailChars: 5000, stderrTailChars: 3000 }), valuesPrinted: false };
    const retryableFailure = agentRunGitMirrorRetryableFailure(spec, action, attempt, result);
    attempts.push({ attempt, retryLabel: `${attempt}/${AGENTRUN_GIT_MIRROR_RETRY_MAX_ATTEMPTS}`, jobName, ok: false, polls, elapsedMs: Date.now() - startedAt, degradedReason, retryableFailure, retryExhausted: retryableFailure !== null && record(retryableFailure).retryExhausted === true });
    if (retryableFailure !== null && record(retryableFailure).retryExhausted !== true) {
      progressEvent("agentrun.yaml-lane.git-mirror.retry", retryableFailure);
      await sleep(agentRunGitMirrorRetryDelayMs(attempt));
      continue;
    }
    return { ...result, retryableFailure, retry: agentRunGitMirrorRetrySummary(attempts) };
  }
  return { ok: false, degradedReason: `git-mirror-${action}-retry-loop-exhausted`, retry: agentRunGitMirrorRetrySummary(attempts), valuesPrinted: false };
}

export function yamlLanePipelineRunCreateScript(spec: AgentRunLaneSpec, sourceCommit: string, pipelineRun: string): string {
  const manifest = yamlLanePipelineRunManifest(spec, sourceCommit, pipelineRun);
  const manifestB64 = Buffer.from(JSON.stringify(manifest), "utf8").toString("base64");
  return [
    "set -eu",
    `namespace=${shQuote(spec.ci.namespace)}`,
    `pipeline_run=${shQuote(pipelineRun)}`,
    `manifest_b64=${shQuote(manifestB64)}`,
    "tmp=$(mktemp)",
    "trap 'rm -f \"$tmp\"' EXIT",
    "printf '%s' \"$manifest_b64\" | base64 -d > \"$tmp\"",
    "existing_status=$(kubectl -n \"$namespace\" get pipelinerun \"$pipeline_run\" -o 'jsonpath={.status.conditions[0].status}' 2>/dev/null || true)",
    "if [ \"$existing_status\" = False ]; then kubectl -n \"$namespace\" delete pipelinerun \"$pipeline_run\" --ignore-not-found=true --wait=false >/dev/null 2>&1 || true; fi",
    "if kubectl -n \"$namespace\" get pipelinerun \"$pipeline_run\" >/dev/null 2>&1; then created=false; else kubectl create -f \"$tmp\"; created=true; fi",
    "status=$(kubectl -n \"$namespace\" get pipelinerun \"$pipeline_run\" -o 'jsonpath={.status.conditions[0].status}' 2>/dev/null || true)",
    "CREATED=\"$created\" STATUS=\"$status\" PIPELINE_RUN=\"$pipeline_run\" python3 - <<'PY'",
    "import json, os",
    "print(json.dumps({'ok': True, 'pipelineRun': os.environ.get('PIPELINE_RUN'), 'created': os.environ.get('CREATED') == 'true', 'status': os.environ.get('STATUS') or None, 'valuesPrinted': False}, ensure_ascii=False))",
    "PY",
  ].join("\n");
}

export function yamlLanePipelineRunManifest(spec: AgentRunLaneSpec, sourceCommit: string, pipelineRun: string): Record<string, unknown> {
  const sourceStageRef = spec.source.statusMode === "k3s-git-mirror" ? agentRunSourceSnapshotRef(spec, sourceCommit) : null;
  return {
    apiVersion: "tekton.dev/v1",
    kind: "PipelineRun",
    metadata: {
      name: pipelineRun,
      namespace: spec.ci.namespace,
      labels: {
        "app.kubernetes.io/part-of": "agentrun",
        "agentrun.pikastech.local/lane": spec.version,
        "agentrun.pikastech.local/node": spec.nodeId,
        "agentrun.pikastech.local/source-commit": sourceCommit,
        "agentrun.pikastech.local/trigger": "unidesk-yaml-only",
      },
      annotations: sourceStageRef === null ? undefined : {
        "agentrun.pikastech.local/source-authority": "git-mirror-snapshot",
        "agentrun.pikastech.local/source-stage-ref": sourceStageRef,
      },
    },
    spec: {
      pipelineRef: { name: spec.ci.pipeline },
      taskRunTemplate: {
        serviceAccountName: spec.ci.serviceAccountName,
        podTemplate: { hostNetwork: true, dnsPolicy: "ClusterFirstWithHostNet", securityContext: { fsGroup: 1000 } },
      },
      params: [
        { name: "git-url", value: spec.source.remote },
        { name: "git-read-url", value: spec.gitMirror.readUrl },
        { name: "git-write-url", value: spec.gitMirror.writeUrl },
        { name: "source-branch", value: spec.source.branch },
        { name: "gitops-branch", value: spec.gitops.branch },
        { name: "revision", value: sourceCommit },
        ...(sourceStageRef === null ? [] : [{ name: "source-stage-ref", value: sourceStageRef }]),
        { name: "registry-prefix", value: spec.ci.registryPrefix },
        { name: "tools-image", value: spec.ci.toolsImage },
      ],
      workspaces: [
        { name: "source", volumeClaimTemplate: { spec: { accessModes: ["ReadWriteOnce"], resources: { requests: { storage: "4Gi" } } } } },
        { name: "git-ssh", secret: { secretName: spec.gitMirror.sshSecretName } },
      ],
    },
  };
}

export function createYamlLaneJobScript(namespace: string, jobName: string, manifest: Record<string, unknown>): string {
  const manifestB64 = Buffer.from(JSON.stringify(manifest), "utf8").toString("base64");
  return [
    "set -eu",
    `namespace=${shQuote(namespace)}`,
    `job=${shQuote(jobName)}`,
    `manifest_b64=${shQuote(manifestB64)}`,
    "tmp=$(mktemp)",
    "trap 'rm -f \"$tmp\"' EXIT",
    "printf '%s' \"$manifest_b64\" | base64 -d > \"$tmp\"",
    "kubectl -n \"$namespace\" delete job \"$job\" --ignore-not-found=true --wait=false >/dev/null 2>&1 || true",
    "kubectl create -f \"$tmp\"",
    "JOB=\"$job\" python3 - <<'PY'",
    "import json, os",
    "print(json.dumps({'ok': True, 'jobName': os.environ.get('JOB'), 'valuesPrinted': False}, ensure_ascii=False))",
    "PY",
  ].join("\n");
}

export function yamlLaneJobProbeScript(namespace: string, jobName: string): string {
  return [
    "set +e",
    `namespace=${shQuote(namespace)}`,
    `job=${shQuote(jobName)}`,
    "kubectl -n \"$namespace\" get job \"$job\" -o json > /tmp/agentrun-job.json 2>/dev/null",
    "job_exit=$?",
    "kubectl -n \"$namespace\" logs \"job/$job\" --tail=120 > /tmp/agentrun-job.log 2>/dev/null",
    "JOB_EXIT=\"$job_exit\" JOB=\"$job\" python3 - <<'PY'",
    "import json, os",
    "def read_json(path):",
    "    try:",
    "        with open(path, 'r', encoding='utf-8') as fh:",
    "            return json.load(fh)",
    "    except Exception:",
    "        return None",
    "def read_text(path):",
    "    try:",
    "        with open(path, 'r', encoding='utf-8') as fh:",
    "            return fh.read()",
    "    except Exception:",
    "        return ''",
    "job = read_json('/tmp/agentrun-job.json') or {}",
    "status = job.get('status') or {}",
    "log = read_text('/tmp/agentrun-job.log')",
    "succeeded = int(status.get('succeeded') or 0) > 0",
    "failed = int(status.get('failed') or 0) > 0",
    "print(json.dumps({'ok': os.environ.get('JOB_EXIT') == '0', 'jobName': os.environ.get('JOB'), 'succeeded': succeeded, 'failed': failed, 'active': status.get('active') or 0, 'logsTail': log[-4000:], 'valuesPrinted': False}, ensure_ascii=False))",
    "PY",
  ].join("\n");
}

export function yamlLaneGitMirrorCacheVolume(spec: AgentRunLaneSpec): Record<string, unknown> {
  if (spec.gitMirror.cacheHostPath !== null) {
    return { name: "cache", hostPath: { path: spec.gitMirror.cacheHostPath, type: "DirectoryOrCreate" } };
  }
  return { name: "cache", persistentVolumeClaim: { claimName: spec.gitMirror.cachePvc } };
}

export function yamlLaneGitMirrorJobManifest(spec: AgentRunLaneSpec, action: "sync" | "flush", name: string): Record<string, unknown> {
  const proxyHostNeedsHostNetwork = spec.gitMirror.githubProxy.host === "127.0.0.1" || spec.gitMirror.githubProxy.host === "localhost";
  return {
    apiVersion: "batch/v1",
    kind: "Job",
    metadata: {
      name,
      namespace: spec.gitMirror.namespace,
      labels: {
        "app.kubernetes.io/name": "git-mirror",
        "app.kubernetes.io/part-of": "agentrun",
        "agentrun.pikastech.local/lane": spec.version,
        "agentrun.pikastech.local/node": spec.nodeId,
        "agentrun.pikastech.local/component": action,
      },
    },
    spec: {
      backoffLimit: 0,
      activeDeadlineSeconds: 600,
      ttlSecondsAfterFinished: 3600,
      template: {
        metadata: {
          labels: {
            "app.kubernetes.io/name": "git-mirror",
            "app.kubernetes.io/part-of": "agentrun",
            "agentrun.pikastech.local/lane": spec.version,
            "agentrun.pikastech.local/node": spec.nodeId,
            "agentrun.pikastech.local/component": action,
          },
        },
        spec: {
          restartPolicy: "Never",
          ...(proxyHostNeedsHostNetwork ? { hostNetwork: true, dnsPolicy: "ClusterFirstWithHostNet" } : {}),
          volumes: [
            yamlLaneGitMirrorCacheVolume(spec),
            { name: "git-ssh", secret: { secretName: spec.gitMirror.sshSecretName, defaultMode: 0o400 } },
          ],
          containers: [{
            name: action,
            image: spec.gitMirror.toolsImage,
            imagePullPolicy: "IfNotPresent",
            command: ["/bin/sh", "-ec", action === "sync" ? yamlLaneGitMirrorSyncShell(spec) : yamlLaneGitMirrorFlushShell(spec)],
            volumeMounts: [
              { name: "cache", mountPath: "/cache" },
              { name: "git-ssh", mountPath: "/git-ssh", readOnly: true },
            ],
          }],
        },
      },
    },
  };
}

export function yamlLaneGitMirrorSshSetupShellLines(spec: AgentRunLaneSpec): string[] {
  const proxyHost = spec.gitMirror.githubProxy.host;
  const proxyPort = spec.gitMirror.githubProxy.port;
  const proxyUrl = `http://${proxyHost}:${proxyPort}`;
  const noProxy = "localhost,127.0.0.1,::1,.svc,.svc.cluster.local,.cluster.local,10.0.0.0/8,10.42.0.0/16,10.43.0.0/16,hyueapi.com,.hyueapi.com";
  const proxySummary = `agentrun git-mirror-egress-proxy node=${spec.nodeId} lane=${spec.lane} host=${proxyHost} port=${proxyPort} ssh=GIT_SSH-wrapper source=yaml`;
  const proxyCommand = `ProxyCommand=node /tmp/agentrun-github-proxy-connect.cjs ${proxyHost} ${proxyPort} %h %p`;
  return [
    "mkdir -p /root/.ssh",
    "cp /git-ssh/ssh-privatekey /root/.ssh/id_rsa",
    "chmod 0400 /root/.ssh/id_rsa",
    `printf '%s\\n' ${shQuote(proxySummary)} >&2`,
    "cat > /tmp/agentrun-github-proxy-connect.cjs <<'NODE_PROXY'",
    "#!/usr/bin/env node",
    "const net = require('node:net');",
    "const [proxyHost, proxyPortRaw, targetHost, targetPortRaw] = process.argv.slice(2);",
    "const proxyPort = Number.parseInt(proxyPortRaw || '', 10);",
    "const targetPort = Number.parseInt(targetPortRaw || '', 10);",
    "if (!proxyHost || !Number.isInteger(proxyPort) || !targetHost || !Number.isInteger(targetPort)) {",
    "  console.error('agentrun git-mirror proxy-connect: invalid ProxyCommand arguments');",
    "  process.exit(64);",
    "}",
    "let settled = false;",
    "let tunnelEstablished = false;",
    "function finish(code, message) {",
    "  if (settled) return;",
    "  settled = true;",
    "  if (message) console.error('agentrun git-mirror proxy-connect: ' + message);",
    "  process.exit(code);",
    "}",
    "const socket = net.createConnection({ host: proxyHost, port: proxyPort });",
    "let buffer = Buffer.alloc(0);",
    "socket.setTimeout(15000, () => { socket.destroy(); finish(65, 'timeout connecting via ' + proxyHost + ':' + proxyPort + ' to ' + targetHost + ':' + targetPort); });",
    "socket.on('connect', () => socket.write('CONNECT ' + targetHost + ':' + targetPort + ' HTTP/1.1\\r\\nHost: ' + targetHost + ':' + targetPort + '\\r\\nProxy-Connection: Keep-Alive\\r\\n\\r\\n'));",
    "socket.on('error', (error) => finish(tunnelEstablished ? 69 : 66, (tunnelEstablished ? 'tunnel socket error: ' : 'tcp error connecting to proxy: ') + (error && error.message ? error.message : String(error))));",
    "socket.on('close', () => { if (!tunnelEstablished) finish(68, 'proxy closed before CONNECT completed via ' + proxyHost + ':' + proxyPort + ' to ' + targetHost + ':' + targetPort); else finish(0); });",
    "function onData(chunk) {",
    "  buffer = Buffer.concat([buffer, chunk]);",
    "  const headerEnd = buffer.indexOf('\\r\\n\\r\\n');",
    "  if (headerEnd === -1 && buffer.length < 8192) return;",
    "  if (headerEnd === -1) { socket.destroy(); finish(68, 'proxy response header exceeded 8192 bytes before CONNECT status via ' + proxyHost + ':' + proxyPort + ' to ' + targetHost + ':' + targetPort); return; }",
    "  const head = buffer.slice(0, headerEnd + 4).toString('latin1');",
    "  const statusLine = head.split('\\r\\n', 1)[0] || '';",
    "  const statusCode = Number.parseInt(statusLine.split(' ')[1] || '', 10);",
    "  if (!statusLine.startsWith('HTTP/1.') || !Number.isInteger(statusCode) || statusCode < 200 || statusCode > 299) {",
    "    const safeStatus = statusLine.replace(/[^\\x20-\\x7e]/g, '?').slice(0, 160);",
    "    socket.destroy();",
    "    finish(67, 'proxy CONNECT failed via ' + proxyHost + ':' + proxyPort + ' to ' + targetHost + ':' + targetPort + ': ' + safeStatus);",
    "    return;",
    "  }",
    "  socket.off('data', onData);",
    "  socket.setTimeout(0);",
    "  tunnelEstablished = true;",
    "  const rest = buffer.slice(headerEnd + 4);",
    "  if (rest.length) process.stdout.write(rest);",
    "  process.stdin.on('error', () => {});",
    "  process.stdout.on('error', () => {});",
    "  process.stdin.pipe(socket);",
    "  socket.pipe(process.stdout);",
    "}",
    "socket.on('data', onData);",
    "NODE_PROXY",
    "chmod 0700 /tmp/agentrun-github-proxy-connect.cjs",
    "cat > /tmp/agentrun-git-ssh-proxy.sh <<'SH_PROXY'",
    "#!/bin/sh",
    `exec ssh -i /root/.ssh/id_rsa -o IdentitiesOnly=yes -o BatchMode=yes -o StrictHostKeyChecking=accept-new -o UserKnownHostsFile=/root/.ssh/known_hosts -o ConnectTimeout=15 -o ServerAliveInterval=5 -o ServerAliveCountMax=1 -o ${shQuote(proxyCommand)} "$@"`,
    "SH_PROXY",
    "chmod 0700 /tmp/agentrun-git-ssh-proxy.sh",
    `export HTTP_PROXY=${shQuote(proxyUrl)}`,
    `export HTTPS_PROXY=${shQuote(proxyUrl)}`,
    `export ALL_PROXY=${shQuote(proxyUrl)}`,
    `export http_proxy=${shQuote(proxyUrl)}`,
    `export https_proxy=${shQuote(proxyUrl)}`,
    `export all_proxy=${shQuote(proxyUrl)}`,
    `export NO_PROXY=${shQuote(noProxy)}`,
    `export no_proxy=${shQuote(noProxy)}`,
    "export GIT_SSH=/tmp/agentrun-git-ssh-proxy.sh",
    "unset GIT_SSH_COMMAND",
  ];
}

export function yamlLaneGitMirrorSyncShell(spec: AgentRunLaneSpec): string {
  return [
    "set -eu",
    ...yamlLaneGitMirrorSshSetupShellLines(spec),
    `repository=${shQuote(spec.source.repository)}`,
    `source_branch=${shQuote(spec.source.branch)}`,
    `source_stage_ref_prefix=${shQuote(agentRunSourceSnapshotStageRefPrefix(spec))}`,
    `gitops_branch=${shQuote(spec.gitops.branch)}`,
    `remote=${shQuote(spec.source.remote)}`,
    "repo=\"/cache/${repository}.git\"",
    "mkdir -p \"$(dirname \"$repo\")\"",
    "if [ -d \"$repo/objects\" ] && [ -f \"$repo/HEAD\" ]; then",
    "  git --git-dir=\"$repo\" remote set-url origin \"$remote\" || git --git-dir=\"$repo\" remote add origin \"$remote\"",
    "else",
    "  rm -rf \"$repo\"",
    "  git init --bare \"$repo\"",
    "  git --git-dir=\"$repo\" remote add origin \"$remote\"",
    "fi",
    "git --git-dir=\"$repo\" config uploadpack.allowReachableSHA1InWant true",
    "git --git-dir=\"$repo\" config uploadpack.allowAnySHA1InWant true",
    "git --git-dir=\"$repo\" config http.uploadpack true",
    "git --git-dir=\"$repo\" config http.receivepack true",
    "timeout 240 git --git-dir=\"$repo\" fetch origin \"+refs/heads/${source_branch}:refs/mirror-stage/heads/${source_branch}\"",
    "source_sha=$(git --git-dir=\"$repo\" rev-parse --verify \"refs/mirror-stage/heads/${source_branch}^{commit}\")",
    "source_stage_ref=\"${source_stage_ref_prefix%/}/$source_sha\"",
    "git --git-dir=\"$repo\" update-ref \"$source_stage_ref\" \"$source_sha\"",
    "git --git-dir=\"$repo\" update-ref \"refs/heads/${source_branch}\" \"$source_sha\"",
    "gitops_fetch_err=$(mktemp)",
    "gitops_remote_missing=false",
    "gitops_sha=\"\"",
    "if timeout 240 git --git-dir=\"$repo\" fetch origin \"+refs/heads/${gitops_branch}:refs/mirror-stage/heads/${gitops_branch}\" 2>\"$gitops_fetch_err\"; then",
    "  gitops_sha=$(git --git-dir=\"$repo\" rev-parse --verify \"refs/mirror-stage/heads/${gitops_branch}^{commit}\")",
    "  git --git-dir=\"$repo\" update-ref \"refs/heads/${gitops_branch}\" \"$gitops_sha\"",
    "else",
    "  gitops_fetch_exit=$?",
    "  if grep -qi \"couldn't find remote ref\" \"$gitops_fetch_err\"; then",
    "    gitops_remote_missing=true",
    "    gitops_sha=$(git --git-dir=\"$repo\" rev-parse --verify \"refs/heads/${gitops_branch}^{commit}\" 2>/dev/null || true)",
    "  else",
    "    cat \"$gitops_fetch_err\" >&2",
    "    exit \"$gitops_fetch_exit\"",
    "  fi",
    "fi",
    "git --git-dir=\"$repo\" update-server-info",
    "SOURCE_SHA=\"$source_sha\" SOURCE_STAGE_REF=\"$source_stage_ref\" GITOPS_SHA=\"$gitops_sha\" GITOPS_REMOTE_MISSING=\"$gitops_remote_missing\" node <<'NODE'",
    "console.log(JSON.stringify({ ok: true, sourceAuthority: 'git-mirror-snapshot', localSource: process.env.SOURCE_SHA, sourceStageRef: process.env.SOURCE_STAGE_REF, localGitops: process.env.GITOPS_SHA || null, gitopsRemoteMissing: process.env.GITOPS_REMOTE_MISSING === 'true', valuesPrinted: false }));",
    "NODE",
  ].join("\n");
}

export function yamlLaneGitMirrorFlushShell(spec: AgentRunLaneSpec): string {
  return [
    "set -eu",
    ...yamlLaneGitMirrorSshSetupShellLines(spec),
    `repository=${shQuote(spec.source.repository)}`,
    `gitops_branch=${shQuote(spec.gitops.branch)}`,
    `remote=${shQuote(spec.gitMirror.writeUrl)}`,
    "repo=\"/cache/${repository}.git\"",
    "test -d \"$repo\"",
    "git --git-dir=\"$repo\" remote set-url origin \"$remote\" || git --git-dir=\"$repo\" remote add origin \"$remote\"",
    "local_gitops=$(git --git-dir=\"$repo\" rev-parse --verify \"refs/heads/${gitops_branch}^{commit}\" 2>/dev/null || true)",
    "if [ -n \"$local_gitops\" ]; then",
    "  git --git-dir=\"$repo\" -c remote.origin.mirror=false push origin \"refs/heads/${gitops_branch}:refs/heads/${gitops_branch}\"",
    "  git --git-dir=\"$repo\" fetch origin \"+refs/heads/${gitops_branch}:refs/mirror-stage/heads/${gitops_branch}\"",
    "fi",
    "github_gitops=$(git --git-dir=\"$repo\" rev-parse --verify \"refs/mirror-stage/heads/${gitops_branch}^{commit}\" 2>/dev/null || true)",
    "pending=false; if [ -n \"$local_gitops\" ] && { [ -z \"$github_gitops\" ] || [ \"$local_gitops\" != \"$github_gitops\" ]; }; then pending=true; fi",
    "LOCAL_GITOPS=\"$local_gitops\" GITHUB_GITOPS=\"$github_gitops\" PENDING=\"$pending\" node <<'NODE'",
    "console.log(JSON.stringify({ ok: process.env.PENDING !== 'true', localGitops: process.env.LOCAL_GITOPS || null, githubGitops: process.env.GITHUB_GITOPS || null, pendingFlush: process.env.PENDING === 'true', valuesPrinted: false }));",
    "NODE",
  ].join("\n");
}

export function yamlLaneGitMirrorStatusScript(spec: AgentRunLaneSpec): string {
  const reconciler = spec.gitMirror.repositoryReconciler;
  const desired = agentRunManagedRepositoryDesiredConfig(spec);
  const expectedRepositories = desired.repositories.map((repository) => ({
    key: repository.key,
    repository: repository.repository,
    sourceBranch: repository.sourceBranch,
    desiredFingerprint: repository.desiredFingerprint,
    remoteFingerprint: sha256Fingerprint(repository.remote),
  }));
  const repositoryReadProbes = expectedRepositories.flatMap((repository) => {
    const url = `${spec.gitMirror.resourceBundleBaseUrl.replace(/\/+$/u, "")}/${repository.repository}.git`;
    const ref = `refs/heads/${repository.sourceBranch}`;
    const inner = [
      "url=\"$1\"",
      "ref=\"$2\"",
      "commit=$(timeout 15 git ls-remote \"$url\" \"$ref\" 2>/dev/null | awk 'NR == 1 { print $1 }')",
      "printf '%s\\t%s\\n' \"$3\" \"$commit\"",
    ].join("; ");
    return [
      `( timeout 20 kubectl -n "$namespace" exec deploy/"$controller_deployment" -- sh -lc ${shQuote(inner)} sh ${shQuote(url)} ${shQuote(ref)} ${shQuote(repository.key)} 2>/dev/null || printf '%s\\t\\n' ${shQuote(repository.key)} ) >${shQuote(`/tmp/agentrun-managed-repository-read-refs/${repository.key}.tsv`)} &`,
    ];
  });
  return [
    "set +e",
    `namespace=${shQuote(spec.gitMirror.namespace)}`,
    `read_deployment=${shQuote(spec.gitMirror.readDeployment)}`,
    `read_service=${shQuote(spec.gitMirror.readService)}`,
    `write_service=${shQuote(spec.gitMirror.writeService)}`,
    `cache_pvc=${shQuote(spec.gitMirror.cachePvc)}`,
    `cache_host_path=${spec.gitMirror.cacheHostPath === null ? "''" : shQuote(spec.gitMirror.cacheHostPath)}`,
    `controller_deployment=${shQuote(reconciler.deploymentName)}`,
    `controller_configmap=${shQuote(reconciler.configMapName)}`,
    `expected_desired_hash=${shQuote(desired.desiredHash)}`,
    `expected_render_hash=${shQuote(agentRunManagedRepositoryRenderHash(spec))}`,
    `repositories_json=${shQuote(JSON.stringify(expectedRepositories))}`,
    `repository=${shQuote(spec.source.repository)}`,
    `source_branch=${shQuote(spec.source.branch)}`,
    `gitops_branch=${shQuote(spec.gitops.branch)}`,
    "kubectl -n \"$namespace\" get deploy \"$read_deployment\" -o json >/tmp/agentrun-gitmirror-read-deploy.json 2>/dev/null",
    "read_deploy_exit=$?",
    "kubectl -n \"$namespace\" get svc \"$read_service\" -o json >/tmp/agentrun-gitmirror-read-service.json 2>/dev/null",
    "read_service_exit=$?",
    "kubectl -n \"$namespace\" get endpoints \"$read_service\" -o json >/tmp/agentrun-gitmirror-read-endpoints.json 2>/dev/null",
    "read_endpoints_exit=$?",
    "kubectl -n \"$namespace\" get svc \"$write_service\" -o json >/tmp/agentrun-gitmirror-write-service.json 2>/dev/null",
    "write_service_exit=$?",
    "kubectl -n \"$namespace\" get endpoints \"$write_service\" -o json >/tmp/agentrun-gitmirror-write-endpoints.json 2>/dev/null",
    "write_endpoints_exit=$?",
    "kubectl -n \"$namespace\" get pvc \"$cache_pvc\" -o json >/tmp/agentrun-gitmirror-cache.json 2>/dev/null",
    "cache_exit=$?",
    "kubectl -n \"$namespace\" get deploy \"$controller_deployment\" -o json >/tmp/agentrun-managed-repository-controller.json 2>/dev/null",
    "controller_deploy_exit=$?",
    "kubectl -n \"$namespace\" get configmap \"$controller_configmap\" -o json >/tmp/agentrun-managed-repository-configmap.json 2>/dev/null",
    "controller_configmap_exit=$?",
    "cache_mode=pvc",
    "if [ -n \"$cache_host_path\" ]; then cache_mode=hostPath; fi",
    "timeout 10 kubectl -n \"$namespace\" exec deploy/\"$controller_deployment\" -- node /etc/agentrun-managed-repository/controller.mjs --status --config /etc/agentrun-managed-repository/config.json >/tmp/agentrun-managed-repository-status.json 2>/dev/null",
    "controller_status_exit=$?",
    "rm -f /tmp/agentrun-gitmirror-legacy-refs.txt",
    "timeout 10 kubectl -n \"$namespace\" exec deploy/\"$read_deployment\" -- sh -lc 'repo_path=\"$1\"; source_branch=\"$2\"; gitops_branch=\"$3\"; source_commit=$(git --git-dir=\"$repo_path\" rev-parse --verify \"refs/heads/$source_branch^{commit}\" 2>/dev/null || true); gitops_commit=$(git --git-dir=\"$repo_path\" rev-parse --verify \"refs/heads/$gitops_branch^{commit}\" 2>/dev/null || true); printf \"sourceCommit=%s\\n\" \"$source_commit\"; printf \"gitopsCommit=%s\\n\" \"$gitops_commit\"' sh \"/cache/${repository}.git\" \"$source_branch\" \"$gitops_branch\" >/tmp/agentrun-gitmirror-legacy-refs.txt 2>/dev/null",
    "rm -rf /tmp/agentrun-managed-repository-read-refs",
    "mkdir -p /tmp/agentrun-managed-repository-read-refs",
    ...repositoryReadProbes,
    "wait",
    "cat /tmp/agentrun-managed-repository-read-refs/*.tsv > /tmp/agentrun-managed-repository-read-refs.tsv 2>/dev/null || true",
    "NAMESPACE=\"$namespace\" READ_DEPLOY_EXIT=\"$read_deploy_exit\" READ_SERVICE_EXIT=\"$read_service_exit\" READ_ENDPOINTS_EXIT=\"$read_endpoints_exit\" WRITE_SERVICE_EXIT=\"$write_service_exit\" WRITE_ENDPOINTS_EXIT=\"$write_endpoints_exit\" CACHE_EXIT=\"$cache_exit\" CONTROLLER_DEPLOY_EXIT=\"$controller_deploy_exit\" CONTROLLER_CONFIGMAP_EXIT=\"$controller_configmap_exit\" CONTROLLER_STATUS_EXIT=\"$controller_status_exit\" CACHE_MODE=\"$cache_mode\" CACHE_HOST_PATH=\"$cache_host_path\" EXPECTED_DESIRED_HASH=\"$expected_desired_hash\" EXPECTED_RENDER_HASH=\"$expected_render_hash\" REPOSITORIES_JSON=\"$repositories_json\" REPOSITORY=\"$repository\" SOURCE_BRANCH=\"$source_branch\" GITOPS_BRANCH=\"$gitops_branch\" python3 - <<'PY'",
    "import json, os",
    "def read_json(path):",
    "    try:",
    "        with open(path, 'r', encoding='utf-8') as fh:",
    "            value = json.load(fh)",
    "            return value if isinstance(value, dict) else {}",
    "    except Exception:",
    "        return {}",
    "def exit_ok(name):",
    "    return os.environ.get(name) == '0'",
    "def deployment_ready(value):",
    "    status = value.get('status') or {}",
    "    spec = value.get('spec') or {}",
    "    replicas = int(spec.get('replicas') or 0)",
    "    available = int(status.get('availableReplicas') or 0)",
    "    return replicas > 0 and available >= replicas and int(status.get('observedGeneration') or 0) >= int((value.get('metadata') or {}).get('generation') or 0)",
    "def endpoint_ready(value):",
    "    for subset in value.get('subsets') or []:",
    "        if len(subset.get('addresses') or []) > 0:",
    "            return True",
    "    return False",
    "def annotation(value, key):",
    "    return ((value.get('metadata') or {}).get('annotations') or {}).get(key)",
    "def pod_annotation(value, key):",
    "    return (((((value.get('spec') or {}).get('template') or {}).get('metadata') or {}).get('annotations') or {}).get(key))",
    "def read_refs(path):",
    "    result = {}",
    "    try:",
    "        with open(path, 'r', encoding='utf-8') as fh:",
    "            for line in fh:",
    "                parts = line.rstrip('\\n').split('\\t', 1)",
    "                if len(parts) == 2:",
    "                    result[parts[0]] = parts[1] or None",
    "    except Exception:",
    "        pass",
    "    return result",
    "def legacy_ref(path, key):",
    "    prefix = key + '='",
    "    try:",
    "        with open(path, 'r', encoding='utf-8') as fh:",
    "            for line in fh:",
    "                if line.startswith(prefix):",
    "                    return line[len(prefix):].strip() or None",
    "    except Exception:",
    "        pass",
    "    return None",
    "try:",
    "    repositories = json.loads(os.environ.get('REPOSITORIES_JSON') or '[]')",
    "except Exception:",
    "    repositories = []",
    "read_deploy = read_json('/tmp/agentrun-gitmirror-read-deploy.json')",
    "read_endpoints = read_json('/tmp/agentrun-gitmirror-read-endpoints.json')",
    "write_endpoints = read_json('/tmp/agentrun-gitmirror-write-endpoints.json')",
    "cache = read_json('/tmp/agentrun-gitmirror-cache.json')",
    "controller_deploy = read_json('/tmp/agentrun-managed-repository-controller.json')",
    "controller_configmap = read_json('/tmp/agentrun-managed-repository-configmap.json')",
    "controller_status = read_json('/tmp/agentrun-managed-repository-status.json')",
    "served_refs = read_refs('/tmp/agentrun-managed-repository-read-refs.tsv')",
    "expected_desired_hash = os.environ.get('EXPECTED_DESIRED_HASH')",
    "expected_render_hash = os.environ.get('EXPECTED_RENDER_HASH')",
    "read_ready = exit_ok('READ_DEPLOY_EXIT') and deployment_ready(read_deploy) and exit_ok('READ_SERVICE_EXIT') and exit_ok('READ_ENDPOINTS_EXIT') and endpoint_ready(read_endpoints)",
    "write_ready = exit_ok('WRITE_SERVICE_EXIT') and exit_ok('WRITE_ENDPOINTS_EXIT') and endpoint_ready(write_endpoints)",
    "cache_pvc_exists = exit_ok('CACHE_EXIT')",
    "cache_mode = os.environ.get('CACHE_MODE') or 'pvc'",
    "cache_ready = True if cache_mode == 'hostPath' else cache_pvc_exists and (cache.get('status') or {}).get('phase') == 'Bound'",
    "controller_deployment_ready = exit_ok('CONTROLLER_DEPLOY_EXIT') and deployment_ready(controller_deploy)",
    "controller_config_rendered = exit_ok('CONTROLLER_CONFIGMAP_EXIT') and annotation(controller_configmap, 'unidesk.ai/managed-repository-desired-sha') == expected_desired_hash and annotation(controller_configmap, 'unidesk.ai/managed-repository-render-sha') == expected_render_hash",
    "controller_workload_rendered = annotation(controller_deploy, 'unidesk.ai/managed-repository-desired-sha') == expected_desired_hash and annotation(controller_deploy, 'unidesk.ai/managed-repository-render-sha') == expected_render_hash and pod_annotation(controller_deploy, 'unidesk.ai/managed-repository-desired-sha') == expected_desired_hash and pod_annotation(controller_deploy, 'unidesk.ai/managed-repository-render-sha') == expected_render_hash",
    "controller_status_aligned = exit_ok('CONTROLLER_STATUS_EXIT') and controller_status.get('desiredHash') == expected_desired_hash",
    "status_repositories = {item.get('desiredFingerprint'): item for item in controller_status.get('repositories') or [] if isinstance(item, dict)}",
    "repository_status = []",
    "for desired in repositories:",
    "    observed = status_repositories.get(desired.get('desiredFingerprint')) or {}",
    "    served_commit = served_refs.get(desired.get('key'))",
    "    cache_commit = observed.get('commit')",
    "    remote_fingerprint_aligned = observed.get('remoteFingerprint') == desired.get('remoteFingerprint')",
    "    cache_ref_present = observed.get('refPresent') is True and isinstance(cache_commit, str) and len(cache_commit) == 40",
    "    served_ref_present = isinstance(served_commit, str) and len(served_commit) == 40",
    "    repository_status.append({",
    "      'key': desired.get('key'),",
    "      'repository': desired.get('repository'),",
    "      'sourceBranch': desired.get('sourceBranch'),",
    "      'desiredFingerprint': desired.get('desiredFingerprint'),",
    "      'remoteFingerprint': desired.get('remoteFingerprint'),",
    "      'observedRemoteFingerprint': observed.get('remoteFingerprint'),",
    "      'remoteFingerprintAligned': remote_fingerprint_aligned,",
    "      'cacheRefPresent': cache_ref_present,",
    "      'cacheCommit': cache_commit if cache_ref_present else None,",
    "      'servedRefPresent': served_ref_present,",
    "      'servedCommit': served_commit if served_ref_present else None,",
    "      'refAligned': cache_ref_present and served_ref_present and cache_commit == served_commit,",
    "      'fresh': observed.get('fresh') is True,",
    "      'lastSuccessAt': observed.get('lastSuccessAt'),",
    "      'lastSuccessAgeMs': observed.get('lastSuccessAgeMs'),",
    "      'lastAttemptOk': observed.get('lastAttemptOk') is True,",
    "      'errorCode': observed.get('errorCode'),",
    "      'errorFingerprint': observed.get('errorFingerprint'),",
    "    })",
    "first_drift = None",
    "if not controller_config_rendered:",
    "    first_drift = 'rendered-desired-not-applied'",
    "elif not controller_workload_rendered:",
    "    first_drift = 'controller-workload-render-mismatch'",
    "elif not controller_deployment_ready:",
    "    first_drift = 'controller-not-ready'",
    "elif not cache_ready:",
    "    first_drift = 'cache-not-ready'",
    "elif not controller_status_aligned:",
    "    first_drift = 'controller-status-not-aligned'",
    "elif not read_ready:",
    "    first_drift = 'read-service-not-ready'",
    "elif not write_ready:",
    "    first_drift = 'write-service-not-ready'",
    "else:",
    "    for item in repository_status:",
    "        if not item.get('cacheRefPresent'):",
    "            first_drift = 'cache-ref-missing:' + str(item.get('key'))",
    "            break",
    "        if not item.get('remoteFingerprintAligned'):",
    "            first_drift = 'remote-fingerprint-mismatch:' + str(item.get('key'))",
    "            break",
    "        if not item.get('fresh'):",
    "            first_drift = 'repository-stale:' + str(item.get('key'))",
    "            break",
    "        if not item.get('servedRefPresent'):",
    "            first_drift = 'served-ref-missing:' + str(item.get('key'))",
    "            break",
    "        if not item.get('refAligned'):",
    "            first_drift = 'served-ref-mismatch:' + str(item.get('key'))",
    "            break",
    "repositories_ready = len(repository_status) == len(repositories) and all(item.get('cacheRefPresent') and item.get('fresh') and item.get('refAligned') and item.get('remoteFingerprintAligned') for item in repository_status)",
    "ok = controller_config_rendered and controller_workload_rendered and controller_deployment_ready and controller_status_aligned and cache_ready and read_ready and write_ready and repositories_ready",
    "print(json.dumps({",
    "  'ok': ok,",
    "  'namespace': os.environ.get('NAMESPACE'),",
    "  'readReady': read_ready,",
    "  'writeReady': write_ready,",
    "  'cacheMode': cache_mode,",
    "  'cacheReady': cache_ready,",
    "  'cachePvcExists': cache_pvc_exists,",
    "  'cacheHostPath': os.environ.get('CACHE_HOST_PATH') or None,",
    "  'repository': os.environ.get('REPOSITORY'),",
    "  'sourceBranch': os.environ.get('SOURCE_BRANCH'),",
    "  'gitopsBranch': os.environ.get('GITOPS_BRANCH'),",
    "  'sourceCommit': legacy_ref('/tmp/agentrun-gitmirror-legacy-refs.txt', 'sourceCommit'),",
    "  'gitopsCommit': legacy_ref('/tmp/agentrun-gitmirror-legacy-refs.txt', 'gitopsCommit'),",
    "  'controller': {",
    "    'deploymentReady': controller_deployment_ready,",
    "    'configRendered': controller_config_rendered,",
    "    'workloadRendered': controller_workload_rendered,",
    "    'statusAligned': controller_status_aligned,",
    "    'desiredHash': expected_desired_hash,",
    "    'renderHash': expected_render_hash,",
    "    'observedDesiredHash': controller_status.get('desiredHash'),",
    "    'heartbeatAt': controller_status.get('heartbeatAt'),",
    "    'heartbeatAgeMs': controller_status.get('heartbeatAgeMs'),",
    "    'heartbeatFresh': controller_status.get('heartbeatFresh') is True,",
    "  },",
    "  'repositoriesReady': repositories_ready,",
    "  'repositories': repository_status,",
    "  'firstDrift': first_drift,",
    "  'pendingFlush': None,",
    "  'valuesPrinted': False",
    "}, ensure_ascii=False))",
    "PY",
  ].join("\n");
}

export type LaneSecretSource = {
  id: string;
  sourceRef: string;
  sourceMode: "env" | "file" | "codex-config" | "json";
  sourceKey: string | null;
  sourceFormat?: "env" | "raw-token" | null;
  codexConfig?: AgentRunCodexConfigSpec | null;
  jsonValue?: unknown;
  targetRef: { namespace: string; name: string; key: string };
  transform?: "codex-auth-json-openai-api-key" | "local-postgres-database" | "local-postgres-user" | "local-postgres-database-url" | null;
};

export interface LaneSecretSourceInspection {
  readonly ok: boolean;
  readonly sourceValue: ReturnType<typeof readSecretSourceValue> | null;
  readonly summary: {
    readonly present: boolean;
    readonly sourceFilePresent: boolean;
    readonly sourcePath: string;
    readonly fingerprint: string | null;
    readonly valueBytes: number | null;
    readonly degradedReason: string | null;
    readonly message: string | null;
    readonly valuesPrinted: false;
  };
}

export function inspectSecretSourceValue(spec: AgentRunLaneSpec, source: LaneSecretSource): LaneSecretSourceInspection {
  let sourcePath: string | null = null;
  let sourceFilePresent = isRenderedLaneSecretSource(source);
  try {
    sourcePath = resolveLaneSecretSourcePath(spec, source);
    sourceFilePresent = isRenderedLaneSecretSource(source) || sourcePath !== null && existsSync(sourcePath);
    const sourceValue = readSecretSourceValue(spec, source);
    return {
      ok: true,
      sourceValue,
      summary: {
        present: true,
        sourceFilePresent,
        sourcePath: sourceValue.redactedPath,
        fingerprint: sourceValue.fingerprint,
        valueBytes: sourceValue.valueBytes,
        degradedReason: null,
        message: null,
        valuesPrinted: false,
      },
    };
  } catch (error) {
    const message = error instanceof Error ? error.message : String(error);
    return {
      ok: false,
      sourceValue: null,
      summary: {
        present: false,
        sourceFilePresent,
        sourcePath: isRenderedLaneSecretSource(source)
          ? source.sourceRef
          : redactAbsoluteSecretPath(sourcePath ?? source.sourceRef),
        fingerprint: null,
        valueBytes: null,
        degradedReason: classifySecretSourceFailure(message),
        message: message.slice(0, 500),
        valuesPrinted: false,
      },
    };
  }
}

export function readSecretSourceValue(spec: AgentRunLaneSpec, source: LaneSecretSource): { redactedPath: string; value: string; valueBytes: number; fingerprint: string } {
  const { sourceRef } = source;
  if (sourceRef.includes("..")) throw new Error(`secret sourceRef must not contain ..: ${sourceRef}`);
  const sourcePath = resolveLaneSecretSourcePath(spec, source);
  if (sourcePath !== null && !existsSync(sourcePath)) throw new Error(`secret source ${sourceRef} is missing`);
  let value: string;
  if (source.sourceMode === "codex-config") {
    if (source.codexConfig === undefined || source.codexConfig === null) throw new Error(`secret source ${sourceRef} is missing codexConfig`);
    value = renderCodexConfigToml(source.codexConfig);
  } else if (source.sourceMode === "json") {
    if (source.jsonValue === undefined) throw new Error(`secret source ${sourceRef} is missing jsonValue`);
    value = `${JSON.stringify(source.jsonValue, null, 2)}\n`;
  } else if (source.sourceMode === "file") {
    if (sourcePath === null) throw new Error(`secret source ${sourceRef} has no source path`);
    value = readFileSync(sourcePath, "utf8");
  } else {
    if (sourcePath === null) throw new Error(`secret source ${sourceRef} has no source path`);
    if (source.sourceKey === null) throw new Error(`secret source ${sourceRef} is missing sourceKey`);
    const text = readFileSync(sourcePath, "utf8");
    const envValue = source.sourceFormat === "raw-token" ? text.trim() : parseEnvFile(text).get(source.sourceKey);
    if (envValue === undefined || envValue.length === 0) throw new Error(`secret source ${sourceRef} is missing required key ${source.sourceKey}`);
    value = envValue;
  }
  if (value.length === 0) throw new Error(`secret source ${sourceRef} is empty`);
  value = transformSecretSourceValue(spec, source, value);
  return {
    redactedPath: isRenderedLaneSecretSource(source) ? sourceRef : redactAbsoluteSecretPath(sourcePath ?? sourceRef),
    value,
    valueBytes: Buffer.byteLength(value, "utf8"),
    fingerprint: sha256Fingerprint(value),
  };
}

function resolveLaneSecretSourcePath(spec: AgentRunLaneSpec, source: LaneSecretSource): string | null {
  if (source.sourceRef.includes("..")) throw new Error(`secret sourceRef must not contain ..: ${source.sourceRef}`);
  if (isRenderedLaneSecretSource(source)) return null;
  return source.sourceRef.startsWith("/")
    ? source.sourceRef
    : join(resolveSecretSourceRoot(spec), ...source.sourceRef.split("/"));
}

function isRenderedLaneSecretSource(source: LaneSecretSource): boolean {
  return source.sourceMode === "codex-config" || source.sourceMode === "json";
}

function classifySecretSourceFailure(message: string): string {
  if (message.includes("missing required key")) return "secret-source-key-missing";
  if (message.includes(" is missing")) return "secret-source-file-missing";
  if (message.includes(" is empty")) return "secret-source-empty";
  if (message.includes("must not contain ..")) return "secret-source-ref-invalid";
  return "secret-source-read-failed";
}

function renderCodexConfigToml(config: AgentRunCodexConfigSpec): string {
  const lines = [
    `model_provider = ${tomlString(config.modelProvider)}`,
    `model = ${tomlString(config.model)}`,
    ...(config.reviewModel === null ? [] : [`review_model = ${tomlString(config.reviewModel)}`]),
    ...(config.modelReasoningEffort === null ? [] : [`model_reasoning_effort = ${tomlString(config.modelReasoningEffort)}`]),
    ...(config.disableResponseStorage === null ? [] : [`disable_response_storage = ${tomlBoolean(config.disableResponseStorage)}`]),
    ...(config.networkAccess === null ? [] : [`network_access = ${tomlString(config.networkAccess)}`]),
    ...(config.windowsWslSetupAcknowledged === null ? [] : [`windows_wsl_setup_acknowledged = ${tomlBoolean(config.windowsWslSetupAcknowledged)}`]),
    ...(config.serviceTier === null ? [] : [`service_tier = ${tomlString(config.serviceTier)}`]),
    ...(config.modelContextWindow === null ? [] : [`model_context_window = ${config.modelContextWindow}`]),
    ...(config.modelAutoCompactTokenLimit === null ? [] : [`model_auto_compact_token_limit = ${config.modelAutoCompactTokenLimit}`]),
    ...(config.modelCatalogJson === null ? [] : [`model_catalog_json = ${tomlString(config.modelCatalogJson)}`]),
    ...(config.approvalsReviewer === null ? [] : [`approvals_reviewer = ${tomlString(config.approvalsReviewer)}`]),
    "",
  ];
  for (const provider of config.modelProviders) {
    lines.push(
      `[model_providers.${tomlKeySegment(provider.id)}]`,
      `name = ${tomlString(provider.name)}`,
      `base_url = ${tomlString(provider.baseUrl)}`,
      `wire_api = ${tomlString(provider.wireApi)}`,
      ...(provider.requiresOpenaiAuth === null ? [] : [`requires_openai_auth = ${tomlBoolean(provider.requiresOpenaiAuth)}`]),
      "",
    );
  }
  if (Object.keys(config.features).length > 0) {
    lines.push("[features]");
    for (const [key, value] of Object.entries(config.features)) lines.push(`${tomlKeySegment(key)} = ${tomlBoolean(value)}`);
    lines.push("");
  }
  for (const project of config.projects) {
    lines.push(`[projects.${tomlKeySegment(project.path)}]`, `trust_level = ${tomlString(project.trustLevel)}`, "");
  }
  if (Object.keys(config.tuiModelAvailabilityNux).length > 0) {
    lines.push("[tui.model_availability_nux]");
    for (const [key, value] of Object.entries(config.tuiModelAvailabilityNux)) lines.push(`${tomlKeySegment(key)} = ${value}`);
    lines.push("");
  }
  if (config.noticeHideFullAccessWarning !== null) {
    lines.push("[notice]", `hide_full_access_warning = ${tomlBoolean(config.noticeHideFullAccessWarning)}`, "");
  }
  return `${lines.join("\n").trimEnd()}\n`;
}

function tomlString(value: string): string {
  return JSON.stringify(value);
}

function tomlBoolean(value: boolean): string {
  return value ? "true" : "false";
}

function tomlKeySegment(value: string): string {
  return /^[A-Za-z0-9_-]+$/u.test(value) ? value : tomlString(value);
}

function transformSecretSourceValue(spec: AgentRunLaneSpec, source: LaneSecretSource, rawValue: string): string {
  if (source.transform === undefined || source.transform === null) return rawValue;
  if (source.transform === "codex-auth-json-openai-api-key") {
    return `${JSON.stringify({ OPENAI_API_KEY: rawValue }, null, 2)}\n`;
  }
  const localPostgres = spec.deployment.localPostgres;
  if (!localPostgres.enabled || localPostgres.serviceName === null || localPostgres.database === null || localPostgres.user === null || localPostgres.port === null) {
    throw new Error(`secret source ${source.id} requires enabled localPostgres with database/user/port`);
  }
  if (source.transform === "local-postgres-database") return localPostgres.database;
  if (source.transform === "local-postgres-user") return localPostgres.user;
  const user = encodeURIComponent(localPostgres.user);
  const password = encodeURIComponent(rawValue);
  const database = encodeURIComponent(localPostgres.database);
  return `postgresql://${user}:${password}@${localPostgres.serviceName}:${localPostgres.port}/${database}`;
}

export function redactAbsoluteSecretPath(sourceRef: string): string {
  const parts = sourceRef.split("/").filter(Boolean);
  return parts.length === 0 ? "/" : `/${parts.slice(0, -1).join("/")}/<redacted>`;
}

export function resolveSecretSourceRoot(spec: AgentRunLaneSpec): string {
  if (spec.database.configRef === null) return rootPath(".state", "secrets");
  const configPath = spec.database.configRef.startsWith("/") ? spec.database.configRef : rootPath(spec.database.configRef);
  const configRoot = record(Bun.YAML.parse(readFileSync(configPath, "utf8")) as unknown);
  const secrets = record(configRoot.secrets);
  const root = stringOrNull(secrets.root);
  if (root === null || !root.startsWith("/") || root.includes("..")) throw new Error(`${spec.database.configRef}.secrets.root must be an absolute path without ..`);
  return root;
}

export function parseEnvFile(text: string): Map<string, string> {
  const result = new Map<string, string>();
  for (const rawLine of text.split(/\r?\n/u)) {
    let line = rawLine.trim();
    if (line.length === 0 || line.startsWith("#")) continue;
    if (line.startsWith("export ")) line = line.slice("export ".length).trim();
    const index = line.indexOf("=");
    if (index <= 0) continue;
    const key = line.slice(0, index).trim();
    let value = line.slice(index + 1).trim();
    if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"))) {
      value = value.slice(1, -1);
    }
    result.set(key, value);
  }
  return result;
}

export function collectLaneSecretSources(spec: AgentRunLaneSpec): LaneSecretSource[] {
  const result: LaneSecretSource[] = [];
  if (spec.database.secretSourceRef !== null) {
    result.push({
      id: "database",
      sourceRef: spec.database.secretSourceRef,
      sourceMode: "env",
      sourceKey: spec.database.secretRef.key,
      targetRef: { namespace: spec.runtime.namespace, name: spec.database.secretRef.name, key: spec.database.secretRef.key },
    });
  }
  const localPostgres = spec.deployment.localPostgres;
  if (spec.database.mode === "local-postgres" && localPostgres.enabled) {
    if (localPostgres.passwordSourceRef === null || localPostgres.passwordSourceKey === null) {
      throw new Error(`config/agentrun.yaml controlPlane.lanes.${spec.lane}.deployment.localPostgres must declare passwordSourceRef/passwordSourceKey`);
    }
    result.push(
      {
        id: "local-postgres-password",
        sourceRef: localPostgres.passwordSourceRef,
        sourceMode: "env",
        sourceKey: localPostgres.passwordSourceKey,
        targetRef: { namespace: spec.runtime.namespace, name: spec.database.secretRef.name, key: "POSTGRES_PASSWORD" },
      },
      {
        id: "local-postgres-user",
        sourceRef: localPostgres.passwordSourceRef,
        sourceMode: "env",
        sourceKey: localPostgres.passwordSourceKey,
        targetRef: { namespace: spec.runtime.namespace, name: spec.database.secretRef.name, key: "POSTGRES_USER" },
        transform: "local-postgres-user",
      },
      {
        id: "local-postgres-database",
        sourceRef: localPostgres.passwordSourceRef,
        sourceMode: "env",
        sourceKey: localPostgres.passwordSourceKey,
        targetRef: { namespace: spec.runtime.namespace, name: spec.database.secretRef.name, key: "POSTGRES_DB" },
        transform: "local-postgres-database",
      },
      {
        id: "database",
        sourceRef: localPostgres.passwordSourceRef,
        sourceMode: "env",
        sourceKey: localPostgres.passwordSourceKey,
        targetRef: { namespace: spec.runtime.namespace, name: spec.database.secretRef.name, key: spec.database.secretRef.key },
        transform: "local-postgres-database-url",
      },
    );
  }
  for (const secret of spec.secrets) {
    result.push({
      id: secret.id,
      sourceRef: secret.sourceRef,
      sourceMode: secret.sourceMode,
      sourceKey: secret.sourceKey,
      sourceFormat: secret.sourceFormat,
      transform: secret.transform,
      codexConfig: secret.codexConfig,
      jsonValue: secret.jsonValue,
      targetRef: secret.targetRef,
    });
  }
  const seenIds = new Set<string>();
  const seenTargetRefs = new Set<string>();
  for (const item of result) {
    if (seenIds.has(item.id)) throw new Error(`duplicate rendered lane Secret id: ${item.id}`);
    seenIds.add(item.id);
    const targetRef = `${item.targetRef.namespace}/${item.targetRef.name}/${item.targetRef.key}`;
    if (seenTargetRefs.has(targetRef)) throw new Error(`duplicate rendered lane Secret targetRef: ${targetRef}`);
    seenTargetRefs.add(targetRef);
  }
  return result;
}

export function secretSyncScript(_spec: AgentRunLaneSpec, values: Array<{ targetRef: { namespace: string; name: string; key: string }; value: string }>): string {
  const encoded = Buffer.from(JSON.stringify(values.map((item) => {
    const valueBase64 = Buffer.from(item.value, "utf8").toString("base64");
    return {
      targetRef: item.targetRef,
      valueBase64,
      sourceValueBytes: Buffer.byteLength(item.value, "utf8"),
      sourceFingerprint: sha256Fingerprint(item.value),
      encodedBytes: Buffer.byteLength(valueBase64, "utf8"),
      encodedFingerprint: sha256Fingerprint(valueBase64),
    };
  })), "utf8").toString("base64");
  return [
    "set -eu",
    `payload_b64=${shQuote(encoded)}`,
    "tmp_dir=$(mktemp -d)",
    "trap 'rm -rf \"$tmp_dir\"' EXIT",
    "payload_json=\"$tmp_dir/payload.json\"",
    "printf '%s' \"$payload_b64\" | base64 -d > \"$payload_json\"",
    "PAYLOAD_JSON=\"$payload_json\" TMP_DIR=\"$tmp_dir\" python3 - <<'PY'",
    "import hashlib, json, os, subprocess",
    "def run(argv, input_text=None):",
    "    out = subprocess.run(argv, input=input_text, text=True, capture_output=True)",
    "    if out.returncode != 0:",
    "        raise RuntimeError('%s failed: %s' % (' '.join(argv), (out.stderr or out.stdout).strip()))",
    "    return out",
    "def try_run(argv):",
    "    return subprocess.run(argv, text=True, capture_output=True)",
    "with open(os.environ['PAYLOAD_JSON'], 'r', encoding='utf-8') as fh:",
    "    items = json.load(fh)",
    "groups = {}",
    "for item in items:",
    "    ref = item.get('targetRef') or {}",
    "    key = (ref.get('namespace'), ref.get('name'))",
    "    groups.setdefault(key, {'namespace': ref.get('namespace'), 'name': ref.get('name'), 'items': []})['items'].append({'key': ref.get('key'), 'valueBase64': item.get('valueBase64') or '', 'sourceValueBytes': item.get('sourceValueBytes'), 'sourceFingerprint': item.get('sourceFingerprint'), 'desiredEncodedBytes': item.get('encodedBytes'), 'desiredEncodedFingerprint': item.get('encodedFingerprint')})",
    "results = []",
    "for group in groups.values():",
    "    namespace = group['namespace']",
    "    name = group['name']",
    "    ns_yaml = run(['kubectl', 'create', 'namespace', namespace, '--dry-run=client', '-o', 'yaml']).stdout",
    "    run(['kubectl', 'apply', '--server-side', '--field-manager=unidesk-agentrun-secret-sync', '-f', '-'], ns_yaml)",
    "    existing = try_run(['kubectl', '-n', namespace, 'get', 'secret', name])",
    "    if existing.returncode != 0:",
    "        run(['kubectl', '-n', namespace, 'create', 'secret', 'generic', name])",
    "    patch = {'data': {entry['key']: entry['valueBase64'] for entry in group['items']}}",
    "    patch_file = os.path.join(os.environ['TMP_DIR'], '%s-%s-patch.json' % (namespace, name))",
    "    with open(patch_file, 'w', encoding='utf-8') as fh:",
    "        json.dump(patch, fh)",
    "    run(['kubectl', '-n', namespace, 'patch', 'secret', name, '--type=merge', '--patch-file=%s' % patch_file])",
    "    fetched = json.loads(run(['kubectl', '-n', namespace, 'get', 'secret', name, '-o', 'json']).stdout)",
    "    data = fetched.get('data') or {}",
    "    for entry in group['items']:",
    "        raw = data.get(entry['key']) or ''",
    "        encoded_bytes = len(raw.encode('utf-8')) if raw else 0",
    "        encoded_fingerprint = ('sha256:' + hashlib.sha256(raw.encode('utf-8')).hexdigest()) if raw else None",
    "        encoded_matches = bool(raw) and encoded_fingerprint == entry['desiredEncodedFingerprint'] and encoded_bytes == entry['desiredEncodedBytes']",
    "        results.append({'namespace': namespace, 'secret': name, 'key': entry['key'], 'present': bool(raw), 'ok': encoded_matches, 'sourceValueBytes': entry['sourceValueBytes'], 'sourceFingerprint': entry['sourceFingerprint'], 'encodedBytes': encoded_bytes, 'encodedFingerprint': encoded_fingerprint, 'encodedFingerprintMatches': encoded_matches, 'valuesPrinted': False})",
    "print(json.dumps({'ok': all(item['ok'] for item in results), 'secretCount': len(results), 'items': results, 'runtimeFingerprintMode': 'kubernetes-secret-data-base64-sha256', 'runtimeSecretValuesDecoded': False, 'valuesPrinted': False}, ensure_ascii=False))",
    "PY",
  ].join("\n");
}

export function restartYamlLaneScript(spec: AgentRunLaneSpec): string {
  return [
    "set +e",
    `namespace=${shQuote(spec.runtime.namespace)}`,
    `deployment=${shQuote(spec.runtime.managerDeployment)}`,
    "ts=$(date -u +%Y-%m-%dT%H:%M:%SZ)",
    "kubectl -n \"$namespace\" patch deployment \"$deployment\" --type='merge' -p \"{\\\"spec\\\":{\\\"template\\\":{\\\"metadata\\\":{\\\"annotations\\\":{\\\"kubectl.kubernetes.io/restartedAt\\\":\\\"$ts\\\"}}}}}\" >/tmp/agentrun-restart-patch.out 2>/tmp/agentrun-restart-patch.err",
    "patch_rc=$?",
    "if [ \"$patch_rc\" -eq 0 ]; then",
    "  kubectl -n \"$namespace\" rollout status deployment/\"$deployment\" --timeout=180s >/tmp/agentrun-rollout-status.out 2>/tmp/agentrun-rollout-status.err",
    "  rollout_rc=$?",
    "else",
    "  rollout_rc=$patch_rc",
    "fi",
    "pods_json=$(kubectl -n \"$namespace\" get pod -l app.kubernetes.io/name=agentrun-mgr -o json 2>/dev/null || printf '{}')",
    "deployment_json=$(kubectl -n \"$namespace\" get deployment \"$deployment\" -o json 2>/dev/null || printf '{}')",
    "patch_err=$(cat /tmp/agentrun-restart-patch.err 2>/dev/null || true)",
    "rollout_err=$(cat /tmp/agentrun-rollout-status.err 2>/dev/null || true)",
    "PODS_JSON=\"$pods_json\" DEPLOYMENT_JSON=\"$deployment_json\" PATCH_RC=\"$patch_rc\" ROLLOUT_RC=\"$rollout_rc\" TS=\"$ts\" PATCH_ERR=\"$patch_err\" ROLLOUT_ERR=\"$rollout_err\" node <<'NODE'",
    "const pods = JSON.parse(process.env.PODS_JSON || '{}');",
    "const deployment = JSON.parse(process.env.DEPLOYMENT_JSON || '{}');",
    "const conditions = Array.isArray(deployment.status?.conditions) ? deployment.status.conditions : [];",
    "const items = Array.isArray(pods.items) ? pods.items : [];",
    "const compactError = (value) => String(value || '').replace(/\\s+/g, ' ').trim().slice(0, 500) || null;",
    "console.log(JSON.stringify({",
    "  ok: process.env.PATCH_RC === '0' && process.env.ROLLOUT_RC === '0',",
    "  restartedAt: process.env.TS,",
    "  patch: { exitCode: Number(process.env.PATCH_RC || '1'), error: compactError(process.env.PATCH_ERR) },",
    "  rollout: { exitCode: Number(process.env.ROLLOUT_RC || '1'), error: compactError(process.env.ROLLOUT_ERR) },",
    "  readyReplicas: deployment.status?.readyReplicas ?? null,",
    "  replicas: deployment.status?.replicas ?? null,",
    "  conditions: conditions.map((item) => ({ type: item.type, status: item.status, reason: item.reason ?? null })),",
    "  pods: items.map((pod) => ({",
    "    name: pod.metadata?.name ?? null,",
    "    phase: pod.status?.phase ?? null,",
    "    ready: (pod.status?.containerStatuses ?? []).map((item) => item.ready),",
    "    restarts: (pod.status?.containerStatuses ?? []).map((item) => item.restartCount),",
    "    waiting: (pod.status?.containerStatuses ?? []).map((item) => item.state?.waiting?.reason ?? null),",
    "  })),",
    "  valuesPrinted: false",
    "}));",
    "NODE",
    "exit \"$rollout_rc\"",
  ].join("\n");
}

export function applyYamlScript(yaml: string, fieldManager: string, dryRun: boolean): string {
  const encoded = Buffer.from(yaml, "utf8").toString("base64");
  return [
    "set -eu",
    `manifest_b64=${shQuote(encoded)}`,
    `field_manager=${shQuote(fieldManager)}`,
    `dry_run=${dryRun ? "true" : "false"}`,
    "tmp_dir=$(mktemp -d)",
    "trap 'rm -rf \"$tmp_dir\"' EXIT",
    "manifest=\"$tmp_dir/manifest.yaml\"",
    "printf '%s' \"$manifest_b64\" | base64 -d > \"$manifest\"",
    "args=\"--server-side --force-conflicts --field-manager=$field_manager\"",
    "if [ \"$dry_run\" = true ]; then args=\"$args --dry-run=server\"; fi",
    "set +e",
    "kubectl apply $args -f \"$manifest\" > \"$tmp_dir/apply.out\" 2> \"$tmp_dir/apply.err\"",
    "apply_exit=$?",
    "set -e",
    "APPLY_EXIT=\"$apply_exit\" APPLY_OUT=\"$tmp_dir/apply.out\" APPLY_ERR=\"$tmp_dir/apply.err\" MANIFEST=\"$manifest\" python3 - <<'PY'",
    "import hashlib, json, os, re",
    "def read_bytes(path):",
    "    try:",
    "        with open(path, 'rb') as fh:",
    "            return fh.read()",
    "    except Exception:",
    "        return b''",
    "out = read_bytes(os.environ['APPLY_OUT']).decode('utf-8', errors='replace')",
    "err = read_bytes(os.environ['APPLY_ERR']).decode('utf-8', errors='replace')",
    "manifest = read_bytes(os.environ['MANIFEST'])",
    "resources = [line.strip() for line in re.split(r'\\r?\\n', out) if line.strip()][:80]",
    "print(json.dumps({'ok': os.environ.get('APPLY_EXIT') == '0', 'exitCode': int(os.environ.get('APPLY_EXIT') or '1'), 'resourceCount': len(resources), 'resources': resources, 'manifestBytes': len(manifest), 'manifestDigest': 'sha256:' + hashlib.sha256(manifest).hexdigest(), 'stderrTail': err[-3000:], 'valuesPrinted': False}, ensure_ascii=False))",
    "PY",
    "exit \"$apply_exit\"",
  ].join("\n");
}

export function manifestObjectRef(object: Record<string, unknown>): Record<string, unknown> {
  const metadata = record(object.metadata);
  return {
    apiVersion: stringOrNull(object.apiVersion),
    kind: stringOrNull(object.kind),
    namespace: stringOrNull(metadata.namespace),
    name: stringOrNull(metadata.name),
  };
}

export function cleanupRunnersFactsNodeScript(): string {
  return String.raw`
function iso(value) {
  if (value instanceof Date) return value.toISOString();
  if (typeof value === "string" && value.length > 0) {
    const ms = Date.parse(value);
    return Number.isFinite(ms) ? new Date(ms).toISOString() : null;
  }
  return null;
}

try {
  const { Pool } = await import("pg");
  const namespace = process.env.RETENTION_NAMESPACE || process.env.AGENTRUN_RUNTIME_NAMESPACE || "";
  const runnerJobId = process.env.RETENTION_RUNNER_JOB_ID || null;
  const pool = new Pool({ connectionString: process.env.DATABASE_URL });
  const sql = [
    "SELECT",
    "  j.id, j.run_id, j.command_id, j.attempt_id, j.runner_id, j.namespace, j.job_name, j.created_at, j.updated_at,",
    "  r.registered_at AS runner_registered_at, r.heartbeat_at AS runner_heartbeat_at,",
    "  rr.status AS run_status, rr.terminal_status AS run_terminal_status, rr.failure_kind AS run_failure_kind,",
    "  rr.created_at AS run_created_at, rr.updated_at AS run_updated_at, rr.claimed_by AS run_claimed_by, rr.lease_expires_at AS run_lease_expires_at,",
    "  c.state AS command_state, c.created_at AS command_created_at, c.updated_at AS command_updated_at, c.acknowledged_at AS command_acknowledged_at,",
    "  command_facts.command_count, event_facts.event_count, event_facts.events",
    "FROM agentrun_runner_jobs j",
    "LEFT JOIN agentrun_runners r ON r.id = j.runner_id",
    "LEFT JOIN agentrun_runs rr ON rr.id = j.run_id",
    "LEFT JOIN agentrun_commands c ON c.id = j.command_id",
    "LEFT JOIN LATERAL (",
    "  SELECT COUNT(*)::int AS command_count FROM agentrun_commands cc WHERE cc.run_id = j.run_id",
    ") command_facts ON TRUE",
    "LEFT JOIN LATERAL (",
    "  SELECT COUNT(*)::int AS event_count, COALESCE(jsonb_agg(jsonb_build_object(",
    "    'seq', e.seq, 'type', e.type, 'phase', e.payload->>'phase', 'commandId', e.payload->>'commandId',",
    "    'userMessageIdPresent', jsonb_typeof(e.payload->'userMessageId') = 'string' AND length(e.payload->>'userMessageId') > 0,",
    "    'textPresent', jsonb_typeof(e.payload->'text') = 'string' AND length(e.payload->>'text') > 0,",
    "    'sourcePresent', jsonb_typeof(e.payload->'source') = 'string' AND length(e.payload->>'source') > 0,",
    "    'createdAt', e.created_at",
    "  ) ORDER BY e.seq), '[]'::jsonb) AS events",
    "  FROM (SELECT seq, type, payload, created_at FROM agentrun_events WHERE run_id = j.run_id ORDER BY seq LIMIT 501) e",
    ") event_facts ON TRUE",
    runnerJobId === null ? "WHERE j.namespace = $1" : "WHERE j.namespace = $1 AND j.id = $2",
    "ORDER BY COALESCE(r.heartbeat_at, j.updated_at, j.created_at) ASC",
  ].join("\n");
  const result = await pool.query(sql, runnerJobId === null ? [namespace] : [namespace, runnerJobId]);
  await pool.end();
  console.log(JSON.stringify({
    ok: true,
    queryMode: runnerJobId === null ? "namespace" : "runner-job-id",
    runnerJobId,
    itemCount: result.rows.length,
    items: result.rows.map((row) => ({
      id: row.id,
      runId: row.run_id,
      commandId: row.command_id,
      attemptId: row.attempt_id,
      runnerId: row.runner_id,
      namespace: row.namespace,
      jobName: row.job_name,
      runnerJobCreatedAt: iso(row.created_at),
      runnerJobUpdatedAt: iso(row.updated_at),
      runnerRegisteredAt: iso(row.runner_registered_at),
      runnerHeartbeatAt: iso(row.runner_heartbeat_at),
      runStatus: row.run_status || null,
      runTerminalStatus: row.run_terminal_status || null,
      runFailureKind: row.run_failure_kind || null,
      runCreatedAt: iso(row.run_created_at),
      runUpdatedAt: iso(row.run_updated_at),
      runClaimedBy: row.run_claimed_by || null,
      runLeaseExpiresAt: iso(row.run_lease_expires_at),
      commandState: row.command_state || null,
      commandCreatedAt: iso(row.command_created_at),
      commandUpdatedAt: iso(row.command_updated_at),
      commandAcknowledgedAt: iso(row.command_acknowledged_at),
      commandCount: Number.isInteger(row.command_count) ? row.command_count : null,
      eventCount: Number.isInteger(row.event_count) ? row.event_count : null,
      events: Array.isArray(row.events) ? row.events.slice(0, 501) : [],
      valuesPrinted: false,
    })),
    valuesPrinted: false,
  }));
} catch (error) {
  console.log(JSON.stringify({
    ok: false,
    itemCount: 0,
    items: [],
    failureKind: "manager-runner-facts-query-failed",
    message: error instanceof Error ? error.message : String(error),
    valuesPrinted: false,
  }));
}
`;
}

export function cleanupRunnersPlanNodeScript(): string {
  return String.raw`
const fs = require("node:fs");
const path = require("node:path");
const crypto = require("node:crypto");

function requiredEnv(name) {
  const value = process.env[name];
  if (typeof value !== "string" || value.length === 0) throw new Error(name + " must be supplied from the owning runner retention YAML");
  return value;
}

function requiredPositiveIntegerEnv(name) {
  const value = Number(requiredEnv(name));
  if (!Number.isInteger(value) || value <= 0) throw new Error(name + " must be a positive integer from the owning runner retention YAML");
  return value;
}

function requiredNonNegativeIntegerEnv(name) {
  const value = Number(requiredEnv(name));
  if (!Number.isInteger(value) || value < 0) throw new Error(name + " must be a non-negative integer supplied by the runner observation wrapper");
  return value;
}

function requiredBooleanEnv(name) {
  const value = requiredEnv(name);
  if (value !== "true" && value !== "false") throw new Error(name + " must be true or false");
  return value === "true";
}

const tmp = requiredEnv("TMP_DIR");
const namespace = requiredEnv("NAMESPACE");
const maxRunners = requiredPositiveIntegerEnv("MAX_RUNNERS");
const cleanupOrder = requiredEnv("CLEANUP_ORDER");
const activeHeartbeatMaxAgeMs = requiredPositiveIntegerEnv("ACTIVE_HEARTBEAT_MAX_AGE_MS");
const stalePendingMaxAgeMs = requiredPositiveIntegerEnv("STALE_PENDING_MAX_AGE_MS");
const ageBasedCleanupEnabled = requiredBooleanEnv("AGE_BASED_CLEANUP_ENABLED");
const ageBasedMaxAgeHours = process.env.AGE_BASED_MAX_AGE_HOURS ? Number(process.env.AGE_BASED_MAX_AGE_HOURS) : null;
const outputFull = requiredBooleanEnv("OUTPUT_FULL");
const factsExit = requiredNonNegativeIntegerEnv("FACTS_EXIT");
const eventsExit = requiredNonNegativeIntegerEnv("EVENTS_EXIT");
const matchLabels = JSON.parse(requiredEnv("MATCH_LABELS_JSON"));
const jobNamePrefixes = JSON.parse(requiredEnv("JOB_NAME_PREFIXES_JSON"));
const now = Date.now();
const allowedNoProgressPhases = new Set(["run-created", "command-created", "runner-job-created", "runner-dispatch-completed"]);

function readJson(name) {
  return JSON.parse(fs.readFileSync(path.join(tmp, name), "utf8"));
}

function dateMs(value) {
  const ms = Date.parse(value || "");
  return Number.isFinite(ms) ? ms : null;
}

function ageMs(value) {
  const ms = dateMs(value);
  return ms === null ? null : Math.max(0, now - ms);
}

function labelsOf(item) {
  return item?.metadata?.labels && typeof item.metadata.labels === "object" ? item.metadata.labels : {};
}

function annotationsOf(item) {
  return item?.metadata?.annotations && typeof item.metadata.annotations === "object" ? item.metadata.annotations : {};
}

function identityOf(item) {
  const annotations = annotationsOf(item);
  return {
    runId: typeof annotations["agentrun.pikastech.local/run-id"] === "string" ? annotations["agentrun.pikastech.local/run-id"] : null,
    commandId: typeof annotations["agentrun.pikastech.local/command-id"] === "string" ? annotations["agentrun.pikastech.local/command-id"] : null,
    runnerId: typeof annotations["agentrun.pikastech.local/runner-id"] === "string" ? annotations["agentrun.pikastech.local/runner-id"] : null,
  };
}

function sameIdentity(left, right) {
  return Boolean(left.runId && left.commandId && left.runnerId
    && left.runId === right.runId
    && left.commandId === right.commandId
    && left.runnerId === right.runnerId);
}

function matchesLabels(labels) {
  return Object.entries(matchLabels).every(([key, value]) => labels?.[key] === value);
}

function matchesPrefix(name) {
  return jobNamePrefixes.length === 0 || jobNamePrefixes.some((prefix) => name === prefix || name.startsWith(prefix + "-"));
}

function terminalRunStatus(value) {
  return value === "completed" || value === "failed" || value === "blocked" || value === "cancelled";
}

function jobCondition(job, type) {
  const conditions = Array.isArray(job?.status?.conditions) ? job.status.conditions : [];
  return conditions.find((entry) => entry?.type === type && entry?.status === "True") || null;
}

function isTerminalJob(job) {
  return jobCondition(job, "Complete") !== null || jobCondition(job, "Failed") !== null || Number(job?.status?.succeeded || 0) > 0 || Number(job?.status?.failed || 0) > 0;
}

function isTerminalPod(pod) {
  const phase = pod?.status?.phase || "";
  return phase === "Succeeded" || phase === "Failed";
}

function bounded(value, max = 320) {
  if (typeof value !== "string" || value.length === 0) return null;
  const oneLine = value.replace(/\s+/g, " ").trim();
  return oneLine.length <= max ? oneLine : oneLine.slice(0, max);
}

function jobNameForPod(pod) {
  const labels = labelsOf(pod);
  if (typeof labels["job-name"] === "string") return labels["job-name"];
  const owner = (Array.isArray(pod?.metadata?.ownerReferences) ? pod.metadata.ownerReferences : []).find((entry) => entry?.kind === "Job" && typeof entry?.name === "string");
  return owner?.name || null;
}

function preferredLastActiveAt(fact, job) {
  return fact?.runnerHeartbeatAt
    || fact?.runUpdatedAt
    || fact?.runnerJobUpdatedAt
    || job?.status?.completionTime
    || job?.status?.startTime
    || job?.metadata?.creationTimestamp
    || null;
}

function conditionReady(pod) {
  return (Array.isArray(pod?.status?.conditions) ? pod.status.conditions : [])
    .some((entry) => entry?.type === "Ready" && entry?.status === "True");
}

function stateKind(state) {
  if (state?.running) return "running";
  if (state?.terminated) return "terminated";
  if (state?.waiting) return "waiting";
  return "unknown";
}

function statusStarted(status) {
  const lastState = status?.lastState || {};
  const kind = stateKind(status?.state || {});
  return status?.started === true
    || status?.ready === true
    || Number(status?.restartCount || 0) > 0
    || kind === "running"
    || kind === "terminated"
    || Boolean(lastState.running)
    || Boolean(lastState.terminated);
}

function waitingFromStatus(status, source) {
  const waiting = status?.state?.waiting;
  if (!waiting || typeof waiting.reason !== "string" || waiting.reason.length === 0) return [];
  return [{
    source,
    name: typeof status.name === "string" ? status.name : null,
    reason: waiting.reason,
    message: bounded(waiting.message),
    deterministic: waiting.reason !== "ContainerCreating" && waiting.reason !== "PodInitializing",
    valuesPrinted: false,
  }];
}

function warningFromEvent(event) {
  if (event?.type !== "Warning" || typeof event?.reason !== "string" || event.reason.length === 0) return null;
  return {
    source: "event",
    name: typeof event?.metadata?.name === "string" ? event.metadata.name : null,
    reason: event.reason,
    message: bounded(event.message),
    deterministic: true,
    count: Number.isFinite(event.count) ? event.count : null,
    lastObservedAt: bounded(event?.series?.lastObservedTime || event?.eventTime || event?.lastTimestamp || event?.firstTimestamp),
    valuesPrinted: false,
  };
}

function podObservation(pod, warningEvents) {
  const statuses = Array.isArray(pod?.status?.containerStatuses) ? pod.status.containerStatuses : [];
  const initStatuses = Array.isArray(pod?.status?.initContainerStatuses) ? pod.status.initContainerStatuses : [];
  const runner = statuses.find((status) => status?.name === "runner") || null;
  const conditions = Array.isArray(pod?.status?.conditions) ? pod.status.conditions : [];
  const warnings = warningEvents.map(warningFromEvent).filter(Boolean);
  const waiting = [
    ...statuses.flatMap((status) => waitingFromStatus(status, "container")),
    ...initStatuses.flatMap((status) => waitingFromStatus(status, "init-container")),
    ...conditions.flatMap((condition) => condition?.status === "False" && typeof condition?.reason === "string" && condition.reason.length > 0
      ? [{ source: "condition", name: condition.type || null, reason: condition.reason, message: bounded(condition.message), deterministic: condition.reason === "Unschedulable", valuesPrinted: false }]
      : []),
    ...warnings,
  ];
  const createdAt = pod?.metadata?.creationTimestamp || null;
  const runnerLastState = runner?.lastState || {};
  const allStatuses = [...statuses, ...initStatuses];
  return {
    name: pod?.metadata?.name || null,
    uid: pod?.metadata?.uid || null,
    resourceVersion: pod?.metadata?.resourceVersion || null,
    identity: identityOf(pod),
    phase: pod?.status?.phase || null,
    createdAt,
    ageMs: ageMs(createdAt),
    podStartTime: pod?.status?.startTime || null,
    ready: conditionReady(pod),
    runnerContainerState: stateKind(runner?.state || {}),
    runnerContainerLastState: runnerLastState.running ? "running" : runnerLastState.terminated ? "terminated" : "none",
    runnerContainerStarted: runner ? statusStarted(runner) : false,
    anyContainerStarted: allStatuses.some(statusStarted),
    runnerContainerReady: runner?.ready === true,
    runnerContainerRestartCount: Number(runner?.restartCount || 0),
    waiting: waiting.slice(0, 8),
    waitingOmittedCount: Math.max(0, waiting.length - 8),
    waitingEvidenceSources: [...new Set(waiting.map((entry) => entry.source))],
    eventFactsAvailable: eventsExit === 0,
    factsAvailable: typeof pod?.status?.phase === "string" && dateMs(createdAt) !== null,
    valuesPrinted: false,
  };
}

function businessFactsComplete(fact) {
  return Boolean(fact
    && typeof fact.runId === "string"
    && typeof fact.commandId === "string"
    && typeof fact.runnerId === "string"
    && typeof fact.runStatus === "string"
    && typeof fact.commandState === "string"
    && Number.isInteger(fact.commandCount)
    && Number.isInteger(fact.eventCount)
    && Array.isArray(fact.events)
    && fact.eventCount === fact.events.length
    && fact.eventCount > 0
    && fact.eventCount < 500);
}

function hasNoBusinessProgress(fact) {
  if (!businessFactsComplete(fact)) return false;
  if (fact.runStatus !== "pending" || fact.runClaimedBy !== null || fact.runLeaseExpiresAt !== null) return false;
  if (fact.commandState !== "pending" || fact.commandAcknowledgedAt !== null) return false;
  const runCreated = dateMs(fact.runCreatedAt);
  const runUpdated = dateMs(fact.runUpdatedAt);
  const commandCreated = dateMs(fact.commandCreatedAt);
  const commandUpdated = dateMs(fact.commandUpdatedAt);
  if (runCreated === null || runCreated !== runUpdated || commandCreated === null || commandCreated !== commandUpdated) return false;
  if (now - runCreated < stalePendingMaxAgeMs || now - commandCreated < stalePendingMaxAgeMs) return false;
  if (fact.commandCount !== 1) return false;
  const userMessages = fact.events.filter((event) => event?.type === "user_message");
  if (userMessages.length > 1) return false;
  const phases = fact.events.flatMap((event) => {
    if (event?.type === "user_message") {
      const valid = event.commandId === fact.commandId
        && event.userMessageIdPresent === true
        && event.textPresent === true
        && event.sourcePresent === true;
      return valid ? [] : [null];
    }
    return event?.type === "backend_status" && typeof event?.phase === "string" && allowedNoProgressPhases.has(event.phase)
      ? [event.phase]
      : [null];
  });
  if (phases.includes(null)) return false;
  if (!phases.includes("run-created") || !phases.includes("command-created") || !phases.includes("runner-job-created")) return false;
  return fact.events.every((event) => event?.commandId === null || event?.commandId === undefined || event.commandId === fact.commandId);
}

function safetyFingerprint(value) {
  return "sha256:" + crypto.createHash("sha256").update(JSON.stringify(value)).digest("hex");
}

function sortOldestLastActive(left, right) {
  const leftMs = dateMs(left.lastActiveAt) ?? dateMs(left.createdAt) ?? Number.MAX_SAFE_INTEGER;
  const rightMs = dateMs(right.lastActiveAt) ?? dateMs(right.createdAt) ?? Number.MAX_SAFE_INTEGER;
  if (leftMs !== rightMs) return leftMs - rightMs;
  return String(left.name).localeCompare(String(right.name));
}

function countNonTerminalPods(pods) {
  return pods.filter((pod) => !isTerminalPod(pod)).length;
}

const jobs = readJson("jobs.json");
const pods = readJson("pods.json");
const events = readJson("events.json");
const facts = readJson("runner-facts.json");
const factItems = Array.isArray(facts.items) ? facts.items : [];
const factByJob = new Map(factItems.map((item) => [item.jobName, item]));
const allPods = Array.isArray(pods.items) ? pods.items : [];
const allEvents = Array.isArray(events.items) ? events.items : [];
const podsByJob = new Map();
const warningsByPod = new Map();
for (const event of allEvents) {
  const podName = event?.involvedObject?.kind === "Pod" && typeof event?.involvedObject?.name === "string"
    ? event.involvedObject.name
    : null;
  if (!podName) continue;
  const entry = warningsByPod.get(podName) || [];
  entry.push(event);
  warningsByPod.set(podName, entry);
}
for (const pod of allPods) {
  const jobName = jobNameForPod(pod);
  if (!jobName) continue;
  const entry = podsByJob.get(jobName) || [];
  entry.push(pod);
  podsByJob.set(jobName, entry);
}

const matchedJobs = (Array.isArray(jobs.items) ? jobs.items : [])
  .map((job) => {
    const name = job?.metadata?.name || "";
    return { job, name, labels: labelsOf(job), annotations: annotationsOf(job) };
  })
  .filter((item) => item.name && matchesLabels(item.labels) && matchesPrefix(item.name));

const runnerJobs = matchedJobs.map(({ job, name, labels, annotations }) => {
  const fact = factByJob.get(name) || null;
  const jobPods = podsByJob.get(name) || [];
  const podObservations = jobPods.map((pod) => podObservation(pod, warningsByPod.get(pod?.metadata?.name || "") || []));
  const nonTerminalPods = jobPods.filter((pod) => !isTerminalPod(pod));
  const terminatingPods = nonTerminalPods.filter((pod) => typeof pod?.metadata?.deletionTimestamp === "string");
  const terminal = isTerminalJob(job) || terminalRunStatus(fact?.runStatus) || terminalRunStatus(fact?.runTerminalStatus);
  const heartbeatAgeMs = ageMs(fact?.runnerHeartbeatAt);
  const heartbeatFresh = heartbeatAgeMs !== null && heartbeatAgeMs <= activeHeartbeatMaxAgeMs;
  const jobIdentity = identityOf(job);
  const factIdentity = { runId: fact?.runId || null, commandId: fact?.commandId || null, runnerId: fact?.runnerId || null };
  const identityComplete = sameIdentity(jobIdentity, factIdentity)
    && podObservations.length > 0
    && podObservations.every((pod) => sameIdentity(jobIdentity, pod.identity));
  const managerFactsComplete = factsExit === 0 && facts.ok === true && businessFactsComplete(fact);
  const podFactsComplete = podObservations.length > 0 && podObservations.every((pod) => pod.factsAvailable && pod.eventFactsAvailable);
  const activePodReason = podObservations.some((pod) => pod.phase === "Running"
    || pod.ready
    || pod.runnerContainerReady
    || pod.runnerContainerStarted
    || pod.anyContainerStarted
    || pod.runnerContainerState === "running"
    || pod.runnerContainerState === "terminated")
    ? "runner-pod-started-running-or-ready"
    : null;
  const lastActiveAt = preferredLastActiveAt(fact, job);
  const allPending = podObservations.length > 0 && podObservations.every((pod) => pod.phase === "Pending");
  const allStale = podObservations.length > 0 && podObservations.every((pod) => pod.ageMs !== null && pod.ageMs >= stalePendingMaxAgeMs);
  const deterministicWaiting = podObservations.length > 0 && podObservations.every((pod) => pod.waiting.length > 0 && pod.waiting.some((item) => item.deterministic === true));
  let protectedReason = null;
  let candidateKind = null;
  if (!facts.ok || factsExit !== 0) protectedReason = "manager-facts-unavailable";
  else if (!identityComplete) protectedReason = "identity-facts-unavailable-or-mismatch";
  else if (!managerFactsComplete) protectedReason = "business-progress-facts-unavailable";
  else if (!podFactsComplete) protectedReason = eventsExit === 0 ? "pod-facts-unavailable" : "pod-warning-event-facts-unavailable";
  else if (fact?.runClaimedBy) protectedReason = "run-claimed";
  else if (fact?.runnerRegisteredAt || fact?.runnerHeartbeatAt) protectedReason = heartbeatFresh ? "runner-heartbeat-fresh" : "runner-registration-or-heartbeat-observed";
  else if (activePodReason) protectedReason = activePodReason;
  else if (terminal) candidateKind = "terminal";
  else if (!allPending) protectedReason = "runner-pod-not-pending";
  else if (!allStale) protectedReason = "stale-pending-window-active";
  else if (!deterministicWaiting) protectedReason = "pod-deterministic-waiting-reason-unavailable";
  else if (!hasNoBusinessProgress(fact)) protectedReason = "business-progress-observed-or-unverifiable";
  else candidateKind = "stale-pending";
  const item = {
    name,
    namespace,
    resourceUid: job?.metadata?.uid || null,
    resourceVersion: job?.metadata?.resourceVersion || null,
    createdAt: job?.metadata?.creationTimestamp || null,
    lastActiveAt,
    lastActiveAgeMs: ageMs(lastActiveAt),
    lastActiveSource: fact?.runnerHeartbeatAt ? "runner-heartbeat" : fact?.runUpdatedAt ? "run-updated" : fact?.runnerJobUpdatedAt ? "runner-job-updated" : job?.status?.completionTime ? "job-completion" : job?.status?.startTime ? "job-start" : "job-created",
    runId: annotations["agentrun.pikastech.local/run-id"] || fact?.runId || null,
    commandId: annotations["agentrun.pikastech.local/command-id"] || fact?.commandId || null,
    runnerJobId: fact?.id || null,
    runnerId: fact?.runnerId || null,
    runStatus: fact?.runStatus || null,
    runTerminalStatus: fact?.runTerminalStatus || null,
    runClaimedBy: fact?.runClaimedBy || null,
    runLeaseExpiresAt: fact?.runLeaseExpiresAt || null,
    commandState: fact?.commandState || null,
    commandAcknowledgedAt: fact?.commandAcknowledgedAt || null,
    commandCount: fact?.commandCount ?? null,
    eventCount: fact?.eventCount ?? null,
    eventPhases: Array.isArray(fact?.events) ? fact.events.slice(0, 12).map((event) => event?.phase || event?.type || null) : [],
    heartbeatAt: fact?.runnerHeartbeatAt || null,
    heartbeatAgeMs,
    heartbeatFresh,
    jobStatus: {
      active: Number(job?.status?.active || 0),
      succeeded: Number(job?.status?.succeeded || 0),
      failed: Number(job?.status?.failed || 0),
      ready: Number(job?.status?.ready || 0),
      terminating: Number(job?.status?.terminating || 0),
    },
    podCount: jobPods.length,
    nonTerminalPodCount: nonTerminalPods.length,
    terminatingPodCount: terminatingPods.length,
    podObservations: podObservations.slice(0, 4),
    podObservationOmittedCount: Math.max(0, podObservations.length - 4),
    podPhases: podObservations.map((pod) => pod.phase),
    waitingReasons: [...new Set(podObservations.flatMap((pod) => pod.waiting.map((entry) => entry.reason)))].slice(0, 8),
    identityComplete,
    managerFactsComplete,
    podFactsComplete,
    managerRetentionCandidate: candidateKind !== null,
    safeCleanupCandidate: false,
    protectedActive: candidateKind === null,
    protectedReason,
    candidateKind,
    classification: candidateKind === "stale-pending" ? "stale-pending-no-business-progress" : candidateKind === "terminal" ? "terminal-complete-facts" : protectedReason,
    valuesPrinted: false,
  };
  return {
    ...item,
    safetyFingerprint: safetyFingerprint({
      resourceUid: item.resourceUid,
      resourceVersion: item.resourceVersion,
      runId: item.runId,
      commandId: item.commandId,
      runnerId: item.runnerId,
      runStatus: item.runStatus,
      runTerminalStatus: item.runTerminalStatus,
      runClaimedBy: item.runClaimedBy,
      runLeaseExpiresAt: item.runLeaseExpiresAt,
      commandState: item.commandState,
      commandAcknowledgedAt: item.commandAcknowledgedAt,
      commandCount: item.commandCount,
      eventCount: item.eventCount,
      eventPhases: item.eventPhases,
      heartbeatAt: item.heartbeatAt,
      podObservations: item.podObservations,
      candidateKind: item.candidateKind,
      classification: item.classification,
    }),
  };
});

const runnerJobCount = runnerJobs.length;
const managerRetentionCandidates = runnerJobs.filter((item) => item.managerRetentionCandidate).sort(sortOldestLastActive);
const stalePendingObservations = managerRetentionCandidates.filter((item) => item.candidateKind === "stale-pending");
const terminalObservations = managerRetentionCandidates.filter((item) => item.candidateKind === "terminal");
const protectedRunners = runnerJobs.filter((item) => item.protectedActive).sort(sortOldestLastActive);
const overLimitCount = Math.max(0, runnerJobCount - maxRunners);
const selected = [];
const matchedPodNames = new Set(runnerJobs.map((item) => item.name));
const matchedPods = allPods.filter((pod) => {
  const jobName = jobNameForPod(pod);
  return jobName !== null && matchedPodNames.has(jobName);
});
const remainingAfterSelection = runnerJobCount - selected.length;
const observationOk = factsExit === 0 && facts.ok === true && eventsExit === 0;
const managerRetentionCapabilityValidated = false;
const atCapacity = runnerJobCount === maxRunners;
const overCapacity = runnerJobCount > maxRunners;
const capacityReady = runnerJobCount < maxRunners || managerRetentionCapabilityValidated;
const capacityStatus = overCapacity
  ? "over-capacity-manager-retention-required"
  : atCapacity
    ? "at-capacity-manager-retention-required"
    : "available";
const availableSlots = Math.max(0, maxRunners - runnerJobCount);
const capacityBlockerCode = overCapacity
  ? "runner-capacity-over-limit-manager-retention-not-validated"
  : atCapacity
    ? "runner-capacity-at-limit-manager-retention-not-validated"
    : null;
const capacityBlocker = capacityBlockerCode === null
  ? null
  : {
      type: "runner-capacity",
      code: capacityBlockerCode,
      status: capacityStatus,
      current: runnerJobCount,
      limit: maxRunners,
      availableSlots,
      managerRetentionCapabilityValidated,
      valuesPrinted: false,
    };
const degradedReason = !observationOk
  ? "runner-retention-observation-incomplete"
  : overCapacity
    ? "runner-capacity-over-limit-manager-retention-not-validated"
    : atCapacity
      ? "runner-capacity-at-limit-manager-retention-not-validated"
      : null;
const ok = observationOk && capacityReady;
const visible = (items) => outputFull ? items : items.slice(0, 8);
console.log(JSON.stringify({
  ok,
  degradedReason,
  planKind: "agentrun-runtime-runner-retention",
  generatedAt: new Date(now).toISOString(),
  namespace,
  criteria: {
    maxRunners,
    cleanupOrder,
    activeHeartbeatMaxAgeMs,
    stalePendingMaxAgeMs,
    selectionBoundary: "read-only-observation",
    incomingRunnerAuthority: "agentrun-pre-create-retention",
    unideskDeletionAuthority: "none",
    managerTrackingIssue: "https://github.com/pikasTech/agentrun/issues/280",
    managerTrackingPullRequest: "https://github.com/pikasTech/agentrun/pull/281",
    selectors: { matchLabels, jobNamePrefixes },
    ageBasedCleanup: { enabled: ageBasedCleanupEnabled, maxAgeHours: ageBasedMaxAgeHours },
  },
  managerFacts: {
    ok: facts.ok === true,
    factCount: factItems.length,
    factsExit,
    failureKind: facts.failureKind || null,
    message: facts.ok === true ? null : facts.message || null,
    valuesPrinted: false,
  },
  kubernetesFacts: {
    podListAvailable: true,
    warningEventsAvailable: eventsExit === 0,
    eventsExit,
    valuesPrinted: false,
  },
  observation: {
    ok: observationOk,
    managerFactsAvailable: factsExit === 0 && facts.ok === true,
    jobListAvailable: true,
    podListAvailable: true,
    warningEventsAvailable: eventsExit === 0,
    valuesPrinted: false,
  },
  capacity: {
    ok: capacityReady,
    status: capacityStatus,
    runnerJobCount,
    maxRunners,
    availableSlots,
    atCapacity,
    overCapacity,
    nextRunnerRequiresManagerRetention: runnerJobCount >= maxRunners,
    managerRetentionCapabilityValidated,
    blocker: capacityBlocker,
    valuesPrinted: false,
  },
  runnerJobCount,
  nonTerminalRunnerPodCount: countNonTerminalPods(matchedPods),
  overLimitCount,
  inactiveCandidateCount: managerRetentionCandidates.length,
  managerRetentionCandidateCount: managerRetentionCandidates.length,
  stalePendingObservationCount: stalePendingObservations.length,
  terminalObservationCount: terminalObservations.length,
  protectedActiveRunnerCount: protectedRunners.length,
  activeRunRisk: remainingAfterSelection > maxRunners || protectedRunners.some((item) => item.runClaimedBy !== null || item.heartbeatAt !== null),
  nextRunnerRequiresManagerRetention: runnerJobCount >= maxRunners,
  managerRetentionCapabilityValidated,
  remainingRunnerJobCountAfterSelection: remainingAfterSelection,
  remainingAfterSelection,
  runnerJobs: visible(runnerJobs.sort(sortOldestLastActive)),
  runnerJobOmittedCount: outputFull ? 0 : Math.max(0, runnerJobs.length - 8),
  candidates: visible(managerRetentionCandidates),
  candidateOmittedCount: outputFull ? 0 : Math.max(0, managerRetentionCandidates.length - 8),
  stalePendingObservations: visible(stalePendingObservations),
  stalePendingObservationOmittedCount: outputFull ? 0 : Math.max(0, stalePendingObservations.length - 8),
  terminalObservations: visible(terminalObservations),
  terminalObservationOmittedCount: outputFull ? 0 : Math.max(0, terminalObservations.length - 8),
  protectedRunners: visible(protectedRunners),
  protectedRunnerOmittedCount: outputFull ? 0 : Math.max(0, protectedRunners.length - 8),
  selected,
  selectedRunnerJobs: selected.map((item) => item.name),
  selectedRunnerJobCount: selected.length,
  selectedRunnerJobOmittedCount: 0,
  deletionAuthority: {
    owner: "agentrun-manager-pre-create-retention",
    unideskConfirmSupported: false,
    trackingIssue: "https://github.com/pikasTech/agentrun/issues/280",
    trackingPullRequest: "https://github.com/pikasTech/agentrun/pull/281",
    valuesPrinted: false,
  },
  valuesPrinted: false,
}));
`;
}
