import { createHash } from "node:crypto";
import { lstat, mkdir, readFile, readdir, realpath, rm, writeFile } from "node:fs/promises";
import path from "node:path";
import { verifyRunrailCandidateAudit } from "./runrail-deepseek-advisory-core.mjs";

export const RUNRAIL_REVIEW_FOCUS = Object.freeze({
  schemaVersion: 1,
  artifactType: "runrail-review-focus",
  profile: "runrail-review-focus-v1",
  product: "Runrail Local v1"
});

const itemDefinitions = Object.freeze({
  RESOLVE_PREFLIGHT_BLOCKERS: Object.freeze({
    category: "condition",
    label: "Resolve the blocked preflight",
    reason: "Inspect the deterministic failure evidence and produce a new candidate before considering approval.",
    blocking: true
  }),
  COLLECT_BASELINE_CONTRACT_EVIDENCE: Object.freeze({
    category: "action",
    label: "Collect baseline contract evidence",
    reason: "Complete the evidence required by the unchanged base-commit review contract.",
    blocking: true
  }),
  REVIEW_SECURITY_SENSITIVE_PATHS: Object.freeze({
    category: "action",
    label: "Complete security-sensitive review",
    reason: "Have a qualified reviewer inspect the security-sensitive change category identified by policy.",
    blocking: true
  }),
  REVIEW_DEPENDENCY_CHANGES: Object.freeze({
    category: "action",
    label: "Complete dependency review",
    reason: "Inspect dependency intent, provenance, compatibility, and supply-chain impact outside Runrail.",
    blocking: true
  }),
  COMPLETE_SPECIALIST_REVIEW: Object.freeze({
    category: "action",
    label: "Complete specialist review",
    reason: "The deterministic risk policy requires a specialist review that Runrail cannot perform.",
    blocking: true
  }),
  REVIEW_PYTHON_IMPORTS: Object.freeze({
    category: "action",
    label: "Review Python imports and runtime behavior",
    reason: "Syntax passed, but imports and runtime behavior still require project-context inspection.",
    blocking: true
  }),
  RUN_APPROVED_PROJECT_CHECKS: Object.freeze({
    category: "action",
    label: "Run the repository's approved project checks",
    reason: "Runrail does not execute project tests or establish semantic correctness.",
    blocking: true
  }),
  MANUAL_PATCH_REVIEW: Object.freeze({
    category: "action",
    label: "Inspect the candidate patch manually",
    reason: "Read the complete local patch and reconcile it with the intended change before deciding.",
    blocking: true
  }),
  HUMAN_MERGE_DECISION: Object.freeze({
    category: "decision",
    label: "Record the human merge decision",
    reason: "Only an authorized human may accept, reject, or request another candidate revision.",
    blocking: true
  })
});

const canonicalItemOrder = Object.freeze(Object.keys(itemDefinitions));
const canonicalItemSet = new Set(canonicalItemOrder);

export class RunrailReviewFocusError extends Error {
  constructor(code, message) {
    super(message);
    this.name = "RunrailReviewFocusError";
    this.code = code;
  }
}

function fail(code, message) {
  throw new RunrailReviewFocusError(code, message);
}

function isRecord(value) {
  return value !== null && typeof value === "object" && !Array.isArray(value);
}

function stableValue(value) {
  if (Array.isArray(value)) return value.map(stableValue);
  if (isRecord(value)) {
    return Object.fromEntries(Object.keys(value).sort().map((key) => [key, stableValue(value[key])]));
  }
  return value;
}

export function stableRunrailReviewFocusJson(value) {
  return `${JSON.stringify(stableValue(value), null, 2)}\n`;
}

function sha256(value) {
  return createHash("sha256").update(value).digest("hex");
}

function exactKeys(value, expected, label) {
  if (!isRecord(value)) fail("focus-artifact-invalid", `${label} must be a plain object.`);
  const actual = Object.keys(value).sort();
  const required = [...expected].sort();
  if (actual.length !== required.length || actual.some((key, index) => key !== required[index])) {
    fail("focus-artifact-invalid", `${label} has an invalid field set.`);
  }
}

function findingCount(audit, ids) {
  const wanted = new Set(ids);
  return audit.decision.findings.filter((finding) => wanted.has(finding.id)).length;
}

