// SPEC: PJ2026-0103 HarnessRL draft-2026-06-25-p0-web-caserun-e2e.
// Responsibility: subject worktree isolation and Keil post-validation collection.

import { createHash, randomUUID } from "node:crypto";
import { readFile } from "node:fs/promises";
import path from "node:path";
import { readHwpodSpec } from "./hwpod-harness-lib.ts";
import {
  clean,
  cliError,
  clipText,
  compactObject,
  firstNumberOption,
  numberOption,
  parseJsonMaybe,
  text,
} from "./hwlab-caserun-evidence.ts";
import type { CaseContext, PreparedCaseRun, PreparedSubjectRun, SourceRootSnapshot } from "./hwlab-caserun-shared.ts";
const DEFAULT_POLL_INTERVAL_MS = 1000;
const DEFAULT_JOB_TIMEOUT_MS = 50000;
const MAX_JOB_TIMEOUT_MS = 300000;

export function slug(value: string) {
  return value.toLowerCase().replace(/[^a-z0-9]+/gu, "-").replace(/^-|-$/gu, "") || "case";
}
export async function prepareSubjectWorktree(context: CaseContext, input: { caseId: string; runId: string; runDir: string; apiUrl: string; definition: Record<string, unknown>; sourceSpecPath: string }, collectSnapshot: (context: CaseContext, run: PreparedCaseRun, label: string) => Promise<SourceRootSnapshot>) {
  const subject = subjectFromDefinition(input.definition);
  const worktreePath = subjectWorktreePath(subject.repoLocalPath, input.runId);
  const relativeWorktreePath = subjectRelativeWorktreePath(subject.repoLocalPath, worktreePath);
  const snapshotRun = caseRunForSubjectSnapshot(input, subject, worktreePath);
  const sourceRootBaseline = await collectSnapshot(context, snapshotRun, "before-prepare");
  const setup = await requestSubjectWorktree(context, input, subject, worktreePath, relativeWorktreePath);
  const sourceRootAfterPrepare = await collectSnapshot(context, snapshotRun, "after-prepare");
  return { ...subject, worktreePath, workspacePath: worktreePath, setup, sourceRootBaseline, sourceRootAfterPrepare };
}

export function caseRunForSubjectSnapshot(input: { caseId: string; runId: string; runDir: string; apiUrl: string; definition: Record<string, unknown>; sourceSpecPath: string }, subject: { repoLocalPath: string; commitId: string; subdir: string }, worktreePath: string): PreparedCaseRun {
  return {
    caseId: input.caseId,
    runId: input.runId,
    runDir: input.runDir,
    caseRepo: "",
    caseDir: "",
    caseFile: "",
    sourceSpecPath: input.sourceSpecPath,
    specPath: input.sourceSpecPath,
    apiUrl: input.apiUrl,
    compileOnly: true,
    createdAt: "",
    updatedAt: "",
    definition: input.definition,
    subject: { ...subject, worktreePath, workspacePath: worktreePath, setup: {} }
  };
}

export function subjectFromDefinition(definition: Record<string, unknown>) {
  const subject = definition.subject;
  if (!subject || typeof subject !== "object" || Array.isArray(subject)) {
    throw cliError("subject_required", "case.json subject is required", { required: ["subject.repoLocalPath", "subject.commitId"] });
  }
  const source = subject as Record<string, unknown>;
  const repoLocalPath = text(source.repoLocalPath);
  if (!repoLocalPath) throw cliError("subject_repo_local_path_required", "case.json subject.repoLocalPath is required and must point to a local subject repo checkout on the bound HWPOD node", { field: "subject.repoLocalPath" });
  const commitId = text(source.commitId).toLowerCase();
  if (!commitId) throw cliError("subject_commit_id_required", "case.json subject.commitId is required and must be a fixed subject repo commit id", { field: "subject.commitId" });
  if (!/^[0-9a-f]{40}$/iu.test(commitId)) {
    throw cliError("invalid_subject_commit_id", "subject.commitId must be a full 40-character git commit id", { commitId });
  }
  return { repoLocalPath, commitId, subdir: text(source.subdir) };
}