function focusItem(id, { supportingFindingCount = 0, requiredEvidenceCount = 0 } = {}) {
  const definition = itemDefinitions[id];
  return {
    id,
    rank: 0,
    category: definition.category,
    label: definition.label,
    reason: definition.reason,
    blocking: definition.blocking,
    supportingFindingCount,
    requiredEvidenceCount
  };
}

function deriveFocusItems(audit) {
  const items = [];
  const candidateIds = new Set(audit.decision.candidateIds);
  const add = (id, counts = {}) => {
    if (items.some((item) => item.id === id)) fail("focus-item-duplicate", "Review Focus items must be unique.");
    items.push(focusItem(id, counts));
  };

  if (!audit.preflightPassed) {
    add("RESOLVE_PREFLIGHT_BLOCKERS", {
      supportingFindingCount: Math.max(1, audit.decision.findings.length)
    });
  }

  if (
    audit.schemaVersion >= 4 &&
    (
      audit.reviewContract.requiredEvidence.length > 0 ||
      audit.reviewContract.matches.length > 0 ||
      !audit.reviewContract.passed
    )
  ) {
    add("COLLECT_BASELINE_CONTRACT_EVIDENCE", {
      supportingFindingCount: findingCount(audit, [
        "REVIEW_CONTRACT_CHANGED",
        "REVIEW_CONTRACT_BUDGET_EXCEEDED",
        "REVIEW_CONTRACT_RULE_MATCH",
        "REVIEW_CONTRACT_COMPANION_MISSING"
      ]),
      requiredEvidenceCount: audit.reviewContract.requiredEvidence.length
    });
  }

  if (candidateIds.has("REVIEW_SECURITY_SENSITIVE_PATHS")) {
    add("REVIEW_SECURITY_SENSITIVE_PATHS", {
      supportingFindingCount: findingCount(audit, ["SECURITY_SENSITIVE_PATHS"])
    });
  }
  if (candidateIds.has("REVIEW_DEPENDENCY_CHANGES")) {
    add("REVIEW_DEPENDENCY_CHANGES", {
      supportingFindingCount: findingCount(audit, ["DEPENDENCY_FILES_CHANGED"])
    });
  }
  if (
    audit.decision.specialistReviewRequired &&
    !candidateIds.has("REVIEW_SECURITY_SENSITIVE_PATHS") &&
    !candidateIds.has("REVIEW_DEPENDENCY_CHANGES")
  ) {
    add("COMPLETE_SPECIALIST_REVIEW", {
      supportingFindingCount: audit.decision.findingCounts.high
    });
  }
  if (candidateIds.has("REVIEW_PYTHON_IMPORTS")) {
    add("REVIEW_PYTHON_IMPORTS", {
      supportingFindingCount: findingCount(audit, ["PYTHON_FILES_CHANGED"])
    });
  }

  add("RUN_APPROVED_PROJECT_CHECKS", {
    supportingFindingCount: findingCount(audit, ["NO_TEST_FILES_CHANGED", "TEST_FILES_CHANGED"])
  });
  add("MANUAL_PATCH_REVIEW", {
    supportingFindingCount: audit.decision.findings.length
  });
  add("HUMAN_MERGE_DECISION");

  return items
    .sort((left, right) => canonicalItemOrder.indexOf(left.id) - canonicalItemOrder.indexOf(right.id))
    .map((item, index) => ({ ...item, rank: index + 1 }));
}