export async function requestSubjectWorktree(context: CaseContext, input: { caseId: string; runId: string; runDir: string; apiUrl: string; sourceSpecPath: string }, subject: { repoLocalPath: string; commitId: string; subdir: string }, worktreePath: string, relativeWorktreePath: string) {
  const document = await readHwpodSpec(input.sourceSpecPath);
  const hwpodId = text(document.metadata.name) || text(document.metadata.uid) || input.caseId;
  const sidecarOp = keilSidecarCopyOp(document, subject, hwpodId, relativeWorktreePath);
  const ops = [{
    opId: "op_01_subject_commit",
    op: "cmd.run",
    args: {
      hwpodId,
      workspacePath: subject.repoLocalPath,
      command: "git",
      argv: ["cat-file", "-e", subject.commitId]
    }
  }, {
    opId: "op_02_subject_worktree_add",
    op: "cmd.run",
    args: {
      hwpodId,
      workspacePath: subject.repoLocalPath,
      command: "git",
      argv: ["worktree", "add", "--detach", "--force", relativeWorktreePath, subject.commitId]
    }
  }, {
    opId: "op_03_subject_worktree_head",
    op: "cmd.run",
    args: {
      hwpodId,
      workspacePath: subject.repoLocalPath,
      command: "git",
      argv: ["-C", relativeWorktreePath, "rev-parse", "HEAD"]
    }
  }, ...(sidecarOp ? [sidecarOp] : [])];
  const plan = {
    contractVersion: "hwpod-node-ops-v1",
    planId: stablePlanId(context, "subject-prepare"),
    hwpodId,
    nodeId: document.spec.nodeBinding.nodeId,
    intent: "cmd.run",
    source: { compiler: "hwlab-cli.case", specPath: input.sourceSpecPath, specAuthority: "case-registry" },
    ops
  };
  const response = await context.fetchImpl(`${input.apiUrl}/v1/hwpod-node-ops`, {
    method: "POST",
    headers: { "content-type": "application/json" },
    body: JSON.stringify(plan)
  });
  const textBody = await response.text();
  const body = parseJsonMaybe(textBody) ?? { raw: textBody };
  const status = subjectWorktreeStatus(response.status, body, { commitId: subject.commitId, worktreePath });
  if (!status.ok) {
    const failure = subjectWorktreeFailureDetails(body, plan, status);
    throw cliError("subject_worktree_prepare_failed", "failed to prepare isolated subject worktree on the HWPOD node", clean({ httpStatus: response.status, nodeId: document.spec.nodeBinding.nodeId, repoLocalPath: subject.repoLocalPath, commitId: subject.commitId, worktreePath, exitCode: status.exitCode, partialWrite: failure.partialWrite, dispatched: failure.dispatched, operation: failure.operation, errorSummary: failure.errorSummary, blocker: failure.blocker }));
  }
  return clean({ httpStatus: response.status, planId: plan.planId, nodeId: document.spec.nodeBinding.nodeId, exitCode: status.exitCode, operation: subjectWorktreeOperationIdentity(body, plan), stdout: clipText(status.stdout), stderr: clipText(status.stderr), keilSidecars: keilSidecarSummary(status.stdout) });
}

export function subjectWorktreeStatus(httpStatus: number, body: any, expected: { commitId: string; worktreePath: string }) {
  const results = Array.isArray(body?.body?.results) ? body.body.results : Array.isArray(body?.results) ? body.results : [];
  const outputs = results.map((result: any) => result.output ?? result);
  const exitCodes = outputs.map((output: any) => numberOption(output.exitCode));
  const stdout = outputs.map((output: any) => text(output.stdout)).filter(Boolean).join("\n");
  const stderr = outputs.map((output: any) => text(output.stderr)).filter(Boolean).join("\n");
  const actualHead = text(outputs[2]?.stdout).split(/\r?\n/u)[0]?.toLowerCase() ?? "";
  const commandOk = httpStatus >= 200 && httpStatus < 300 && outputs.length >= 3 && exitCodes.every((exitCode) => exitCode === 0);
  const ok = commandOk && actualHead === expected.commitId;
  return { ok, exitCode: exitCodes.find((exitCode) => exitCode !== 0) ?? exitCodes[exitCodes.length - 1], stdout, stderr, actualHead, actualWorktreePath: expected.worktreePath };
}