export function buildRunrailReviewFocus(audit) {
  verifyRunrailCandidateAudit(audit);
  const items = deriveFocusItems(audit);
  if (items.length < 1) fail("focus-item-empty", "Review Focus must contain at least one canonical item.");
  const itemIdentity = sha256(stableRunrailReviewFocusJson({
    sourceAuditChecksumSha256: audit.checksumSha256,
    itemIds: items.map((item) => item.id)
  }));
  const priorityLevel = audit.preflightPassed ? audit.decision.riskLevel : "blocked";
  const body = {
    schemaVersion: RUNRAIL_REVIEW_FOCUS.schemaVersion,
    artifactType: RUNRAIL_REVIEW_FOCUS.artifactType,
    profile: RUNRAIL_REVIEW_FOCUS.profile,
    product: RUNRAIL_REVIEW_FOCUS.product,
    focusId: `RRF-${audit.checksumSha256.slice(0, 16).toUpperCase()}-${itemIdentity.slice(0, 12).toUpperCase()}`,
    sourceAuditChecksumSha256: audit.checksumSha256,
    priority: {
      level: priorityLevel,
      reasonCode: audit.preflightPassed
        ? `deterministic-${audit.decision.riskLevel}-review-priority`
        : "preflight-blocked"
    },
    items,
    summary: {
      itemCount: items.length,
      blockingItemCount: items.filter((item) => item.blocking).length,
      specialistItemCount: items.filter((item) => [
        "REVIEW_SECURITY_SENSITIVE_PATHS",
        "REVIEW_DEPENDENCY_CHANGES",
        "COMPLETE_SPECIALIST_REVIEW"
      ].includes(item.id)).length
    },
    boundaries: {
      pathFree: true,
      authority: "none",
      humanApprovalRequired: true,
      humanApprovalRequiredByPolicy: true,
      projectTestsExecuted: false,
      projectTestsRequired: true,
      mergeReady: false,
      networkUsed: false,
      repositoryOpened: false,
      sourceMutationPerformed: false,
      automaticApplyAvailable: false
    }
  };
  return { ...body, checksumSha256: sha256(stableRunrailReviewFocusJson(body)) };
}

export function verifyRunrailReviewFocus(focus, audit = null) {
  exactKeys(focus, [
    "schemaVersion",
    "artifactType",
    "profile",
    "product",
    "focusId",
    "sourceAuditChecksumSha256",
    "priority",
    "items",
    "summary",
    "boundaries",
    "checksumSha256"
  ], "Review Focus");
  if (
    focus.schemaVersion !== RUNRAIL_REVIEW_FOCUS.schemaVersion ||
    focus.artifactType !== RUNRAIL_REVIEW_FOCUS.artifactType ||
    focus.profile !== RUNRAIL_REVIEW_FOCUS.profile ||
    focus.product !== RUNRAIL_REVIEW_FOCUS.product
  ) {
    fail("focus-artifact-invalid", "Review Focus identity fields are invalid.");
  }
  if (!/^RRF-[A-F0-9]{16}-[A-F0-9]{12}$/.test(focus.focusId || "")) {
    fail("focus-artifact-invalid", "Review Focus identifier is invalid.");
  }
  if (!/^[a-f0-9]{64}$/.test(focus.sourceAuditChecksumSha256 || "")) {
    fail("focus-artifact-invalid", "Review Focus source checksum is invalid.");
  }
  if (!/^[a-f0-9]{64}$/.test(focus.checksumSha256 || "")) {
    fail("focus-artifact-invalid", "Review Focus checksum is invalid.");
  }
  const body = Object.fromEntries(Object.entries(focus).filter(([key]) => key !== "checksumSha256"));
  if (sha256(stableRunrailReviewFocusJson(body)) !== focus.checksumSha256) {
    fail("focus-checksum-mismatch", "Review Focus content checksum does not match.");
  }
  exactKeys(focus.priority, ["level", "reasonCode"], "Review Focus priority");
  if (!new Set(["blocked", "high", "medium", "low"]).has(focus.priority.level)) {
    fail("focus-artifact-invalid", "Review Focus priority is invalid.");
  }
  if (typeof focus.priority.reasonCode !== "string" || !/^[a-z0-9-]{3,80}$/.test(focus.priority.reasonCode)) {
    fail("focus-artifact-invalid", "Review Focus priority reason is invalid.");
  }
  if (!Array.isArray(focus.items) || focus.items.length < 1 || focus.items.length > canonicalItemOrder.length) {
    fail("focus-item-empty", "Review Focus must contain a bounded non-empty item array.");
  }
  const seen = new Set();
  let previousOrder = -1;
  for (const [index, item] of focus.items.entries()) {
    exactKeys(item, [
      "id",
      "rank",
      "category",
      "label",
      "reason",
      "blocking",
      "supportingFindingCount",
      "requiredEvidenceCount"
    ], `Review Focus item ${index + 1}`);
    if (!canonicalItemSet.has(item.id) || seen.has(item.id)) {
      fail("focus-item-invalid", "Review Focus items must be unique fixed allowlist entries.");
    }
    seen.add(item.id);
    const order = canonicalItemOrder.indexOf(item.id);
    if (order <= previousOrder || item.rank !== index + 1) {
      fail("focus-item-order", "Review Focus items must use canonical ordering and consecutive ranks.");
    }
    previousOrder = order;
    const definition = itemDefinitions[item.id];
    if (
      item.category !== definition.category ||
      item.label !== definition.label ||
      item.reason !== definition.reason ||
      item.blocking !== true
    ) {
      fail("focus-item-invalid", "Review Focus item content must match its fixed definition.");
    }
    for (const count of [item.supportingFindingCount, item.requiredEvidenceCount]) {
      if (!Number.isSafeInteger(count) || count < 0 || count > 100) {
        fail("focus-item-invalid", "Review Focus item counts must be bounded integers.");
      }
    }
  }
  exactKeys(focus.summary, ["itemCount", "blockingItemCount", "specialistItemCount"], "Review Focus summary");
  const expectedSpecialistItemCount = focus.items.filter((item) => [
    "REVIEW_SECURITY_SENSITIVE_PATHS",
    "REVIEW_DEPENDENCY_CHANGES",
    "COMPLETE_SPECIALIST_REVIEW"
  ].includes(item.id)).length;
  if (
    focus.summary.itemCount !== focus.items.length ||
    focus.summary.blockingItemCount !== focus.items.length ||
    focus.summary.specialistItemCount !== expectedSpecialistItemCount
  ) {
    fail("focus-artifact-invalid", "Review Focus summary does not match its items.");
  }
  exactKeys(focus.boundaries, [
    "pathFree",
    "authority",
    "humanApprovalRequired",
    "humanApprovalRequiredByPolicy",
    "projectTestsExecuted",
    "projectTestsRequired",
    "mergeReady",
    "networkUsed",
    "repositoryOpened",
    "sourceMutationPerformed",
    "automaticApplyAvailable"
  ], "Review Focus boundaries");
  if (
    focus.boundaries.pathFree !== true ||
    focus.boundaries.authority !== "none" ||
    focus.boundaries.humanApprovalRequired !== true ||
    focus.boundaries.humanApprovalRequiredByPolicy !== true ||
    focus.boundaries.projectTestsExecuted !== false ||
    focus.boundaries.projectTestsRequired !== true ||
    focus.boundaries.mergeReady !== false ||
    focus.boundaries.networkUsed !== false ||
    focus.boundaries.repositoryOpened !== false ||
    focus.boundaries.sourceMutationPerformed !== false ||
    focus.boundaries.automaticApplyAvailable !== false
  ) {
    fail("focus-authority-boundary", "Review Focus authority and execution boundaries are invalid.");
  }
  if (audit !== null) {
    const expected = buildRunrailReviewFocus(audit);
    if (stableRunrailReviewFocusJson(focus) !== stableRunrailReviewFocusJson(expected)) {
      fail("focus-source-mismatch", "Review Focus does not match the verified source audit.");
    }
  }
  return focus;
}

function escapeHtml(value) {
  return String(value)
    .replaceAll("&", "&amp;")
    .replaceAll("<", "&lt;")
    .replaceAll(">", "&gt;")
    .replaceAll('"', "&quot;")
    .replaceAll("'", "&#39;");
}

function escapeMarkdown(value) {
  return String(value)
    .replaceAll("&", "&amp;")
    .replaceAll("<", "&lt;")
    .replaceAll(">", "&gt;")
    .replaceAll("`", "&#96;");
}

export function renderRunrailReviewFocusMarkdown(focus) {
  verifyRunrailReviewFocus(focus);
  const items = focus.items
    .map((item) => `${item.rank}. **${escapeMarkdown(item.label)}** - ${escapeMarkdown(item.reason)}`)
    .join("\n");
  return `# Runrail Review Focus

Focus: ${focus.focusId}

Priority: ${focus.priority.level}

## Ordered review plan

${items}

## Decision boundary

- Authority: none
- Human approval required by policy: yes
- Project tests executed: no
- Project tests required: yes
- Merge ready: no
- Repository opened: no
- Network used: no
- Source mutation performed: no

This path-free plan is deterministically derived from one checksum-verified Runrail audit. The source checksum links copies of the same audit and can therefore correlate those copies; it does not identify or authenticate the repository, operator, creator, time, or origin. Consumers must not use this artifact to automate apply, merge, deployment, or approval.
`;
}