export function subjectWorktreeFailureDetails(body: any, plan: any, status: { stderr?: string }) {
  const results = subjectWorktreeResults(body);
  const mutatingOpIds = new Set(["op_02_subject_worktree_add", "op_04_keil_sidecars"]);
  const dispatched = subjectWorktreeBooleanFact(body, results, "dispatched") ?? subjectWorktreeDispatchedFromMode(body, results);
  const explicitPartialWrite = subjectWorktreeBooleanFact(body, results, "partialWrite");
  const successfulMutatingResult = results.some((result: any) => mutatingOpIds.has(text(result?.opId)) && result?.ok === true);
  const partialWrite = explicitPartialWrite ?? (dispatched === false ? false : successfulMutatingResult ? true : null);
  const sourceBlocker = body?.blocker ?? results.find((result: any) => result?.blocker)?.blocker ?? body?.error ?? null;
  const errorSummary = redactSubjectWorktreeError(text(sourceBlocker?.summary ?? sourceBlocker?.message ?? status.stderr) || "HWPOD subject worktree operation failed without an error summary");
  const blocker = clean({
    code: text(sourceBlocker?.code) || "subject_worktree_prepare_failed",
    layer: text(sourceBlocker?.layer) || "hwpod-node",
    retryable: typeof sourceBlocker?.retryable === "boolean" ? sourceBlocker.retryable : true,
    summary: errorSummary,
    details: clean({ nodeId: plan.nodeId, planId: plan.planId, hwpodId: plan.hwpodId, partialWrite, dispatched })
  });
  return { partialWrite, dispatched, errorSummary, blocker, operation: subjectWorktreeOperationIdentity(body, plan) };
}

function subjectWorktreeBooleanFact(body: any, results: any[], field: "dispatched" | "partialWrite") {
  const containers = [body, body?.body];
  const values = [
    ...containers.flatMap((container) => [container?.[field], container?.blocker?.details?.[field], container?.diagnostic?.details?.[field]]),
    ...results.flatMap((result: any) => [result?.[field], result?.blocker?.details?.[field], result?.blocker?.diagnostic?.details?.[field]])
  ];
  return values.find((value) => typeof value === "boolean") ?? null;
}

function subjectWorktreeDispatchedFromMode(body: any, results: any[]) {
  const containers = [body, body?.body];
  const modes = [
    ...containers.flatMap((container) => [container?.dispatchMode, container?.blocker?.details?.dispatchMode, container?.diagnostic?.details?.dispatchMode]),
    ...results.flatMap((result: any) => [result?.dispatchMode, result?.blocker?.details?.dispatchMode, result?.blocker?.diagnostic?.details?.dispatchMode])
  ].map(text).filter(Boolean);
  const dispatchMode = modes[0];
  if (dispatchMode === "none") return false;
  if (["websocket", "direct-url", "handler", "handler-exception"].includes(dispatchMode)) return true;
  return null;
}

function subjectWorktreeOperationIdentity(body: any, plan: any) {
  const results = subjectWorktreeResults(body);
  const resultById = new Map(results.map((result: any) => [text(result?.opId), result]));
  return clean({
    contractVersion: text(body?.contractVersion) || plan.contractVersion,
    planId: text(body?.planId) || plan.planId,
    hwpodId: text(body?.hwpodId) || plan.hwpodId,
    nodeId: text(body?.nodeId) || plan.nodeId,
    intent: plan.intent,
    ops: plan.ops.map((op: any) => {
      const result: any = resultById.get(text(op.opId));
      return clean({ opId: op.opId, op: op.op, status: text(result?.status) || null, ok: typeof result?.ok === "boolean" ? result.ok : null, blockerCode: text(result?.blocker?.code) || null });
    })
  });
}

function subjectWorktreeResults(body: any) {
  return Array.isArray(body?.body?.results) ? body.body.results : Array.isArray(body?.results) ? body.results : [];
}

function redactSubjectWorktreeError(value: string) {
  return clipText(value)
    .replace(/\b(?:hwl_live|sk|ghp)_[A-Za-z0-9_-]+\b/gu, "[REDACTED]")
    .replace(/\b(?:authorization|api[_-]?key|token|secret|password)\s*[:=]\s*\S+/giu, "$1=[REDACTED]");
}