export function renderRunrailReviewFocusHtml(focus) {
  verifyRunrailReviewFocus(focus);
  const items = focus.items
    .map((item) => `<li><span>${item.rank}</span><div><strong>${escapeHtml(item.label)}</strong><p>${escapeHtml(item.reason)}</p></div><small>${escapeHtml(item.category)}</small></li>`)
    .join("");
  return `<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width,initial-scale=1">
  <meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src 'unsafe-inline'; img-src 'none'; media-src 'none'; object-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'">
  <title>Runrail Review Focus</title>
  <style>
    :root{color-scheme:light;--ink:#101820;--muted:#5e6973;--line:#d9dee2;--paper:#fff;--wash:#f3f5f5;--green:#0b6c4e;--amber:#805500;--red:#982d25}*{box-sizing:border-box}body{margin:0;background:var(--wash);color:var(--ink);font:15px/1.55 ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif}main{width:min(980px,calc(100% - 32px));margin:0 auto;padding:42px 0 68px}header{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:24px;align-items:end;padding-bottom:28px;border-bottom:1px solid var(--line)}.eyebrow{margin:0;color:var(--green);font-size:11px;font-weight:900;text-transform:uppercase}h1{margin:6px 0 8px;font-size:clamp(32px,5vw,54px);line-height:1.02}p{margin:0;color:var(--muted)}.priority{min-width:150px;padding:15px;border:1px solid currentColor;text-align:center;font-weight:900;text-transform:uppercase}.priority-blocked,.priority-high{color:var(--red)}.priority-medium{color:var(--amber)}.priority-low{color:var(--green)}ol{margin:32px 0 0;padding:0;list-style:none;background:var(--paper);border-top:1px solid var(--line)}li{display:grid;grid-template-columns:44px minmax(0,1fr) auto;gap:16px;padding:19px 20px;border-bottom:1px solid var(--line)}li>span{display:grid;width:30px;height:30px;place-items:center;border:1px solid var(--line);font-weight:900}li strong,li p{display:block}li small{color:var(--muted);text-transform:uppercase;font-size:10px;font-weight:800}.boundary{display:grid;grid-template-columns:minmax(0,1fr) minmax(250px,.55fr);gap:24px;margin-top:28px;padding:23px;border:1px solid var(--line);background:var(--paper)}.boundary h2{margin:4px 0 7px;font-size:21px}.boundary dl{margin:0}.boundary dl div{display:flex;justify-content:space-between;gap:16px;padding:8px 0;border-top:1px solid var(--line)}dt{color:var(--muted)}dd{margin:0;font-weight:800}.checksum{margin-top:24px;font:10px/1.5 ui-monospace,SFMono-Regular,Consolas,monospace;overflow-wrap:anywhere}@media(max-width:680px){header,.boundary{grid-template-columns:1fr}.priority{text-align:left}li{grid-template-columns:36px minmax(0,1fr)}li small{grid-column:2}}
  </style>
</head>
<body>
<main>
  <header>
    <div><p class="eyebrow">Runrail local decision support</p><h1>Start with the highest-value review work.</h1><p>${escapeHtml(focus.focusId)} · deterministic and path-free</p></div>
    <div class="priority priority-${escapeHtml(focus.priority.level)}">${escapeHtml(focus.priority.level)} priority</div>
  </header>
  <ol>${items}</ol>
  <section class="boundary">
    <div><p class="eyebrow">Decision boundary</p><h2>A human still owns the decision.</h2><p>This plan does not run project tests, open a repository, call a provider, mutate source, or approve a merge. Its source checksum can correlate copies of the same audit but does not authenticate origin.</p></div>
    <dl><div><dt>Authority</dt><dd>None</dd></div><div><dt>Project tests</dt><dd>Not run</dd></div><div><dt>Merge ready</dt><dd>No</dd></div><div><dt>Automatic apply</dt><dd>Disabled</dd></div></dl>
  </section>
  <p class="checksum">Review Focus checksum: ${escapeHtml(focus.checksumSha256)}</p>
</main>
</body>
</html>
`;
}

function isInside(candidate, root) {
  const relative = path.relative(root, candidate);
  return relative !== "" && relative !== ".." && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative);
}

async function verifyStoredArtifacts({ audit, focus, focusPath, reviewPath, htmlReportPath }) {
  for (const artifactPath of [focusPath, reviewPath, htmlReportPath]) {
    const artifact = await lstat(artifactPath);
    if (!artifact.isFile() || artifact.isSymbolicLink()) {
      fail(
        "focus-artifact-boundary",
        "Review Focus artifacts must be ordinary Runrail-owned files."
      );
    }
  }
  const storedFocus = JSON.parse(await readFile(focusPath, "utf8"));
  verifyRunrailReviewFocus(storedFocus, audit);
  if (stableRunrailReviewFocusJson(storedFocus) !== stableRunrailReviewFocusJson(focus)) {
    fail("focus-artifact-mismatch", "Stored Review Focus JSON does not match its deterministic rebuild.");
  }
  if (await readFile(reviewPath, "utf8") !== renderRunrailReviewFocusMarkdown(focus)) {
    fail("focus-artifact-mismatch", "Stored Review Focus Markdown does not match its deterministic rebuild.");
  }
  if (await readFile(htmlReportPath, "utf8") !== renderRunrailReviewFocusHtml(focus)) {
    fail("focus-artifact-mismatch", "Stored Review Focus HTML does not match its deterministic rebuild.");
  }
}

export async function writeRunrailReviewFocusArtifacts({ audit, reviewRoot }) {
  const focus = buildRunrailReviewFocus(audit);
  const canonicalReviewRoot = await realpath(reviewRoot);
  const outputRoot = path.join(canonicalReviewRoot, "focus");
  let created = false;
  try {
    const existing = await lstat(outputRoot);
    if (!existing.isDirectory() || existing.isSymbolicLink()) {
      fail("focus-artifact-boundary", "The Review Focus path must be an ordinary Runrail-owned directory.");
    }
    const canonicalOutputRoot = await realpath(outputRoot);
    if (!isInside(canonicalOutputRoot, canonicalReviewRoot)) {
      fail("focus-artifact-boundary", "The Review Focus directory escaped the Runrail review directory.");
    }
    const entries = (await readdir(canonicalOutputRoot)).sort();
    if (entries.join(",") !== "REVIEW_FOCUS.md,focus.html,focus.json") {
      fail("focus-artifact-mismatch", "The existing Review Focus directory contains an invalid file set.");
    }
  } catch (error) {
    if (error instanceof RunrailReviewFocusError) throw error;
    if (error?.code !== "ENOENT") throw error;
    await mkdir(outputRoot, { mode: 0o700 });
    created = true;
    const canonicalOutputRoot = await realpath(outputRoot);
    if (!isInside(canonicalOutputRoot, canonicalReviewRoot)) {
      await rm(outputRoot, { recursive: true, force: true });
      fail("focus-artifact-boundary", "The Review Focus directory escaped the Runrail review directory.");
    }
  }

  const focusPath = path.join(outputRoot, "focus.json");
  const reviewPath = path.join(outputRoot, "REVIEW_FOCUS.md");
  const htmlReportPath = path.join(outputRoot, "focus.html");
  if (created) {
    try {
      await writeFile(focusPath, stableRunrailReviewFocusJson(focus), { encoding: "utf8", mode: 0o600, flag: "wx" });
      await writeFile(reviewPath, renderRunrailReviewFocusMarkdown(focus), { encoding: "utf8", mode: 0o600, flag: "wx" });
      await writeFile(htmlReportPath, renderRunrailReviewFocusHtml(focus), { encoding: "utf8", mode: 0o600, flag: "wx" });
    } catch (error) {
      await rm(outputRoot, { recursive: true, force: true }).catch(() => {});
      throw error;
    }
  }
  await verifyStoredArtifacts({ audit, focus, focusPath, reviewPath, htmlReportPath });
  return {
    ok: true,
    mode: "deterministic-review-focus",
    focusId: focus.focusId,
    outputRoot,
    focusPath,
    reviewPath,
    htmlReportPath,
    reusedExistingArtifacts: !created,
    priority: focus.priority.level,
    itemCount: focus.summary.itemCount,
    humanApprovalRequired: true,
    projectTestsExecuted: false,
    projectTestsRequired: true,
    mergeReady: false,
    networkRequired: false,
    sourceApplyPerformed: false,
    authority: "none",
    focus
  };
}