export function keilSidecarCopyOp(document: any, subject: { repoLocalPath: string }, hwpodId: string, relativeWorktreePath: string) {
  const project = subjectRelativeKeilProject(document, subject.repoLocalPath);
  if (!project) return null;
  const sourceBase = stripUvprojx(project).replace(/\//gu, "\\");
  const destinationBase = windowsJoin(relativeWorktreePath, sourceBase);
  return {
    opId: "op_04_keil_sidecars",
    op: "cmd.run",
    args: {
      hwpodId,
      workspacePath: subject.repoLocalPath,
      command: "node",
      argv: ["-e", keilSidecarCopyNodeScript(), JSON.stringify({ sourceBase, destinationBase })],
      commandBinding: {
        kind: "keil-mdk-sidecar-copy",
        source: "hwlab-cli.case.prepare",
        sourceBase,
        destinationBase,
        sidecars: [".uvoptx", ".uvopt"]
      }
    }
  };
}

export function subjectRelativeKeilProject(document: any, repoLocalPath: string) {
  const workspace = objectRecord(document.spec?.workspace);
  const projectWorkspace = objectRecord(document.spec?.projectWorkspace);
  const rawProject = text(workspace.keilProject ?? workspace.projectPath ?? workspace.project ?? projectWorkspace.projectPath);
  if (!rawProject || !/\.uvprojx$/iu.test(rawProject)) return "";
  if (!isAbsolutePathLike(rawProject)) return rawProject.replace(/^[\\/]+/u, "");
  const repo = repoLocalPath.replace(/[\\/]+$/u, "");
  const normalizedRepo = normalizeLocalPath(repo);
  const normalizedProject = normalizeLocalPath(rawProject);
  if (!normalizedProject.startsWith(`${normalizedRepo}/`)) return "";
  return rawProject.slice(repo.length).replace(/^[\\/]+/u, "");
}

export function stripUvprojx(value: string) {
  return value.replace(/\.uvprojx$/iu, "");
}

export function windowsJoin(parent: string, child: string) {
  const left = parent.replace(/[\\/]+$/u, "").replace(/\//gu, "\\");
  const right = child.replace(/^[\\/]+/u, "").replace(/\//gu, "\\");
  return `${left}\\${right}`;
}

export function keilSidecarCopyNodeScript() {
  return [
    "const fs=require('fs');",
    "const path=require('path');",
    "const input=JSON.parse(process.argv[1]||'{}');",
    "const sidecars=['.uvoptx','.uvopt'];",
    "const copiedFiles=[];",
    "const missing=[];",
    "for(const ext of sidecars){",
    "const source=String(input.sourceBase||'')+ext;",
    "const destination=String(input.destinationBase||'')+ext;",
    "if(fs.existsSync(source)){",
    "const dir=destination.includes('\\\\')?path.win32.dirname(destination):path.dirname(destination);",
    "fs.mkdirSync(dir,{recursive:true});",
    "fs.copyFileSync(source,destination);",
    "copiedFiles.push({ext,source,destination});",
    "}else{missing.push(source);}",
    "}",
    "console.log('caseRunKeilSidecars '+JSON.stringify({copied:copiedFiles.length,copiedFiles,missing,sourceBase:input.sourceBase,destinationBase:input.destinationBase}));"
  ].join("");
}

export function keilSidecarSummary(stdout: string) {
  const line = stdout.split(/\r?\n/u).find((item) => item.includes("caseRunKeilSidecars"));
  if (!line) return null;
  const payload = line.replace(/^.*caseRunKeilSidecars\s*/u, "");
  const parsed = parseJsonMaybe(payload);
  if (parsed && typeof parsed === "object") return clean({ ...(parsed as Record<string, unknown>), line });
  return clean({ copied: numberOption(line.match(/copied=(\d+)/u)?.[1]), line });
}

export function objectRecord(value: unknown): Record<string, unknown> {
  return value && typeof value === "object" && !Array.isArray(value) ? value as Record<string, unknown> : {};
}

export function isAbsolutePathLike(value: string) {
  return /^[A-Za-z]:[\\/]/u.test(value) || value.startsWith("/") || value.startsWith("\\\\");
}

export function subjectRelativeWorktreePath(repoLocalPath: string, worktreePath: string) {
  const repo = repoLocalPath.replace(/[\\/]+$/u, "");
  const normalizedRepo = normalizeLocalPath(repo);
  const normalizedWorktree = normalizeLocalPath(worktreePath);
  if (!normalizedWorktree.startsWith(`${normalizedRepo}/`)) throw cliError("invalid_subject_worktree_path", "subject worktree path must stay under subject repo local path", { repoLocalPath, worktreePath });
  const relative = worktreePath.slice(repo.length).replace(/^[\\/]+/u, "");
  return relative || ".";
}

export function normalizeLocalPath(value: string) {
  return value.replace(/[\\/]+$/u, "").replace(/\\/gu, "/").toLowerCase();
}

export function subjectWorktreePath(repoLocalPath: string, runId: string) {
  const separator = looksLikeWindowsPath(repoLocalPath) ? "\\" : "/";
  return [repoLocalPath.replace(/[\\/]+$/u, ""), ".worktree", `caserun-${slug(runId)}`].join(separator);
}

export function looksLikeWindowsPath(value: string) {
  return /^[A-Za-z]:[\\/]/u.test(value) || value.includes("\\");
}

export function rewriteHwpodSpecWorkspacePath(specText: string, workspacePath: string) {
  const lines = specText.replace(/\r\n/gu, "\n").split("\n");
  let workspaceIndent: number | null = null;
  for (let index = 0; index < lines.length; index += 1) {
    const line = lines[index] ?? "";
    const workspaceMatch = line.match(/^(\s*)workspace:\s*(?:#.*)?$/u);
    if (workspaceMatch) {
      workspaceIndent = workspaceMatch[1].length;
      continue;
    }
    if (workspaceIndent === null) continue;
    const indent = line.match(/^(\s*)/u)?.[1].length ?? 0;
    if (line.trim() && indent <= workspaceIndent) {
      workspaceIndent = null;
      continue;
    }
    const pathMatch = line.match(/^(\s*)path\s*:/u);
    if (pathMatch) {
      lines[index] = `${pathMatch[1]}path: ${JSON.stringify(workspacePath)}`;
      return lines.join("\n");
    }
  }
  throw cliError("hwpod_spec_workspace_path_missing", "hwpod-spec.yaml must contain spec.workspace.path so CaseRun can isolate the subject worktree", {});
}

export function hwpodResultTexts(payload: any) {
  const results = Array.isArray(payload?.body?.results) ? payload.body.results : Array.isArray(payload?.results) ? payload.results : [];
  return results.flatMap((item: any) => [item?.stdout, item?.stderr, item?.summary, item?.output?.stdout, item?.output?.stderr, item?.output?.summary]).filter((item: unknown) => text(item));
}

export function artifactsFrom(value: any) {
  const candidates = [value?.artifacts, value?.artifact_paths, value?.artifactPaths, value?.data?.artifacts, value?.data?.artifact_paths];
  for (const candidate of candidates) {
    if (Array.isArray(candidate)) return candidate;
  }
  return [text(value?.result?.hex_file), text(value?.result?.axf_file), text(value?.hex_file), text(value?.axf_file)].filter(Boolean);
}

export function dirnameForCommandPath(value: string) {
  if (/^[A-Za-z]:\\/u.test(value)) return value.replace(/\\[^\\]+$/u, "") || value;
  return path.dirname(value);
}

function splitCommandWords(value: string) {
  const words = String(value || "py -3").match(/"[^"]*"|'[^']*'|\S+/gu) ?? ["py", "-3"];
  return words.map((word) => word.replace(/^"(.*)"$/su, "$1").replace(/^'(.*)'$/su, "$1"));
}

export function jobStatusCommandForTest(pythonCommand: string, keilCliPath: string, jobId: string) {
  const python = splitCommandWords(pythonCommand);
  return commandRunForTokens([python[0] ?? "py", ...python.slice(1), keilCliPath, "job-status", jobId], keilCliPath);
}

function commandRunForTokens(tokens: string[], pathHint = "") {
  if (tokens.some(looksLikeWindowsPath) || looksLikeWindowsPath(pathHint)) return { command: "cmd.exe", argv: ["/d", "/s", "/c", shellCommand(tokens)] };
  return { command: tokens[0] ?? "", argv: tokens.slice(1) };
}

function shellCommand(tokens: string[]) {
  return tokens.map(shellArg).join(" ");
}

function shellArg(value: string) {
  const raw = String(value);
  if (/^[A-Za-z0-9_./:@\\-]+$/u.test(raw)) return raw;
  return `"${raw.replace(/(["^&|<>])/gu, "^$1").replace(/%/gu, "%%")}"`;
}

export function artifactsFromKeilStatusForTest(value: any) {
  return artifactsFrom(value);
}

export async function pollKeilJobStatus(context: CaseContext, run: PreparedCaseRun, jobId: string, timeouts: Record<string, unknown>) {
  const timeoutMs = Math.min(numberOption(context.parsed.jobTimeoutMs ?? context.parsed.timeoutMs) ?? numberOption(timeouts.jobTimeoutMs ?? timeouts.keilJobTimeoutMs) ?? DEFAULT_JOB_TIMEOUT_MS, MAX_JOB_TIMEOUT_MS);
  const pollIntervalMs = numberOption(context.parsed.pollIntervalMs) ?? DEFAULT_POLL_INTERVAL_MS;
  const started = Date.now();
  const polls: any[] = [];
  let last: any = null;
  while (Date.now() - started <= timeoutMs) {
    const response = await requestKeilJobStatus(context, run, jobId);
    const status = keilJobStatus(response.body);
    last = { response, status };
    polls.push(status.summary);
    if (status.terminal) return { ok: status.ok, summary: { ...status.summary, jobId, polls: polls.length, timedOut: false } };
    await context.sleep(pollIntervalMs);
  }
  return { ok: false, summary: { jobId, status: text(last?.status?.summary?.status) || "timeout", polls: polls.length, timedOut: true, last: last?.status?.summary ?? null } };
}

export async function requestKeilJobStatus(context: CaseContext, run: PreparedCaseRun, jobId: string) {
  const document = await readHwpodSpec(run.specPath);
  const keilCliPath = text(document.spec.workspace.keilCliPath) || text(document.spec.debugProbe.keilCliPath) || "keil-cli.py";
  const pythonCommand = text(document.spec.workspace.pythonCommand) || text(document.spec.debugProbe.pythonCommand) || "py -3";
  const jobStatusCommand = jobStatusCommandForTest(pythonCommand, keilCliPath, jobId);
  const hwpodId = text(document.metadata.name) || text(document.metadata.uid) || run.caseId;
  const plan = {
    contractVersion: "hwpod-node-ops-v1",
    planId: stablePlanId(context, `job-${jobId}`),
    hwpodId,
    nodeId: document.spec.nodeBinding.nodeId,
    intent: "cmd.run",
    source: { compiler: "hwlab-cli.case", specPath: run.specPath, specAuthority: "case-run-state" },
    ops: [{
      opId: "op_01_keil_job_status",
      op: "cmd.run",
      args: {
        hwpodId,
        workspacePath: dirnameForCommandPath(keilCliPath),
        command: jobStatusCommand.command,
        argv: jobStatusCommand.argv
      }
    }]
  };
  const response = await context.fetchImpl(`${run.apiUrl}/v1/hwpod-node-ops`, {
    method: "POST",
    headers: { "content-type": "application/json" },
    body: JSON.stringify(plan)
  });
  const textBody = await response.text();
  return { status: response.status, body: parseJsonMaybe(textBody) ?? { raw: textBody }, plan };
}

function stablePlanId(context: CaseContext, suffix: string) {
  const identity = String((context.parsed as any).operationIdentity ?? "").trim();
  return identity ? `${identity}:${suffix.replace(/[^a-z0-9_.:-]+/giu, "-")}` : `case_plan_${randomUUID()}`;
}

export function keilJobStatus(body: any) {
  const parsed = parseJsonMaybe(hwpodResultTexts(body)[0]) ?? body;
  const status = text(parsed?.status ?? parsed?.data?.status ?? parsed?.job?.status) || "unknown";
  const returnCode = numberOption(parsed?.return_code ?? parsed?.returnCode ?? parsed?.data?.return_code);
  const success = parsed?.success === true || parsed?.ok === true || returnCode === 0;
  const failed = parsed?.success === false || parsed?.ok === false || (returnCode !== undefined && returnCode !== 0) || ["failed", "error", "cancelled"].includes(status);
  const terminal = success || failed || ["completed", "succeeded"].includes(status);
  const artifacts = artifactsFrom(parsed);
  const warningCount = firstNumberOption(parsed?.warningCount, parsed?.warning_count, parsed?.data?.warningCount, parsed?.data?.warning_count, parsed?.summary?.warningCount, parsed?.summary?.warning_count);
  return { terminal, ok: success || ["completed", "succeeded"].includes(status), summary: clean({ status, returnCode, warningCount, success, artifacts, raw: compactObject(parsed) }) };
}
