#!/usr/bin/env node
"use strict";

const crypto = require("node:crypto");
const { spawnSync } = require("node:child_process");
const fs = require("node:fs");
const path = require("node:path");
const readline = require("node:readline");

const SERVER_NAME = "new-client-review";
const PLUGIN_NAME = "new-client";
const PLUGIN_ROOT = path.resolve(__dirname, "..");
const PLUGIN_MANIFEST = JSON.parse(
  fs.readFileSync(path.join(PLUGIN_ROOT, ".codex-plugin", "plugin.json"), "utf8"),
);
const SERVER_VERSION = PLUGIN_MANIFEST.version || "0.1.0";
const CONTRACT_VERSION = "1.1";
const WIDGET_URI = "ui://widget/new-client-review.html";
const WIDGET_MIME_TYPE = "text/html;profile=mcp-app";
const MAX_ITEMS = 2500;
const MAX_PAYLOAD_BYTES = 1_500_000;
const MAX_TEXT_LENGTH = 10_000;
const MAX_SHORT_TEXT_LENGTH = 500;
const MAX_SOURCE_ARTIFACTS = 24;
const MAX_REQUESTED_DOCUMENTS = 100;
const MAX_LOCAL_JSON_BYTES = 25_000_000;
const MAX_PERSISTENCE_CONTEXTS = 128;
const PERSISTENCE_CONTEXT_TTL_MS = 4 * 60 * 60 * 1000;
const PERSISTENCE_TOKEN_RE = /^[A-Za-z0-9_-]{43}$/;
const PERSISTENCE_CONTEXTS = new Map();
const TEMPORAL_VALIDITY_POLICY = "inclusive_earliest_material_deadline_v1";
const TEMPORAL_APPLY_RULE = "system_utc_date_must_not_exceed_valid_through";
const TEMPORAL_DEADLINE_KINDS = [
  "authoritative_source_registry_review_by",
  "evidence_expires_on",
  "primary_identity_expires_on",
  "representative_identity_expires_on",
  "beneficial_owner_identity_expires_on",
  "template_valid_until",
  "template_review_due_on",
];
const TOOL_NAMES = {
  validate: "validate_new_client_review",
  render: "render_new_client_review",
  save: "save_new_client_decisions",
  apply: "apply_new_client_decisions",
};

function pythonExecutable() {
  const virtualEnvironmentPython = process.env.VIRTUAL_ENV
    ? path.join(
        process.env.VIRTUAL_ENV,
        process.platform === "win32" ? "Scripts/python.exe" : "bin/python",
      )
    : "";
  const repositoryPython = path.resolve(
    PLUGIN_ROOT,
    "..",
    "..",
    ".venv",
    process.platform === "win32" ? "Scripts/python.exe" : "bin/python",
  );
  const candidates = [
    process.env.VERA_CLIENT_WORKFLOW_PYTHON,
    process.env.PYTHON,
    virtualEnvironmentPython,
    repositoryPython,
    "python3",
    "python",
  ].filter(Boolean);
  for (const candidate of candidates) {
    if (path.isAbsolute(candidate) && !fs.existsSync(candidate)) continue;
    return candidate;
  }
  return "python3";
}

const CLIENT_WORKFLOW_PREFLIGHT = String.raw`
import json
import sys
from pathlib import Path

plugin_root = Path(sys.argv[1]).resolve()
output_dir = Path(sys.argv[2])
workflow_id = sys.argv[3]
candidates = (
    plugin_root.parent / "_shared" / "vendor" / "modules",
    plugin_root / "vendor" / "modules",
    plugin_root.parent.parent / "vendor" / "modules",
)
for candidate in candidates:
    if (candidate / "vera_assurance").is_dir():
        sys.path.insert(0, str(candidate))
        break
else:
    raise RuntimeError("The required vera_assurance module is not available.")

from vera_assurance import load_client_workflow_context_for_output

context = load_client_workflow_context_for_output(
    output_dir,
    expected_workflow_id=workflow_id,
)
print(json.dumps({
    "ok": True,
    "schema_version": context["schema_version"],
    "workflow_id": context["workflow_id"],
    "run_id": context["run_id"],
}, separators=(",", ":")))
`;

function preflightClientWorkflowRun(outputDir, expectedRunId) {
  if (!outputDir) return null;
  // Run ownership and lifecycle state are exact audit properties, so every
  // persistence boundary reuses the shared v2 validator immediately before write.
  const completed = spawnSync(
    pythonExecutable(),
    ["-I", "-B", "-c", CLIENT_WORKFLOW_PREFLIGHT, PLUGIN_ROOT, outputDir, PLUGIN_NAME],
    { cwd: PLUGIN_ROOT, encoding: "utf8", maxBuffer: 64 * 1024 },
  );
  if (completed.error || completed.status !== 0) {
    throw new Error(
      "New Client persistence requires a running v2 customer-folder workflow run",
    );
  }
  let result;
  try {
    result = JSON.parse(completed.stdout.trim());
  } catch {
    throw new Error("New Client customer-run preflight returned an invalid result");
  }
  if (
    !isObject(result)
    || result.ok !== true
    || result.schema_version !== "vera.client_workflow_context.v2"
    || result.workflow_id !== PLUGIN_NAME
    || result.run_id !== expectedRunId
  ) {
    throw new Error("New Client customer-run preflight returned an invalid result");
  }
  return result;
}

// These are the complete public review vocabularies. Domain classification remains
// upstream/model-led; this server only validates the resulting review contract.
const ITEM_TYPES = new Set([
  "party_fact",
  "party_profile",
  "party_structure",
  "representative_fact",
  "beneficial_owner_fact",
  "engagement_service",
  "engagement",
  "screening_result",
  "screening_subject",
  "document_applicability",
  "aml_risk_factor",
  "aml_factor_section",
  "aml_mandatory_trigger",
  "aml_trigger_set",
  "aml_assessment",
  "missing_evidence",
  "document_plan",
  "monitoring_plan",
  "official_source",
  "client_file_preparation_binding",
  "privacy_processing",
  "marketing_consent",
]);
const ALLOWED_ACTIONS = new Set([
  "accept",
  "reject",
  "edit",
  "mark_unclear",
  "request_more_documents",
  "skip",
]);
const SEMANTIC_ITEM_TYPES = new Set([
  "party_profile",
  "party_structure",
  "engagement_service",
  "engagement",
  "screening_result",
  "screening_subject",
  "document_applicability",
  "aml_risk_factor",
  "aml_factor_section",
  "aml_mandatory_trigger",
  "aml_trigger_set",
  "aml_assessment",
  "document_plan",
  "monitoring_plan",
  "official_source",
  "client_file_preparation_binding",
  "privacy_processing",
  "marketing_consent",
]);
const BLOCKER_ACTIONS = new Set([
  "reject",
  "mark_unclear",
  "request_more_documents",
]);
const ACTION_STATUSES = {
  accept: "accepted",
  reject: "rejected",
  edit: "edited",
  mark_unclear: "needs_clarification",
  request_more_documents: "needs_documents",
  skip: "skipped",
};
const SOURCE_ARTIFACT_KEYS = new Set([
  "facts",
  "sources",
  "applicability",
  "aml",
  "documents",
  "monitoring",
]);
const REQUIRED_SOURCE_ARTIFACTS = {
  facts: "case_facts_validated.json",
  sources: "source_registry.json",
  applicability: "applicability_plan_validated.json",
  aml: "aml_calculation_audit.json",
  documents: "document_plan.json",
  monitoring: "monitoring_plan.json",
};
const REQUIRED_BASIS_HASH_KEYS = new Set([
  "new_client_input",
  "aml",
  "documents",
  "monitoring",
  "sources",
]);
const REQUIRED_EXPORT_OUTPUTS = new Set([
  "run_intake.json",
  "case_facts_validated.json",
  "source_registry.json",
  "applicability_plan_validated.json",
  "aml_assessment_draft.json",
  "aml_calculation_audit.json",
  "missing_evidence.json",
  "document_plan.json",
  "monitoring_plan.json",
  "studio_new_client_memo.md",
  "client_missing_information_draft.md",
  "review_payload.json",
  "ui_decisions.json",
  "review_handoff.md",
]);
const SOURCE_ARTIFACT_FIELDS = new Set([
  "path",
  "type",
  "sha256",
  "count",
]);
const REVIEW_STATUSES = new Set([
  "pending_review",
  "ready_for_review",
  "proposals_ready",
  "revision_required",
]);
const FINAL_ARTIFACT_STATUSES = new Set([
  "blocked",
  "pending_review",
  "ready_for_review",
  "partial_review_applied",
  "proposals_ready",
  "revision_required",
  "ready_for_professional_export",
]);
const UI_DECISION_STATUSES = new Set([
  "pending",
  "pending_review",
  "partial_review",
  "reviewed",
]);
const EXPORT_GATE_STATUSES = new Set([
  "blocked",
  "pending_review",
  "ready_for_professional_export",
]);
const EXPORT_GATE_FIELDS = new Set([
  "contract_version",
  "export_scope",
  "evaluated_at",
  "review_revision",
  "status",
  "relationship_ready",
  "domain_blockers",
  "review_blockers",
  "artifact_blockers",
  "marketing_only_blockers",
  "required_outputs",
  "basis_hashes",
]);
const REVIEW_FIELDS = new Set([
  "schema_version",
  "contract_version",
  "plugin",
  "workflow",
  "run_id",
  "review_revision",
  "generated_at",
  "temporal_validity",
  "status",
  "client_reference",
  "source_paths",
  "review_type",
  "item_count",
  "columns",
  "allowed_actions",
  "privacy",
  "summary",
  "source_artifacts",
  "basis_hashes",
  "professional_review_required",
  "signature_performed",
  "client_communication_sent",
  "relationship_activation_performed",
  "items",
]);
const REVIEW_ITEM_FIELDS = new Set([
  "id",
  "item_type",
  "title",
  "status",
  "allowed_actions",
  "recommended_action",
  "source_ids",
  "data",
]);
const ITEM_DATA_FIELDS = {
  party_profile: new Set([
    "client_reference",
    "client_type",
    "tax_fact_statuses",
    "party_facts",
    "identity_status",
    "identity_expires_on",
    "identity_document_number",
  ]),
  party_structure: new Set([
    "representative_posture_status",
    "representative_roles",
    "representative_identity_statuses",
    "executor_selected",
    "ownership_status",
    "owner_count",
    "owner_verification_statuses",
    "owner_identity_statuses",
    "representatives",
    "beneficial_owners",
  ]),
  party_fact: new Set([
    "fact_code",
    "value",
    "confirmation_status",
    "evidence_count",
    "document_type",
    "document_number",
    "verification_date",
  ]),
  representative_fact: new Set([
    "representative_reference",
    "role",
    "authority_basis",
    "confirmation_status",
    "verification_date",
    "document_number",
    "evidence_count",
  ]),
  beneficial_owner_fact: new Set([
    "owner_reference",
    "control_basis",
    "confirmation_status",
    "identity_verification_status",
    "verification_date",
    "document_number",
    "evidence_count",
  ]),
  engagement_service: new Set([
    "service_id",
    "confirmation_status",
    "description",
  ]),
  engagement: new Set([
    "engagement_kind",
    "services",
    "terms_review_status",
    "duration_months",
    "notice_days",
    "advance_amount",
    "currency",
    "payment_terms_recorded",
    "indexation_basis_recorded",
    "insurance_reference_recorded",
  ]),
  screening_result: new Set([
    "screening_id",
    "subject_reference",
    "screening_type",
    "source_reference",
    "checked_at",
    "outcome",
    "confirmation_status",
    "resolution_status",
    "relationship_decision",
    "resolution_evidence_count",
  ]),
  screening_subject: new Set([
    "subject_reference",
    "coverage_complete",
    "results",
  ]),
  document_applicability: new Set([
    "topic",
    "applicability_status",
    "review_status",
    "basis",
    "supporting_case_fact_codes",
    "supporting_case_fact_count",
  ]),
  aml_risk_factor: new Set([
    "factor_code",
    "score",
    "confirmation_status",
    "basis",
    "evidence_count",
  ]),
  aml_factor_section: new Set([
    "section",
    "factors",
  ]),
  aml_mandatory_trigger: new Set([
    "trigger_id",
    "status",
    "review_status",
    "basis",
  ]),
  aml_trigger_set: new Set(["triggers"]),
  aml_assessment: new Set([
    "calculation_status",
    "effective_risk",
    "calculated_band",
    "minimum_verification_mode_for_review",
    "uses_proposed_inputs",
    "professional_review_required",
  ]),
  missing_evidence: new Set([
    "missing_evidence_count",
    "evidence_status",
    "reason_counts",
    "item_type_counts",
    "reference",
    "reason",
  ]),
  document_plan: new Set(["documents"]),
  monitoring_plan: new Set([
    "status",
    "review_interval_months",
    "next_review_date",
    "minimum_verification_mode_for_review",
  ]),
  official_source: new Set([
    "source_id",
    "title",
    "issuer",
    "version",
    "authority",
    "public_url_recorded_locally",
  ]),
  client_file_preparation_binding: new Set([
    "binding_mode",
    "verification_status",
    "final_ready",
    "reviewed_client_file_preparation",
    "manifest_sha256",
    "relationship_blocker",
  ]),
  privacy_processing: new Set([
    "decision_alias",
    "purpose",
    "role",
    "legal_basis_code",
    "processor_authority_recorded",
    "retention_status",
    "review_status",
    "source_count",
  ]),
  marketing_consent: new Set([
    "scope",
    "request_status",
    "choice",
    "purpose_count",
    "channel_count",
    "review_status",
    "relationship_export_blocking",
  ]),
};
const FORBIDDEN_PRIVATE_KEYS = new Set([
  "access_token",
  "auth_token",
  "authorization_header",
  "cookie",
  "cookies",
  "credential",
  "credentials",
  "evidence_path",
  "local_path",
  "one_time_code",
  "otp",
  "otp_code",
  "password",
  "private_session_url",
  "raw_path",
  "refresh_token",
  "session_cookie",
  "session_token",
  "session_url",
  "token",
]);
const SAFE_IDENTIFIER_RE = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,159}$/;
const LOWERCASE_SHA256_RE = /^[0-9a-f]{64}$/;

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

function objectSchema(properties, required = [], additionalProperties = true) {
  return { type: "object", properties, required, additionalProperties };
}

function toolUiMeta() {
  return {
    ui: { resourceUri: WIDGET_URI, visibility: ["model"] },
    "ui/resourceUri": WIDGET_URI,
    "openai/outputTemplate": WIDGET_URI,
    "openai/widgetAccessible": true,
    "openai/toolInvocation/invoking": "Rendering new client review",
    "openai/toolInvocation/invoked": "Rendered new client review",
  };
}

function widgetMeta() {
  return {
    ui: { resourceUri: WIDGET_URI },
    "openai/widgetDescription":
      "Professional review of client facts, engagement scope, AML assessment, documents, and monitoring plan.",
    "openai/widgetPrefersBorder": false,
    "openai/widgetCSP": { connect_domains: [], resource_domains: [] },
    "openai/widgetDomain": "https://chatgpt.com",
  };
}

function toolDefinitions() {
  const reviewPayload = objectSchema(
    {
      schema_version: { type: "string" },
      contract_version: { type: "string" },
      plugin: { type: "string" },
      workflow: { type: "string" },
      run_id: { type: "string" },
      review_revision: { type: "integer", minimum: 1 },
      temporal_validity: { type: "object" },
      review_type: { type: "string" },
      status: { type: "string" },
      item_count: { type: "integer", minimum: 0, maximum: MAX_ITEMS },
      items: { type: "array", maxItems: MAX_ITEMS, items: { type: "object" } },
      source_artifacts: { type: "object" },
      basis_hashes: { type: "object" },
    },
    [
      "schema_version",
      "contract_version",
      "plugin",
      "workflow",
      "run_id",
      "review_revision",
      "temporal_validity",
      "review_type",
      "status",
      "item_count",
      "items",
      "source_artifacts",
      "basis_hashes",
    ],
  );
  const reviewInput = objectSchema(
    {
      client_engagement: { type: "string" },
      run_intake: { type: "object", description: "Optional run_intake.json object." },
      review_payload: reviewPayload,
      ui_decisions: { type: "object", description: "Optional current ui_decisions.json object." },
      final_artifacts: { type: "object", description: "Optional current final_artifacts.json object." },
    },
    ["review_payload"],
  );
  const decision = objectSchema(
    {
      item_id: { type: "string" },
      action: { type: "string", enum: [...ALLOWED_ACTIONS] },
      reviewer_note: { type: "string" },
      edit_value: { type: "string" },
      requested_documents: { type: "array", items: { type: "string" } },
      reuse_saved_details: {
        type: "boolean",
        description:
          "Reuse server-held decision details after a reload.",
      },
    },
    ["item_id", "action"],
  );
  const decisionInput = objectSchema(
    {
      client_engagement: { type: "string" },
      run_intake: { type: "object", description: "Optional run_intake.json with output_dir for persistence." },
      persistence_token: {
        type: "string",
        description: "Opaque token returned by the render tool for path-private persistence.",
      },
      review_payload: reviewPayload,
      ui_decisions: { type: "object" },
      final_artifacts: { type: "object" },
      decisions: { type: "array", maxItems: MAX_ITEMS, items: decision },
      decision_source: { type: "string" },
      reviewer: { type: "string" },
      expected_decision_revision: { type: "integer", minimum: 0 },
    },
    ["review_payload", "decisions"],
  );
  return [
    {
      name: TOOL_NAMES.validate,
      title: "Validate new client review",
      description: `Validate the private professional review before calling ${TOOL_NAMES.render}.`,
      inputSchema: reviewInput,
      annotations: {
        readOnlyHint: true,
        destructiveHint: false,
        idempotentHint: true,
        openWorldHint: false,
      },
    },
    {
      name: TOOL_NAMES.render,
      title: "Open new client review",
      description:
        "Render the professional review surface without exposing the local output directory or raw client documents.",
      inputSchema: reviewInput,
      _meta: toolUiMeta(),
      annotations: {
        readOnlyHint: true,
        destructiveHint: false,
        idempotentHint: false,
        openWorldHint: false,
      },
    },
    {
      name: TOOL_NAMES.save,
      title: "Save new client decisions",
      description:
        "Validate reviewer actions and save ui_decisions.json in the run directory when one is provided.",
      inputSchema: decisionInput,
      annotations: {
        readOnlyHint: false,
        destructiveHint: true,
        idempotentHint: false,
        openWorldHint: false,
      },
    },
    {
      name: TOOL_NAMES.apply,
      title: "Apply new client decisions",
      description:
        "Record review effects in applied_decisions.json and update final_artifacts.json without modifying domain artifacts, signing, contacting the client, or activating the relationship.",
      inputSchema: decisionInput,
      annotations: {
        readOnlyHint: false,
        destructiveHint: true,
        idempotentHint: false,
        openWorldHint: false,
      },
    },
  ];
}

function resources() {
  return [
    {
      uri: WIDGET_URI,
      name: "new_client_review_widget",
      title: "New Client professional setup review",
      description: "Interactive professional review of a new-client case.",
      mimeType: WIDGET_MIME_TYPE,
      _meta: widgetMeta(),
    },
  ];
}

function resourceText(uri) {
  if (uri !== WIDGET_URI) throw new Error(`unknown widget resource: ${uri}`);
  return fs.readFileSync(
    path.join(PLUGIN_ROOT, "assets", "new-client-review-widget.html"),
    "utf8",
  );
}

function requireText(value, field, maxLength = MAX_TEXT_LENGTH) {
  if (typeof value !== "string" || value.trim() === "") {
    throw new Error(`${field} must be a non-empty string`);
  }
  if (value.length > maxLength) throw new Error(`${field} exceeds ${maxLength} characters`);
  return value.trim();
}

function optionalText(value, field, maxLength = MAX_TEXT_LENGTH) {
  if (value == null) return "";
  if (typeof value !== "string") throw new Error(`${field} must be a string when provided`);
  if (value.length > maxLength) throw new Error(`${field} exceeds ${maxLength} characters`);
  return value.trim();
}

function safeIdentifier(value, field) {
  const text = requireText(value, field, 160);
  if (
    !SAFE_IDENTIFIER_RE.test(text)
    || text.includes("//")
  ) {
    throw new Error(`${field} must be a structurally safe identifier`);
  }
  return text;
}

function validatePublicUrl(raw, field) {
  let url;
  try {
    url = new URL(raw);
  } catch {
    throw new Error(`${field} contains an invalid URL`);
  }
  const privateHost =
    url.hostname === "localhost"
    || url.hostname.endsWith(".local")
    || /^(?:127\.|10\.|192\.168\.|169\.254\.|172\.(?:1[6-9]|2\d|3[01])\.)/.test(url.hostname);
  if (
    url.protocol !== "https:"
    || url.username
    || url.password
    || url.search
    || url.hash
    || (url.port && url.port !== "443")
    || privateHost
  ) {
    throw new Error(`${field} must use a public, untokenized HTTPS URL`);
  }
}

function assertSessionSafeText(value, field, maxLength = MAX_TEXT_LENGTH) {
  const text = optionalText(value, field, maxLength);
  if (!text) return text;
  for (const match of text.matchAll(/https?:\/\/[^\s<>"']+/gi)) {
    validatePublicUrl(match[0].replace(/[.,);\]]+$/, ""), field);
  }
  return text;
}

function auditSecretBoundaries(value, field = "review_payload") {
  if (Array.isArray(value)) {
    value.forEach((entry, index) => auditSecretBoundaries(entry, `${field}[${index}]`));
    return;
  }
  if (isObject(value)) {
    for (const [key, entry] of Object.entries(value)) {
      if (FORBIDDEN_PRIVATE_KEYS.has(key.toLowerCase())) {
        throw new Error(`${field}.${key} contains local-path, credential, or session material`);
      }
      auditSecretBoundaries(entry, `${field}.${key}`);
    }
    return;
  }
  if (typeof value === "string") {
    assertSessionSafeText(value, field);
  }
}

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

function canonicalJson(value) {
  return JSON.stringify(canonicalValue(value));
}

function canonicalSha256(value) {
  return crypto.createHash("sha256").update(canonicalJson(value), "utf8").digest("hex");
}

function payloadBytes(value) {
  return Buffer.byteLength(JSON.stringify(value), "utf8");
}

function assertAllowedKeys(value, allowed, field) {
  for (const key of Object.keys(value)) {
    if (!allowed.has(key)) throw new Error(`${field}.${key} is not allowed`);
  }
}

function validateOptionalDate(value, field) {
  if (value == null) return null;
  const text = requireText(value, field, 32);
  if (!/^\d{4}-\d{2}-\d{2}$/.test(text) || Number.isNaN(Date.parse(`${text}T00:00:00Z`))) {
    throw new Error(`${field} must be a YYYY-MM-DD date or null`);
  }
  return text;
}

function validateDateOnly(value, field) {
  const text = requireText(value, field, 10);
  if (!/^\d{4}-\d{2}-\d{2}$/.test(text)) {
    throw new Error(`${field} must be a YYYY-MM-DD date`);
  }
  const parsed = new Date(`${text}T00:00:00.000Z`);
  if (Number.isNaN(parsed.getTime()) || parsed.toISOString().slice(0, 10) !== text) {
    throw new Error(`${field} must be a real YYYY-MM-DD date`);
  }
  return text;
}

function validateTemporalValidityRecord(value, field = "review_payload.temporal_validity") {
  if (!isObject(value)) throw new Error(`${field} must be an object`);
  const allowed = new Set([
    "policy",
    "evaluated_on",
    "valid_through",
    "deadline_count",
    "deadlines",
    "source_currentness_status",
    "apply_rule",
  ]);
  assertAllowedKeys(value, allowed, field);
  if (Object.keys(value).length !== allowed.size) {
    throw new Error(`${field} must contain the complete temporal-validity contract`);
  }
  if (value.policy !== TEMPORAL_VALIDITY_POLICY) {
    throw new Error(`${field}.policy must be ${TEMPORAL_VALIDITY_POLICY}`);
  }
  if (value.apply_rule !== TEMPORAL_APPLY_RULE) {
    throw new Error(`${field}.apply_rule must be ${TEMPORAL_APPLY_RULE}`);
  }
  if (value.source_currentness_status !== "verified_current") {
    throw new Error(`${field}.source_currentness_status must be verified_current`);
  }
  const evaluatedOn = validateDateOnly(value.evaluated_on, `${field}.evaluated_on`);
  const validThrough = validateDateOnly(value.valid_through, `${field}.valid_through`);
  if (!Array.isArray(value.deadlines) || value.deadlines.length === 0) {
    throw new Error(`${field}.deadlines must be a non-empty array`);
  }
  const seen = new Set();
  let deadlineCount = 0;
  let earliest = null;
  let previousIndex = -1;
  value.deadlines.forEach((deadline, index) => {
    const root = `${field}.deadlines[${index}]`;
    if (!isObject(deadline)) throw new Error(`${root} must be an object`);
    const fields = new Set(["kind", "count", "valid_through"]);
    assertAllowedKeys(deadline, fields, root);
    if (Object.keys(deadline).length !== fields.size) {
      throw new Error(`${root} must contain exactly kind, count, and valid_through`);
    }
    const kind = requireText(deadline.kind, `${root}.kind`, 100);
    const kindIndex = TEMPORAL_DEADLINE_KINDS.indexOf(kind);
    if (kindIndex < 0 || kindIndex <= previousIndex || seen.has(kind)) {
      throw new Error(`${field}.deadlines must use unique contract kinds in canonical order`);
    }
    previousIndex = kindIndex;
    seen.add(kind);
    if (!Number.isInteger(deadline.count) || deadline.count < 1 || deadline.count > 100_000) {
      throw new Error(`${root}.count must be a positive bounded integer`);
    }
    deadlineCount += deadline.count;
    const deadlineDate = validateDateOnly(deadline.valid_through, `${root}.valid_through`);
    earliest = earliest == null || deadlineDate < earliest ? deadlineDate : earliest;
  });
  if (!seen.has("authoritative_source_registry_review_by")) {
    throw new Error(`${field}.deadlines must include authoritative source currentness`);
  }
  if (value.deadline_count !== deadlineCount) {
    throw new Error(`${field}.deadline_count must equal the summed deadline counts`);
  }
  if (validThrough !== earliest) {
    throw new Error(`${field}.valid_through must be the earliest material deadline`);
  }
  return value;
}

function validateOptionalNonNegativeInteger(value, field, maximum = 1_000_000) {
  if (value == null) return null;
  if (!Number.isInteger(value) || value < 0 || value > maximum) {
    throw new Error(`${field} must be a bounded non-negative integer or null`);
  }
  return value;
}

function validateSafeIdentifierArray(value, field, maximum = 100) {
  if (value == null) return [];
  if (!Array.isArray(value) || value.length > maximum) {
    throw new Error(`${field} must be an array with at most ${maximum} entries`);
  }
  const seen = new Set();
  return value.map((entry, index) => {
    const identifier = safeIdentifier(entry, `${field}[${index}]`);
    if (seen.has(identifier)) throw new Error(`${field} contains duplicate identifier: ${identifier}`);
    seen.add(identifier);
    return identifier;
  });
}

function validateRelativeBasename(value, field) {
  const text = requireText(value, field, 300);
  if (
    path.isAbsolute(text)
    || text === "."
    || text === ".."
    || text.includes("/")
    || text.includes("\\")
    || path.basename(text) !== text
  ) {
    throw new Error(`${field} must be a run-local basename`);
  }
  return text;
}

function validateSourceArtifact(value, field) {
  if (!isObject(value)) {
    throw new Error(`${field} must be a bounded source-reference object`);
  }
  const keys = Object.keys(value);
  if (keys.length === 0) throw new Error(`${field} must not be empty`);
  for (const key of keys) {
    if (!SOURCE_ARTIFACT_FIELDS.has(key)) {
      throw new Error(`${field}.${key} is not allowed in a source reference`);
    }
  }
  if (value.path == null) throw new Error(`${field} must include path`);
  const artifact = {};
  artifact.path = validateRelativeBasename(value.path, `${field}.path`);
  if (value.type != null) {
    artifact.type = safeIdentifier(value.type, `${field}.type`);
  }
  if (typeof value.sha256 !== "string" || !LOWERCASE_SHA256_RE.test(value.sha256)) {
    throw new Error(`${field}.sha256 must be a lowercase SHA-256 digest`);
  }
  artifact.sha256 = value.sha256;
  if (value.count != null) {
    if (!Number.isInteger(value.count) || value.count < 0 || value.count > 1_000_000) {
      throw new Error(`${field}.count must be a bounded non-negative integer`);
    }
    artifact.count = value.count;
  }
  return artifact;
}

function validateSourceArtifacts(value) {
  if (!isObject(value)) throw new Error("review_payload.source_artifacts must be an object");
  const entries = Object.entries(value);
  if (entries.length > MAX_SOURCE_ARTIFACTS) {
    throw new Error(`review_payload.source_artifacts exceeds ${MAX_SOURCE_ARTIFACTS} entries`);
  }
  const supplied = new Set(entries.map(([key]) => key));
  const missing = [...SOURCE_ARTIFACT_KEYS].filter((key) => !supplied.has(key));
  const unsupported = [...supplied].filter((key) => !SOURCE_ARTIFACT_KEYS.has(key));
  if (missing.length || unsupported.length) {
    const details = [];
    if (missing.length) details.push(`missing required keys: ${missing.sort().join(", ")}`);
    if (unsupported.length) details.push(`unsupported keys: ${unsupported.sort().join(", ")}`);
    throw new Error(`review_payload.source_artifacts ${details.join("; ")}`);
  }
  const artifacts = {};
  for (const [key, artifact] of entries) {
    artifacts[key] = validateSourceArtifact(
      artifact,
      `review_payload.source_artifacts.${key}`,
    );
    if (artifacts[key].path !== REQUIRED_SOURCE_ARTIFACTS[key]) {
      throw new Error(
        `review_payload.source_artifacts.${key}.path must be ${REQUIRED_SOURCE_ARTIFACTS[key]}`,
      );
    }
  }
  return artifacts;
}

function validateBasisHashes(value) {
  if (!isObject(value)) throw new Error("review_payload.basis_hashes must be an object");
  if (Object.keys(value).length > MAX_SOURCE_ARTIFACTS) {
    throw new Error(`review_payload.basis_hashes exceeds ${MAX_SOURCE_ARTIFACTS} entries`);
  }
  const supplied = new Set(Object.keys(value));
  const missing = [...REQUIRED_BASIS_HASH_KEYS].filter((key) => !supplied.has(key));
  const unsupported = [...supplied].filter((key) => !REQUIRED_BASIS_HASH_KEYS.has(key));
  if (missing.length || unsupported.length) {
    const details = [];
    if (missing.length) details.push(`missing required keys: ${missing.sort().join(", ")}`);
    if (unsupported.length) details.push(`unsupported keys: ${unsupported.sort().join(", ")}`);
    throw new Error(`review_payload.basis_hashes ${details.join("; ")}`);
  }
  const hashes = {};
  for (const [key, digest] of Object.entries(value)) {
    safeIdentifier(key, `review_payload.basis_hashes.${key}`);
    if (typeof digest !== "string" || !LOWERCASE_SHA256_RE.test(digest)) {
      throw new Error(`review_payload.basis_hashes.${key} must be a lowercase SHA-256 digest`);
    }
    hashes[key] = digest;
  }
  return hashes;
}

function requireBoolean(value, field) {
  if (typeof value !== "boolean") throw new Error(`${field} must be a boolean`);
  return value;
}

function validateCalculatedBand(value, field) {
  if (!isObject(value)) throw new Error(`${field} must be an object`);
  const fields = new Set(["code", "label_it", "interval", "baseline_verification_mode"]);
  assertAllowedKeys(value, fields, field);
  safeIdentifier(value.code, `${field}.code`);
  const label = assertSessionSafeText(value.label_it, `${field}.label_it`, 120);
  if (!label) throw new Error(`${field}.label_it must be non-empty`);
  const interval = assertSessionSafeText(value.interval, `${field}.interval`, 40);
  if (!interval) throw new Error(`${field}.interval must be non-empty`);
  if (!["simplified", "ordinary", "enhanced"].includes(value.baseline_verification_mode)) {
    throw new Error(`${field}.baseline_verification_mode is not supported`);
  }
}

function validateDocumentRows(value, field) {
  if (!Array.isArray(value) || value.length > 20) {
    throw new Error(`${field} must be an array with at most 20 entries`);
  }
  const fields = new Set([
    "document_type",
    "status",
    "template_reference_id",
  ]);
  const seen = new Set();
  value.forEach((record, index) => {
    const root = `${field}[${index}]`;
    if (!isObject(record)) throw new Error(`${root} must be an object`);
    assertAllowedKeys(record, fields, root);
    const documentType = safeIdentifier(record.document_type, `${root}.document_type`);
    if (seen.has(documentType)) throw new Error(`${field} contains duplicate document_type: ${documentType}`);
    seen.add(documentType);
    safeIdentifier(record.status, `${root}.status`);
    if (record.template_reference_id != null) {
      safeIdentifier(record.template_reference_id, `${root}.template_reference_id`);
    }
  });
}

function validateReviewItemData(data, itemType, field) {
  if (!isObject(data)) throw new Error(`${field} must be an object`);
  const allowed = ITEM_DATA_FIELDS[itemType];
  if (!allowed) throw new Error(`${field} has no declared allowlist for ${itemType}`);
  assertAllowedKeys(data, allowed, field);

  for (const key of [
    "fact_code",
    "representative_reference",
    "role",
    "confirmation_status",
    "owner_reference",
    "identity_verification_status",
    "service_id",
    "screening_id",
    "screening_type",
    "outcome",
    "resolution_status",
    "relationship_decision",
    "topic",
    "applicability_status",
    "review_status",
    "factor_code",
    "trigger_id",
    "status",
    "calculation_status",
    "minimum_verification_mode_for_review",
    "evidence_status",
    "reference",
    "reason",
    "document_type",
    "template_id",
    "source_id",
    "authority",
    "binding_mode",
    "verification_status",
    "decision_alias",
    "legal_basis_code",
    "retention_status",
    "scope",
    "request_status",
    "choice",
  ]) {
    if (data[key] != null) safeIdentifier(data[key], `${field}.${key}`);
  }
  for (const key of [
    "uses_proposed_inputs",
    "professional_review_required",
    "public_url_recorded_locally",
    "final_ready",
    "reviewed_client_file_preparation",
    "relationship_blocker",
    "processor_authority_recorded",
    "relationship_export_blocking",
  ]) {
    if (data[key] != null) requireBoolean(data[key], `${field}.${key}`);
  }
  for (const key of [
    "evidence_count",
    "missing_evidence_count",
    "review_interval_months",
    "source_count",
    "purpose_count",
    "channel_count",
    "resolution_evidence_count",
  ]) {
    if (Object.prototype.hasOwnProperty.call(data, key)) {
      validateOptionalNonNegativeInteger(data[key], `${field}.${key}`);
    }
  }
  for (const key of ["verification_date", "next_review_date"]) {
    if (Object.prototype.hasOwnProperty.call(data, key)) validateOptionalDate(data[key], `${field}.${key}`);
  }
  if (data.checked_at != null) {
    const checkedAt = requireText(data.checked_at, `${field}.checked_at`, 64);
    if (Number.isNaN(Date.parse(checkedAt)) || !/[zZ]|[+-]\d{2}:\d{2}$/.test(checkedAt)) {
      throw new Error(`${field}.checked_at must be an ISO-8601 timestamp with timezone`);
    }
  }
  for (const key of ["score", "effective_risk"]) {
    if (data[key] != null) {
      if (typeof data[key] !== "number" || !Number.isFinite(data[key]) || data[key] < 1 || data[key] > 4) {
        throw new Error(`${field}.${key} must be a finite number from 1 to 4`);
      }
    }
  }
  if (data.calculated_band != null) validateCalculatedBand(data.calculated_band, `${field}.calculated_band`);
  if (data.documents != null) validateDocumentRows(data.documents, `${field}.documents`);
  if (
    data.manifest_sha256 != null
    && (typeof data.manifest_sha256 !== "string"
      || !LOWERCASE_SHA256_RE.test(data.manifest_sha256))
  ) {
    throw new Error(`${field}.manifest_sha256 must be a lowercase SHA-256 digest`);
  }
  for (const key of ["title", "issuer", "version"]) {
    if (data[key] != null) {
      const text = assertSessionSafeText(data[key], `${field}.${key}`, 400);
      if (!text) throw new Error(`${field}.${key} must be non-empty`);
    }
  }
  for (const key of [
    "authority_basis",
    "basis",
    "control_basis",
    "description",
    "document_number",
    "purpose",
    "source_reference",
  ]) {
    if (data[key] != null) assertSessionSafeText(data[key], `${field}.${key}`);
  }
  auditSecretBoundaries(data, field);
}

function validateItem(item, index, seenIds) {
  const root = `review_payload.items[${index}]`;
  if (!isObject(item)) throw new Error(`${root} must be an object`);
  assertAllowedKeys(item, REVIEW_ITEM_FIELDS, root);
  const id = safeIdentifier(item.id, `${root}.id`);
  if (seenIds.has(id)) throw new Error(`review_payload.items contains duplicate id: ${id}`);
  seenIds.add(id);
  const itemType = safeIdentifier(item.item_type, `${root}.item_type`);
  if (!ITEM_TYPES.has(itemType)) throw new Error(`${root}.item_type is not supported: ${itemType}`);
  const title = assertSessionSafeText(item.title, `${root}.title`, 400);
  if (!title) throw new Error(`${root}.title must be a non-empty string`);
  if (!Array.isArray(item.allowed_actions) || item.allowed_actions.length === 0) {
    throw new Error(`${root}.allowed_actions must be a non-empty array`);
  }
  const seenActions = new Set();
  for (const action of item.allowed_actions) {
    if (!ALLOWED_ACTIONS.has(action)) throw new Error(`${root}.allowed_actions contains unsupported action: ${action}`);
    if (seenActions.has(action)) throw new Error(`${root}.allowed_actions contains duplicate action: ${action}`);
    seenActions.add(action);
  }
  if (item.recommended_action != null) {
    if (!ALLOWED_ACTIONS.has(item.recommended_action)) {
      throw new Error(`${root}.recommended_action is not supported`);
    }
    if (!seenActions.has(item.recommended_action)) {
      throw new Error(`${root}.recommended_action must be present in allowed_actions`);
    }
  }
  if (item.status != null && !["needs_review", "pending_review"].includes(item.status)) {
    throw new Error(`${root}.status is not supported`);
  }
  validateSafeIdentifierArray(item.source_ids, `${root}.source_ids`);
  validateReviewItemData(item.data || {}, itemType, `${root}.data`);
}

function validateBoundaryFlags(value, field) {
  if (!isObject(value)) return;
  if (value.professional_review_required != null && value.professional_review_required !== true) {
    throw new Error(`${field}.professional_review_required must be true`);
  }
  for (const key of [
    "signature_performed",
    "client_communication_sent",
    "relationship_activation_performed",
  ]) {
    if (value[key] != null && value[key] !== false) {
      throw new Error(`${field}.${key} must be false`);
    }
  }
}

function validateExportScope(value, field) {
  const scope = requireText(value, field, 200);
  if (
    scope !== "relationship_export"
    && scope !== "marketing_use"
    && !/^document:[A-Za-z0-9][A-Za-z0-9._:-]{0,159}$/.test(scope)
  ) {
    throw new Error(`${field} is not a supported export-gate scope`);
  }
  return scope;
}

function validateExportBlockers(value, field, includeDomain) {
  if (!Array.isArray(value) || value.length > MAX_ITEMS) {
    throw new Error(`${field} must be an array with at most ${MAX_ITEMS} entries`);
  }
  const allowed = includeDomain
    ? new Set(["code", "reference", "scope", "domain"])
    : new Set(["code", "reference", "scope"]);
  const seen = new Set();
  value.forEach((blocker, index) => {
    const root = `${field}[${index}]`;
    if (!isObject(blocker)) throw new Error(`${root} must be an object`);
    assertAllowedKeys(blocker, allowed, root);
    if (Object.keys(blocker).length !== allowed.size) {
      throw new Error(`${root} must contain exactly ${[...allowed].join(", ")}`);
    }
    const code = safeIdentifier(blocker.code, `${root}.code`);
    const reference = safeIdentifier(blocker.reference, `${root}.reference`);
    const scope = validateExportScope(blocker.scope, `${root}.scope`);
    const domain = includeDomain
      ? safeIdentifier(blocker.domain, `${root}.domain`)
      : "";
    const identity = `${code}\u0000${reference}\u0000${scope}\u0000${domain}`;
    if (seen.has(identity)) throw new Error(`${field} contains a duplicate blocker`);
    seen.add(identity);
  });
}

function validateExportGate(gate, review, manifestRecords = null) {
  if (!isObject(gate)) throw new Error("final_artifacts.export_gate must be an object");
  assertAllowedKeys(gate, EXPORT_GATE_FIELDS, "final_artifacts.export_gate");
  if (Object.keys(gate).length !== EXPORT_GATE_FIELDS.size) {
    throw new Error("final_artifacts.export_gate must contain the complete 1.1 contract");
  }
  if (gate.contract_version !== CONTRACT_VERSION) {
    throw new Error(`final_artifacts.export_gate.contract_version must be "${CONTRACT_VERSION}"`);
  }
  if (gate.export_scope !== "owner_only_professional_review_dossier") {
    throw new Error(
      "final_artifacts.export_gate.export_scope must be owner_only_professional_review_dossier",
    );
  }
  const evaluatedAt = requireText(
    gate.evaluated_at,
    "final_artifacts.export_gate.evaluated_at",
    64,
  );
  if (Number.isNaN(Date.parse(evaluatedAt)) || !/[zZ]|[+-]\d{2}:\d{2}$/.test(evaluatedAt)) {
    throw new Error(
      "final_artifacts.export_gate.evaluated_at must be an ISO-8601 timestamp with timezone",
    );
  }
  if (gate.review_revision !== review.review_revision) {
    throw new Error(
      "final_artifacts.export_gate.review_revision must match review_payload.review_revision",
    );
  }
  if (!EXPORT_GATE_STATUSES.has(gate.status)) {
    throw new Error("final_artifacts.export_gate.status is not supported");
  }
  requireBoolean(
    gate.relationship_ready,
    "final_artifacts.export_gate.relationship_ready",
  );
  validateExportBlockers(
    gate.domain_blockers,
    "final_artifacts.export_gate.domain_blockers",
    true,
  );
  validateExportBlockers(
    gate.review_blockers,
    "final_artifacts.export_gate.review_blockers",
    false,
  );
  validateExportBlockers(
    gate.artifact_blockers,
    "final_artifacts.export_gate.artifact_blockers",
    false,
  );
  validateExportBlockers(
    gate.marketing_only_blockers,
    "final_artifacts.export_gate.marketing_only_blockers",
    false,
  );
  if (
    !Array.isArray(gate.required_outputs)
    || gate.required_outputs.length === 0
    || gate.required_outputs.length > MAX_ITEMS
  ) {
    throw new Error("final_artifacts.export_gate.required_outputs must be non-empty");
  }
  const requiredOutputs = gate.required_outputs.map((entry, index) =>
    validateRelativeBasename(
      entry,
      `final_artifacts.export_gate.required_outputs[${index}]`,
    ));
  if (new Set(requiredOutputs).size !== requiredOutputs.length) {
    throw new Error("final_artifacts.export_gate.required_outputs must be unique");
  }
  const requiredOutputSet = new Set(requiredOutputs);
  if (
    requiredOutputSet.size !== REQUIRED_EXPORT_OUTPUTS.size
    || [...REQUIRED_EXPORT_OUTPUTS].some((name) => !requiredOutputSet.has(name))
  ) {
    throw new Error(
      "final_artifacts.export_gate.required_outputs must match the complete 1.1 base package",
    );
  }
  const basisHashes = validateBasisHashes(gate.basis_hashes);
  for (const key of REQUIRED_BASIS_HASH_KEYS) {
    if (basisHashes[key] !== review.basis_hashes[key]) {
      throw new Error(
        `final_artifacts.export_gate.basis_hashes.${key} must match review_payload`,
      );
    }
  }
  if (manifestRecords) {
    const missingRequiredOutputs = requiredOutputs.filter(
      (name) => !manifestRecords.has(name),
    );
    if (missingRequiredOutputs.length > 0) {
      throw new Error(
        `final_artifacts.export_gate.required_outputs are missing from manifest outputs: ${missingRequiredOutputs.join(", ")}`,
      );
    }
  }
  return gate;
}

function validateRunIdentity(runIntake, review) {
  if (!isObject(runIntake)) return;
  if (runIntake.schema_version != null && runIntake.schema_version !== CONTRACT_VERSION) {
    throw new Error(`run_intake.schema_version must be "${CONTRACT_VERSION}"`);
  }
  if (runIntake.run_id != null && runIntake.run_id !== review.run_id) {
    throw new Error("run_intake.run_id must match review_payload.run_id");
  }
  if (runIntake.plugin != null && runIntake.plugin !== PLUGIN_NAME) {
    throw new Error(`run_intake.plugin must be ${PLUGIN_NAME}`);
  }
  if (runIntake.workflow != null && runIntake.workflow !== review.workflow) {
    throw new Error("run_intake.workflow must match review_payload.workflow");
  }
}

function validateFinalIdentity(finalArtifacts, review) {
  if (!isObject(finalArtifacts)) return;
  if (finalArtifacts.schema_version != null && finalArtifacts.schema_version !== CONTRACT_VERSION) {
    throw new Error(`final_artifacts.schema_version must be "${CONTRACT_VERSION}"`);
  }
  if (
    finalArtifacts.contract_version != null
    && finalArtifacts.contract_version !== CONTRACT_VERSION
  ) {
    throw new Error(`final_artifacts.contract_version must be "${CONTRACT_VERSION}"`);
  }
  if (finalArtifacts.plugin != null && finalArtifacts.plugin !== PLUGIN_NAME) {
    throw new Error(`final_artifacts.plugin must be ${PLUGIN_NAME}`);
  }
  if (finalArtifacts.workflow != null && finalArtifacts.workflow !== review.workflow) {
    throw new Error("final_artifacts.workflow must match review_payload.workflow");
  }
  if (finalArtifacts.run_id != null && finalArtifacts.run_id !== review.run_id) {
    throw new Error("final_artifacts.run_id must match review_payload.run_id");
  }
  if (
    finalArtifacts.status != null
    && !FINAL_ARTIFACT_STATUSES.has(finalArtifacts.status)
  ) {
    throw new Error(
      `final_artifacts.status is not supported; expected one of ${[...FINAL_ARTIFACT_STATUSES].join(", ")}`,
    );
  }
  if (
    finalArtifacts.review_status != null
    && !FINAL_ARTIFACT_STATUSES.has(finalArtifacts.review_status)
    && !UI_DECISION_STATUSES.has(finalArtifacts.review_status)
  ) {
    throw new Error("final_artifacts.review_status is not supported");
  }
  validateExportGate(finalArtifacts.export_gate, review);
  validateBoundaryFlags(finalArtifacts, "final_artifacts");
}

function validateUiDecisionIdentity(uiDecisions, review) {
  if (!isObject(uiDecisions)) return;
  if (uiDecisions.schema_version != null && uiDecisions.schema_version !== CONTRACT_VERSION) {
    throw new Error(`ui_decisions.schema_version must be "${CONTRACT_VERSION}"`);
  }
  if (
    uiDecisions.contract_version != null
    && uiDecisions.contract_version !== CONTRACT_VERSION
  ) {
    throw new Error(`ui_decisions.contract_version must be "${CONTRACT_VERSION}"`);
  }
  if (uiDecisions.plugin != null && uiDecisions.plugin !== PLUGIN_NAME) {
    throw new Error(`ui_decisions.plugin must be ${PLUGIN_NAME}`);
  }
  if (uiDecisions.workflow != null && uiDecisions.workflow !== review.workflow) {
    throw new Error("ui_decisions.workflow must match review_payload.workflow");
  }
  if (uiDecisions.run_id != null && uiDecisions.run_id !== review.run_id) {
    throw new Error("ui_decisions.run_id must match review_payload.run_id");
  }
  if (
    uiDecisions.review_revision != null
    && uiDecisions.review_revision !== review.review_revision
  ) {
    throw new Error("ui_decisions.review_revision must match review_payload.review_revision");
  }
  if (uiDecisions.status != null && !UI_DECISION_STATUSES.has(uiDecisions.status)) {
    throw new Error("ui_decisions.status is not supported");
  }
  if (uiDecisions.reviewer != null) {
    assertSessionSafeText(uiDecisions.reviewer, "ui_decisions.reviewer", 300);
  }
}

function validateReviewMetadata(review) {
  assertAllowedKeys(review, REVIEW_FIELDS, "review_payload");
  if (review.contract_version !== CONTRACT_VERSION) {
    throw new Error(`review_payload.contract_version must be "${CONTRACT_VERSION}"`);
  }
  if (review.client_reference != null) {
    safeIdentifier(review.client_reference, "review_payload.client_reference");
  }
  if (review.generated_at != null) {
    const generatedAt = requireText(review.generated_at, "review_payload.generated_at", 64);
    if (Number.isNaN(Date.parse(generatedAt)) || !/[zZ]|[+-]\d{2}:\d{2}$/.test(generatedAt)) {
      throw new Error("review_payload.generated_at must be an ISO-8601 timestamp with timezone");
    }
  }
  validateTemporalValidityRecord(review.temporal_validity);
  if (review.source_paths != null) {
    if (!Array.isArray(review.source_paths) || review.source_paths.length !== 0) {
      throw new Error("review_payload.source_paths must be an empty array");
    }
  }
  if (review.columns != null) {
    const expected = ["title", "status", "source_ids", "data"];
    if (!Array.isArray(review.columns) || canonicalJson(review.columns) !== canonicalJson(expected)) {
      throw new Error(`review_payload.columns must be ${expected.join(", ")}`);
    }
  }
  if (review.allowed_actions != null) {
    if (!Array.isArray(review.allowed_actions)) {
      throw new Error("review_payload.allowed_actions must be an array");
    }
    const supplied = new Set(review.allowed_actions);
    if (
      supplied.size !== ALLOWED_ACTIONS.size
      || [...ALLOWED_ACTIONS].some((action) => !supplied.has(action))
    ) {
      throw new Error("review_payload.allowed_actions must contain the complete action vocabulary");
    }
  }
  if (review.privacy != null) {
    if (!isObject(review.privacy)) throw new Error("review_payload.privacy must be an object");
    assertAllowedKeys(review.privacy, new Set(["classification", "excluded"]), "review_payload.privacy");
    if (review.privacy.classification !== "private_professional_review") {
      throw new Error("review_payload.privacy.classification must be private_professional_review");
    }
    if (!Array.isArray(review.privacy.excluded) || review.privacy.excluded.length > 20) {
      throw new Error("review_payload.privacy.excluded must be a bounded array");
    }
    review.privacy.excluded.forEach((entry, index) => {
      const text = assertSessionSafeText(entry, `review_payload.privacy.excluded[${index}]`, 120);
      if (!text) throw new Error(`review_payload.privacy.excluded[${index}] must be non-empty`);
    });
  }
  if (review.summary != null) {
    if (!isObject(review.summary)) throw new Error("review_payload.summary must be an object");
    const summaryFields = new Set([
      "review_item_count",
      "missing_information_count",
      "aml_status",
      "document_count",
      "jurisdiction",
      "country_pack",
      "language",
    ]);
    assertAllowedKeys(review.summary, summaryFields, "review_payload.summary");
    for (const key of ["review_item_count", "missing_information_count", "document_count"]) {
      if (review.summary[key] != null) {
        validateOptionalNonNegativeInteger(review.summary[key], `review_payload.summary.${key}`);
      }
    }
    for (const key of [
      "aml_status",
      "jurisdiction",
      "country_pack",
      "language",
    ]) {
      if (review.summary[key] != null) safeIdentifier(review.summary[key], `review_payload.summary.${key}`);
    }
  }
}

function publicRunIntake(value, review) {
  if (!isObject(value)) return null;
  return {
    schema_version: value.schema_version || null,
    plugin: value.plugin || PLUGIN_NAME,
    workflow: value.workflow || PLUGIN_NAME,
    run_id: review.run_id,
    reference_date: value.reference_date || null,
    language: ["it", "en", "fr", "de", "es"].includes(value.language)
      ? value.language
      : null,
    status: value.status || null,
  };
}

function publicUiDecisions(value, review) {
  if (!isObject(value)) return null;
  const itemIds = new Set(review.items.map((item) => item.id));
  const decisions = Array.isArray(value.decisions)
    ? value.decisions
        .filter(
          (decision) =>
            isObject(decision)
            && itemIds.has(decision.item_id)
            && ALLOWED_ACTIONS.has(decision.action),
        )
        .slice(0, review.items.length)
        .map((decision) => ({
          item_id: decision.item_id,
          action: decision.action,
          status: ACTION_STATUSES[decision.action],
        }))
    : [];
  return {
    schema_version: review.schema_version,
    contract_version: review.contract_version,
    plugin: PLUGIN_NAME,
    workflow: PLUGIN_NAME,
    run_id: review.run_id,
    review_revision: review.review_revision,
    decision_revision:
      Number.isInteger(value.decision_revision) && value.decision_revision >= 0
        ? value.decision_revision
        : 0,
    review_payload_sha256:
      typeof value.review_payload_sha256 === "string"
        ? value.review_payload_sha256
        : null,
    decisions,
    decision_count: decisions.length,
    item_count: review.items.length,
    status: typeof value.status === "string" ? value.status : "pending_review",
    reviewer:
      typeof value.reviewer === "string"
        ? assertSessionSafeText(value.reviewer, "ui_decisions.reviewer", 300)
        : null,
  };
}

function publicFinalArtifacts(value, review) {
  if (!isObject(value)) return null;
  const outputs = Array.isArray(value.outputs)
    ? value.outputs
        .filter((record) => {
          if (!isObject(record) || typeof record.path !== "string") return false;
          const fileName = record.path.trim();
          return fileName
            && fileName.length <= 240
            && fileName !== "."
            && fileName !== ".."
            && path.basename(fileName) === fileName;
        })
        .slice(0, MAX_ITEMS)
        .map((record) => ({
          path: record.path.trim(),
          kind: typeof record.kind === "string" ? record.kind.slice(0, 80) : "file",
          status:
            typeof record.status === "string" ? record.status.slice(0, 120) : null,
          size_bytes:
            Number.isInteger(record.size_bytes) && record.size_bytes >= 0
              ? record.size_bytes
              : null,
          sha256:
            typeof record.sha256 === "string" && LOWERCASE_SHA256_RE.test(record.sha256)
              ? record.sha256
              : null,
        }))
    : [];
  return {
    schema_version: value.schema_version || review.schema_version,
    contract_version: value.contract_version || review.contract_version,
    plugin: PLUGIN_NAME,
    workflow: PLUGIN_NAME,
    run_id: review.run_id,
    status: typeof value.status === "string" ? value.status : null,
    review_payload_sha256:
      typeof value.review_payload_sha256 === "string" ? value.review_payload_sha256 : null,
    professional_review_required: true,
    signature_performed: false,
    client_communication_sent: false,
    relationship_activation_performed: false,
    blocker_count: Array.isArray(value.blockers) ? Math.min(value.blockers.length, MAX_ITEMS) : 0,
    output_count: outputs.length,
    outputs,
    export_gate: isObject(value.export_gate)
      ? {
          status: value.export_gate.status || null,
          relationship_ready: value.export_gate.relationship_ready === true,
          domain_blocker_count: Array.isArray(value.export_gate.domain_blockers)
            ? value.export_gate.domain_blockers.length
            : 0,
          review_blocker_count: Array.isArray(value.export_gate.review_blockers)
            ? value.export_gate.review_blockers.length
            : 0,
          artifact_blocker_count: Array.isArray(value.export_gate.artifact_blockers)
            ? value.export_gate.artifact_blockers.length
            : 0,
          marketing_only_blocker_count: Array.isArray(value.export_gate.marketing_only_blockers)
            ? value.export_gate.marketing_only_blockers.length
            : 0,
          required_output_count: Array.isArray(value.export_gate.required_outputs)
            ? value.export_gate.required_outputs.length
            : 0,
        }
      : null,
    review_application: isObject(value.review_application)
      ? {
          status: value.review_application.status || null,
          decision_count: value.review_application.decision_count || 0,
          item_count: value.review_application.item_count || review.items.length,
          blocker_count: value.review_application.blocker_count || 0,
          pending_count: value.review_application.pending_count || 0,
          domain_artifacts_modified: false,
        }
      : null,
  };
}

function validateReview(input) {
  if (!isObject(input)) throw new Error("tool arguments must be an object");
  const review = input.review_payload;
  if (!isObject(review)) throw new Error("review_payload must be an object");
  if (review.schema_version !== CONTRACT_VERSION) {
    throw new Error(`review_payload.schema_version must be "${CONTRACT_VERSION}"`);
  }
  if (review.plugin !== PLUGIN_NAME || review.workflow !== PLUGIN_NAME) {
    throw new Error(`review_payload.plugin and workflow must be ${PLUGIN_NAME}`);
  }
  validateReviewMetadata(review);
  safeIdentifier(review.run_id, "review_payload.run_id");
  if (!Number.isInteger(review.review_revision) || review.review_revision < 1) {
    throw new Error("review_payload.review_revision must be a positive integer");
  }
  requireText(review.review_type, "review_payload.review_type", 120);
  requireText(review.status, "review_payload.status", 120);
  if (!REVIEW_STATUSES.has(review.status)) {
    throw new Error(
      `review_payload.status is not supported; expected one of ${[...REVIEW_STATUSES].join(", ")}`,
    );
  }
  if (!Array.isArray(review.items)) throw new Error("review_payload.items must be an array");
  if (review.items.length > MAX_ITEMS) {
    throw new Error(`review_payload.items exceeds ${MAX_ITEMS} items`);
  }
  if (!Number.isInteger(review.item_count) || review.item_count !== review.items.length) {
    throw new Error("review_payload.item_count must equal review_payload.items.length");
  }
  validateSourceArtifacts(review.source_artifacts);
  validateBasisHashes(review.basis_hashes);
  const seenIds = new Set();
  review.items.forEach((item, index) => validateItem(item, index, seenIds));
  auditSecretBoundaries(review);
  const runIntake = isObject(input.run_intake) ? input.run_intake : null;
  const uiDecisions = isObject(input.ui_decisions) ? input.ui_decisions : null;
  const finalArtifacts = isObject(input.final_artifacts) ? input.final_artifacts : null;
  validateRunIdentity(runIntake, review);
  validateUiDecisionIdentity(uiDecisions, review);
  validateFinalIdentity(finalArtifacts, review);
  validateBoundaryFlags(review, "review_payload");
  const reviewPayloadSha256 = canonicalSha256(review);
  const payload = {
    widget_type: "new_client_review",
    run_intake: publicRunIntake(runIntake, review),
    review_payload: review,
    review_payload_sha256: reviewPayloadSha256,
    ui_decisions: publicUiDecisions(uiDecisions, review),
    final_artifacts: publicFinalArtifacts(finalArtifacts, review),
    decision_policy: {
      save_tool: TOOL_NAMES.save,
      apply_tool: TOOL_NAMES.apply,
      can_persist: Boolean(
        isObject(runIntake)
        && typeof runIntake.output_dir === "string"
        && runIntake.output_dir.trim(),
      ),
      edits_are_review_instructions: true,
      domain_artifacts_modified: false,
      professional_review_required: true,
      fallback: "copy_or_download_json",
    },
  };
  if (payloadBytes(payload) > MAX_PAYLOAD_BYTES) {
    throw new Error(`new client widget payload exceeds ${MAX_PAYLOAD_BYTES} bytes`);
  }
  return payload;
}

function normalizeRequestedDocuments(value, field) {
  if (value == null) return [];
  if (!Array.isArray(value)) throw new Error(`${field} must be an array when provided`);
  if (value.length > MAX_REQUESTED_DOCUMENTS) {
    throw new Error(`${field} exceeds ${MAX_REQUESTED_DOCUMENTS} entries`);
  }
  const seen = new Set();
  return value.map((entry, index) => {
    const text = assertSessionSafeText(entry, `${field}[${index}]`, 300);
    if (!text) throw new Error(`${field}[${index}] must be a non-empty string`);
    if (seen.has(text)) throw new Error(`${field} contains duplicate document request: ${text}`);
    seen.add(text);
    return text;
  });
}

function normalizeDecision(decision, index, itemById, seenIds, decidedAt) {
  const root = `decisions[${index}]`;
  if (!isObject(decision)) throw new Error(`${root} must be an object`);
  const itemId = safeIdentifier(decision.item_id, `${root}.item_id`);
  if (seenIds.has(itemId)) throw new Error(`decisions contains duplicate item_id: ${itemId}`);
  seenIds.add(itemId);
  const item = itemById.get(itemId);
  if (!item) throw new Error(`${root}.item_id is not in review_payload.items: ${itemId}`);
  const action = requireText(decision.action, `${root}.action`, 80);
  if (!ALLOWED_ACTIONS.has(action)) throw new Error(`${root}.action is not supported: ${action}`);
  if (!item.allowed_actions.includes(action)) {
    throw new Error(`${root}.action is not allowed for item ${itemId}: ${action}`);
  }
  const reviewerNote = assertSessionSafeText(
    decision.reviewer_note ?? decision.note,
    `${root}.reviewer_note`,
  );
  const editValue = assertSessionSafeText(
    decision.edit_value ?? decision.user_text,
    `${root}.edit_value`,
  );
  if (action === "edit" && !editValue) {
    throw new Error(`${root}.edit_value is required when action is edit`);
  }
  if (action === "edit" && SEMANTIC_ITEM_TYPES.has(item.item_type) && !reviewerNote) {
    throw new Error(`${root}.reviewer_note is required for semantic edits`);
  }
  const requestedDocuments = normalizeRequestedDocuments(
    decision.requested_documents,
    `${root}.requested_documents`,
  );
  if (action === "request_more_documents" && requestedDocuments.length === 0) {
    throw new Error(`${root}.requested_documents is required for document requests`);
  }
  const normalized = {
    item_id: itemId,
    item_type: item.item_type,
    title: item.title,
    action,
    status: ACTION_STATUSES[action],
    decided_at: decidedAt,
  };
  if (reviewerNote) normalized.reviewer_note = reviewerNote;
  if (editValue) normalized.edit_value = editValue;
  if (requestedDocuments.length) normalized.requested_documents = requestedDocuments;
  return normalized;
}

function mergeSavedDecisionDetails(decision, index, savedDecisionById) {
  const root = `decisions[${index}]`;
  if (decision.reuse_saved_details == null || decision.reuse_saved_details === false) {
    return decision;
  }
  if (decision.reuse_saved_details !== true) {
    throw new Error(`${root}.reuse_saved_details must be boolean when provided`);
  }
  for (const field of ["reviewer_note", "edit_value", "requested_documents"]) {
    if (Object.prototype.hasOwnProperty.call(decision, field)) {
      throw new Error(
        `${root}.${field} cannot be supplied when reuse_saved_details is true`,
      );
    }
  }
  const savedDecision = savedDecisionById.get(decision.item_id);
  if (!savedDecision || savedDecision.action !== decision.action) {
    throw new Error(
      `${root}.reuse_saved_details requires a saved decision with the same item_id and action`,
    );
  }
  return {
    ...decision,
    ...(savedDecision.reviewer_note
      ? { reviewer_note: savedDecision.reviewer_note }
      : {}),
    ...(savedDecision.edit_value ? { edit_value: savedDecision.edit_value } : {}),
    ...(Array.isArray(savedDecision.requested_documents)
      && savedDecision.requested_documents.length
      ? { requested_documents: [...savedDecision.requested_documents] }
      : {}),
  };
}

function buildUiDecisions(input, payload = validateReview(input), savedUiDecisions = null) {
  const review = payload.review_payload;
  if (!Array.isArray(input.decisions)) throw new Error("decisions must be an array");
  if (input.decisions.length > review.items.length) {
    throw new Error("decisions cannot exceed review_payload.items.length");
  }
  if (payloadBytes(input.decisions) > MAX_PAYLOAD_BYTES) {
    throw new Error(`decisions payload exceeds ${MAX_PAYLOAD_BYTES} bytes`);
  }
  const itemById = new Map(review.items.map((item) => [item.id, item]));
  const savedDecisionById = new Map(
    Array.isArray(savedUiDecisions?.decisions)
      ? savedUiDecisions.decisions.map((decision) => [decision.item_id, decision])
      : [],
  );
  const seenIds = new Set();
  const decidedAt = new Date().toISOString();
  const decisions = input.decisions.map((decision, index) => {
    if (!isObject(decision)) throw new Error(`decisions[${index}] must be an object`);
    const merged = mergeSavedDecisionDetails(decision, index, savedDecisionById);
    return normalizeDecision(merged, index, itemById, seenIds, decidedAt);
  });
  const decisionSource = input.decision_source
    ? safeIdentifier(input.decision_source, "decision_source")
    : "mcp_widget";
  const reviewer = input.reviewer ? assertSessionSafeText(input.reviewer, "reviewer", 300) : "";
  const status =
    decisions.length === 0
      ? "pending_review"
      : decisions.length === review.items.length
        ? "reviewed"
        : "partial_review";
  const uiDecisions = {
    schema_version: review.schema_version,
    contract_version: review.contract_version,
    plugin: PLUGIN_NAME,
    workflow: PLUGIN_NAME,
    run_id: review.run_id,
    review_revision: review.review_revision,
    decision_revision: 0,
    decided_at: decisions.length ? decidedAt : null,
    decision_source: decisionSource,
    review_payload_path: "review_payload.json",
    review_payload_sha256: payload.review_payload_sha256,
    decisions,
    decision_count: decisions.length,
    item_count: review.items.length,
    status,
    professional_review_required: true,
    signature_performed: false,
    client_communication_sent: false,
    relationship_activation_performed: false,
  };
  if (reviewer) uiDecisions.reviewer = reviewer;
  if (payloadBytes(uiDecisions) > MAX_PAYLOAD_BYTES) {
    throw new Error(`ui_decisions payload exceeds ${MAX_PAYLOAD_BYTES} bytes`);
  }
  return { payload, uiDecisions };
}

function currentDecisionRevision(value) {
  if (value == null) return 0;
  if (!isObject(value)) throw new Error("ui_decisions.json must contain an object");
  const revision = value.decision_revision ?? 0;
  if (!Number.isInteger(revision) || revision < 0) {
    throw new Error("ui_decisions.decision_revision must be a non-negative integer");
  }
  return revision;
}

function expectedDecisionRevision(input) {
  const explicit = input.expected_decision_revision;
  const embedded = isObject(input.ui_decisions) ? input.ui_decisions.decision_revision : null;
  const revision = explicit ?? embedded ?? 0;
  if (!Number.isInteger(revision) || revision < 0) {
    throw new Error("expected_decision_revision must be a non-negative integer");
  }
  return revision;
}

function pruneExpiredPersistenceContexts(now = Date.now()) {
  for (const [token, context] of PERSISTENCE_CONTEXTS.entries()) {
    if (context.expiresAt <= now) PERSISTENCE_CONTEXTS.delete(token);
  }
}

function reservePersistenceContextSlot() {
  pruneExpiredPersistenceContexts();
  while (PERSISTENCE_CONTEXTS.size >= MAX_PERSISTENCE_CONTEXTS) {
    const oldest = PERSISTENCE_CONTEXTS.keys().next().value;
    if (oldest == null) break;
    PERSISTENCE_CONTEXTS.delete(oldest);
  }
}

function validatePersistentState(outputDir, payload) {
  verifyStoredReview(
    outputDir,
    payload.review_payload,
    payload.review_payload_sha256,
  );
  const currentFinal = readJsonIfPresent(
    path.join(outputDir, "final_artifacts.json"),
  );
  if (!currentFinal) {
    throw new Error("run_intake.output_dir must contain final_artifacts.json");
  }
  validateCurrentFinalArtifacts(
    currentFinal,
    payload.review_payload,
    payload.review_payload_sha256,
  );
  const manifestRecords = verifyLocalArtifactBindings(
    outputDir,
    payload.review_payload,
    payload.review_payload_sha256,
    currentFinal,
  );
  const currentUiDecisions = readJsonIfPresent(
    path.join(outputDir, "ui_decisions.json"),
  );
  if (currentUiDecisions) {
    validateUiDecisionIdentity(currentUiDecisions, payload.review_payload);
  }
  return { currentFinal, currentUiDecisions, manifestRecords };
}

function issuePersistenceToken(input, payload) {
  const outputDir = resolveOutputDir(input);
  if (!outputDir) return null;
  validatePersistentState(outputDir, payload);
  reservePersistenceContextSlot();
  const token = crypto.randomBytes(32).toString("base64url");
  PERSISTENCE_CONTEXTS.set(token, {
    outputReference: input.run_intake.output_dir,
    pathReference: input.run_intake.path_reference,
    runId: payload.review_payload.run_id,
    reviewHash: payload.review_payload_sha256,
    expiresAt: Date.now() + PERSISTENCE_CONTEXT_TTL_MS,
  });
  return token;
}

function resolvePersistentOutputDir(input, payload) {
  const rawToken = input.persistence_token;
  if (rawToken == null || rawToken === "") return resolveOutputDir(input);
  if (typeof rawToken !== "string" || !PERSISTENCE_TOKEN_RE.test(rawToken)) {
    throw new Error("persistence_token has an invalid format");
  }
  pruneExpiredPersistenceContexts();
  const context = PERSISTENCE_CONTEXTS.get(rawToken);
  if (!context || context.expiresAt <= Date.now()) {
    throw new Error("persistence_token is unknown or expired; render the review again");
  }
  if (
    context.runId !== payload.review_payload.run_id
    || context.reviewHash !== payload.review_payload_sha256
  ) {
    throw new Error("persistence_token does not match this review run");
  }
  const outputDir = resolveOutputDir({
    ...input,
    run_intake: {
      output_dir: context.outputReference,
      path_reference: context.pathReference,
    },
  });
  const directOutputDir = resolveOutputDir(input);
  if (directOutputDir && directOutputDir !== outputDir) {
    throw new Error("persistence_token and run_intake.output_dir do not match");
  }
  return outputDir;
}

function preparePersistentRun(input, payload) {
  const outputDir = resolvePersistentOutputDir(input, payload);
  if (!outputDir) {
    return {
      outputDir: null,
      currentFinal: {},
      currentUiDecisions: null,
      manifestRecords: null,
      nextDecisionRevision: 1,
    };
  }
  const { currentFinal, currentUiDecisions, manifestRecords } =
    validatePersistentState(outputDir, payload);
  const currentRevision = currentDecisionRevision(currentUiDecisions);
  const expectedRevision = expectedDecisionRevision(input);
  if (expectedRevision !== currentRevision) {
    throw new Error(
      `stale decision revision: expected ${expectedRevision}, current ${currentRevision}`,
    );
  }
  return {
    outputDir,
    currentFinal,
    currentUiDecisions,
    manifestRecords,
    nextDecisionRevision: currentRevision + 1,
  };
}

function findRepositoryRoot(start) {
  let current = path.resolve(start);
  while (true) {
    if (fs.existsSync(path.join(current, ".git"))) return current;
    const parent = path.dirname(current);
    if (parent === current) return null;
    current = parent;
  }
}

function protectedOutputRoots() {
  const roots = new Set([PLUGIN_ROOT]);
  const repositoryRoot = findRepositoryRoot(PLUGIN_ROOT);
  if (repositoryRoot) roots.add(repositoryRoot);
  const parent = path.dirname(PLUGIN_ROOT);
  if (path.basename(parent) === "modules") roots.add(path.dirname(parent));
  return [...roots].map((entry) => fs.realpathSync(entry));
}

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

function resolveOutputDir(input) {
  const raw = isObject(input.run_intake) ? input.run_intake.output_dir : null;
  if (typeof raw !== "string" || !raw.trim()) return null;
  let requested;
  if (path.isAbsolute(raw.trim())) {
    requested = path.resolve(raw.trim());
  } else {
    if (input.run_intake.path_reference !== "run_root_relative") {
      throw new Error("relative run output requires path_reference=run_root_relative");
    }
    const contextValue =
      typeof input.client_engagement === "string"
        ? input.client_engagement.trim()
        : "";
    if (!contextValue || !path.isAbsolute(contextValue)) {
      throw new Error(
        "client_engagement is required to resolve the portable run output",
      );
    }
    const contextPath = path.resolve(contextValue);
    const contextStat = fs.lstatSync(contextPath);
    if (!contextStat.isFile() || contextStat.isSymbolicLink()) {
      throw new Error("client_engagement must identify a regular context file");
    }
    if (fs.realpathSync(contextPath) !== contextPath) {
      throw new Error("client_engagement may not traverse symlinks");
    }
    const runRoot = path.dirname(contextPath);
    requested = path.resolve(runRoot, raw.trim());
    if (requested === runRoot || !isInsideOrEqual(requested, runRoot)) {
      throw new Error("run output reference leaves the customer run");
    }
  }
  if (!fs.existsSync(requested)) {
    throw new Error("run_intake.output_dir must identify an existing directory");
  }
  const stat = fs.lstatSync(requested);
  if (!stat.isDirectory() || stat.isSymbolicLink()) {
    throw new Error("run_intake.output_dir must be a regular directory without symlinks");
  }
  const resolved = fs.realpathSync(requested);
  if (resolved !== requested) {
    throw new Error("run_intake.output_dir may not traverse symlinks");
  }
  const protectedRoot = protectedOutputRoots().find((root) => isInsideOrEqual(resolved, root));
  if (protectedRoot) {
    throw new Error(
      "run_intake.output_dir must be outside the plugin package and source repository",
    );
  }
  if (process.platform !== "win32" && (stat.mode & 0o077) !== 0) {
    throw new Error("run_intake.output_dir must be owner-only (mode 0700)");
  }
  return resolved;
}

function readJsonIfPresent(filePath) {
  if (!filePath || !fs.existsSync(filePath)) return null;
  const stat = fs.lstatSync(filePath);
  if (!stat.isFile() || stat.isSymbolicLink()) {
    throw new Error(`${path.basename(filePath)} must be a regular JSON file`);
  }
  if (stat.size > MAX_LOCAL_JSON_BYTES) {
    throw new Error(`${path.basename(filePath)} exceeds ${MAX_LOCAL_JSON_BYTES} bytes`);
  }
  if (process.platform !== "win32" && (stat.mode & 0o077) !== 0) {
    throw new Error(`${path.basename(filePath)} must be owner-only (mode 0600)`);
  }
  const value = JSON.parse(fs.readFileSync(filePath, "utf8"));
  if (!isObject(value)) throw new Error(`${path.basename(filePath)} must contain a JSON object`);
  return value;
}

function verifyStoredReview(outputDir, review, expectedHash) {
  if (!outputDir) return;
  const storedPath = path.join(outputDir, "review_payload.json");
  const stored = readJsonIfPresent(storedPath);
  if (!stored) {
    throw new Error("run_intake.output_dir must contain the reviewed review_payload.json");
  }
  const storedHash = canonicalSha256(stored);
  if (storedHash !== expectedHash || canonicalJson(stored) !== canonicalJson(review)) {
    throw new Error("stored review_payload.json does not match the reviewed payload hash");
  }
}

function manifestOutputRecords(outputDir, finalArtifacts) {
  if (!Array.isArray(finalArtifacts.outputs)) {
    throw new Error("final_artifacts.outputs must be an array");
  }
  const records = new Map();
  for (const [index, record] of finalArtifacts.outputs.entries()) {
    const field = `final_artifacts.outputs[${index}]`;
    if (!isObject(record)) throw new Error(`${field} must be an object`);
    const fileName = validateRelativeBasename(record.path, `${field}.path`);
    if (records.has(fileName)) throw new Error(`final_artifacts.outputs contains duplicate path: ${fileName}`);
    if (typeof record.sha256 !== "string" || !LOWERCASE_SHA256_RE.test(record.sha256)) {
      throw new Error(`${field}.sha256 must be a lowercase SHA-256 digest`);
    }
    const filePath = path.join(outputDir, fileName);
    if (!fs.existsSync(filePath)) throw new Error(`manifest output is missing: ${fileName}`);
    const stat = fs.lstatSync(filePath);
    if (!stat.isFile() || stat.isSymbolicLink()) {
      throw new Error(`manifest output must be a regular file without symlinks: ${fileName}`);
    }
    if (process.platform !== "win32" && (stat.mode & 0o077) !== 0) {
      throw new Error(`manifest output must be owner-only (mode 0600): ${fileName}`);
    }
    if (sha256File(filePath) !== record.sha256) {
      throw new Error(`manifest output hash mismatch: ${fileName}`);
    }
    records.set(fileName, record);
  }
  const packageHash = outputPackageHash([...records.values()]);
  if (finalArtifacts.package_hash !== packageHash) {
    throw new Error("final_artifacts.package_hash does not match manifest outputs");
  }
  return records;
}

function deriveTemporalValidity(review, facts, sources, documents) {
  // These comparisons enforce explicit dates only. They do not infer source
  // authority, legal applicability, or an appropriate semantic review cadence.
  for (const [field, artifact, key] of [
    ["case_facts_validated.generated_at", facts, "generated_at"],
    ["source_registry.packaged_at", sources, "packaged_at"],
    ["document_plan.generated_at", documents, "generated_at"],
  ]) {
    if (!isObject(artifact) || artifact[key] !== review.generated_at) {
      throw new Error(`${field} must match review_payload.generated_at`);
    }
  }
  const evaluatedOn = validateDateOnly(
    review.generated_at.slice(0, 10),
    "review_payload.generated_at date",
  );
  const deadlines = new Map(TEMPORAL_DEADLINE_KINDS.map((kind) => [kind, []]));
  const addDeadline = (kind, raw, field) => {
    if (raw == null) return;
    deadlines.get(kind).push(validateDateOnly(raw, field));
  };

  if (!isObject(sources.currentness)) {
    throw new Error("source_registry.currentness must be an object");
  }
  if (sources.currentness.status !== "verified_current") {
    throw new Error("source_registry.currentness.status must be verified_current");
  }
  addDeadline(
    "authoritative_source_registry_review_by",
    sources.currentness.review_by,
    "source_registry.currentness.review_by",
  );

  if (!Array.isArray(facts.evidence_register)) {
    throw new Error("case_facts_validated.evidence_register must be an array");
  }
  facts.evidence_register.forEach((evidence, index) => {
    if (!isObject(evidence)) {
      throw new Error(`case_facts_validated.evidence_register[${index}] must be an object`);
    }
    addDeadline(
      "evidence_expires_on",
      evidence.expires_on,
      `case_facts_validated.evidence_register[${index}].expires_on`,
    );
  });

  if (!isObject(facts.party_identity_document)) {
    throw new Error("case_facts_validated.party_identity_document must be an object");
  }
  addDeadline(
    "primary_identity_expires_on",
    facts.party_identity_document.expires_on,
    "case_facts_validated.party_identity_document.expires_on",
  );

  if (!Array.isArray(facts.representatives)) {
    throw new Error("case_facts_validated.representatives must be an array");
  }
  facts.representatives.forEach((representative, index) => {
    if (!isObject(representative) || !isObject(representative.identity_document)) {
      throw new Error(
        `case_facts_validated.representatives[${index}].identity_document must be an object`,
      );
    }
    addDeadline(
      "representative_identity_expires_on",
      representative.identity_document.expires_on,
      `case_facts_validated.representatives[${index}].identity_document.expires_on`,
    );
  });

  if (!Array.isArray(facts.beneficial_owners)) {
    throw new Error("case_facts_validated.beneficial_owners must be an array");
  }
  facts.beneficial_owners.forEach((owner, index) => {
    if (!isObject(owner) || !isObject(owner.identity_document)) {
      throw new Error(
        `case_facts_validated.beneficial_owners[${index}].identity_document must be an object`,
      );
    }
    addDeadline(
      "beneficial_owner_identity_expires_on",
      owner.identity_document.expires_on,
      `case_facts_validated.beneficial_owners[${index}].identity_document.expires_on`,
    );
  });

  if (!Array.isArray(documents.documents)) {
    throw new Error("document_plan.documents must be an array");
  }
  documents.documents.forEach((document, index) => {
    if (!isObject(document)) {
      throw new Error(`document_plan.documents[${index}] must be an object`);
    }
    const template = document.template_reference;
    if (template == null) return;
    if (!isObject(template)) {
      throw new Error(`document_plan.documents[${index}].template_reference must be an object`);
    }
    addDeadline(
      "template_valid_until",
      template.valid_until,
      `document_plan.documents[${index}].template_reference.valid_until`,
    );
    addDeadline(
      "template_review_due_on",
      template.review_due_on,
      `document_plan.documents[${index}].template_reference.review_due_on`,
    );
  });

  const summaries = [];
  const allDeadlines = [];
  for (const kind of TEMPORAL_DEADLINE_KINDS) {
    const values = deadlines.get(kind);
    if (values.length === 0) continue;
    const earliest = [...values].sort()[0];
    summaries.push({ kind, count: values.length, valid_through: earliest });
    allDeadlines.push(...values);
  }
  if (allDeadlines.length === 0) {
    throw new Error("no material temporal deadline was recorded");
  }
  const derived = {
    policy: TEMPORAL_VALIDITY_POLICY,
    evaluated_on: evaluatedOn,
    valid_through: [...allDeadlines].sort()[0],
    deadline_count: allDeadlines.length,
    deadlines: summaries,
    source_currentness_status: sources.currentness.status,
    apply_rule: TEMPORAL_APPLY_RULE,
  };
  validateTemporalValidityRecord(derived);
  return derived;
}

function assertTemporalValidityAtApply(temporalValidity) {
  const record = validateTemporalValidityRecord(temporalValidity);
  const systemUtcDate = new Date().toISOString().slice(0, 10);
  // Expiry dates are inclusive: Apply is allowed through valid_through and
  // fails on the following UTC calendar day.
  if (systemUtcDate > record.valid_through) {
    throw new Error(
      `New Client temporal validity expired on ${record.valid_through}; regenerate the package before Apply`,
    );
  }
}

function verifyLocalArtifactBindings(outputDir, review, reviewHash, finalArtifacts) {
  const manifestRecords = manifestOutputRecords(outputDir, finalArtifacts);
  validateExportGate(finalArtifacts.export_gate, review, manifestRecords);
  for (const required of ["run_intake.json", "review_payload.json", ...Object.values(REQUIRED_SOURCE_ARTIFACTS)]) {
    if (!manifestRecords.has(required)) {
      throw new Error(`final_artifacts.outputs is missing required binding: ${required}`);
    }
  }
  const storedReviewPath = path.join(outputDir, "review_payload.json");
  if (canonicalSha256(readJsonIfPresent(storedReviewPath)) !== reviewHash) {
    throw new Error("review_payload.json canonical hash does not match the reviewed payload");
  }
  if (
    finalArtifacts.review_payload_sha256 != null
    && finalArtifacts.review_payload_sha256 !== reviewHash
  ) {
    throw new Error("final_artifacts.review_payload_sha256 does not match the reviewed payload");
  }

  const canonicalArtifactHashes = {};
  for (const [key, expectedPath] of Object.entries(REQUIRED_SOURCE_ARTIFACTS)) {
    const reference = review.source_artifacts[key];
    if (reference.path !== expectedPath) {
      throw new Error(`review_payload.source_artifacts.${key}.path must be ${expectedPath}`);
    }
    const artifact = readJsonIfPresent(path.join(outputDir, expectedPath));
    if (!artifact) throw new Error(`required source artifact is missing: ${expectedPath}`);
    const digest = canonicalSha256(artifact);
    if (digest !== reference.sha256) {
      throw new Error(`canonical source artifact hash mismatch: ${expectedPath}`);
    }
    canonicalArtifactHashes[key] = digest;
  }

  const facts = readJsonIfPresent(path.join(outputDir, REQUIRED_SOURCE_ARTIFACTS.facts));
  if (
    typeof facts.input_hash !== "string"
    || !LOWERCASE_SHA256_RE.test(facts.input_hash)
    || review.basis_hashes.new_client_input !== facts.input_hash
  ) {
    throw new Error("review_payload.basis_hashes.new_client_input does not match case_facts_validated.json");
  }
  const expectedBasis = {
    aml: canonicalArtifactHashes.aml,
    documents: canonicalArtifactHashes.documents,
    monitoring: canonicalArtifactHashes.monitoring,
    sources: canonicalArtifactHashes.sources,
  };
  for (const [key, digest] of Object.entries(expectedBasis)) {
    if (review.basis_hashes[key] !== digest) {
      throw new Error(`review_payload.basis_hashes.${key} does not match its local artifact`);
    }
  }

  const sources = readJsonIfPresent(
    path.join(outputDir, REQUIRED_SOURCE_ARTIFACTS.sources),
  );
  const documents = readJsonIfPresent(
    path.join(outputDir, REQUIRED_SOURCE_ARTIFACTS.documents),
  );
  const temporalValidity = deriveTemporalValidity(review, facts, sources, documents);
  if (canonicalJson(review.temporal_validity) !== canonicalJson(temporalValidity)) {
    throw new Error(
      "review_payload.temporal_validity does not match the persisted material deadlines",
    );
  }

  const storedRunIntake = readJsonIfPresent(path.join(outputDir, "run_intake.json"));
  const storedOutputReference = String(storedRunIntake.output_dir || "").trim();
  const storedOutputMatches = path.isAbsolute(storedOutputReference)
    ? path.resolve(storedOutputReference) === outputDir
    : storedRunIntake.path_reference === "run_root_relative"
      && storedOutputReference === path.basename(outputDir);
  if (
    storedRunIntake.plugin !== PLUGIN_NAME
    || storedRunIntake.workflow !== review.workflow
    || storedRunIntake.run_id !== review.run_id
    || !storedOutputMatches
  ) {
    throw new Error("stored run_intake.json does not bind this review to the output directory");
  }
  if (
    canonicalJson(storedRunIntake.temporal_validity) !== canonicalJson(temporalValidity)
  ) {
    throw new Error(
      "stored run_intake.json temporal validity does not match the persisted material deadlines",
    );
  }
  if (canonicalJson(finalArtifacts.temporal_validity) !== canonicalJson(temporalValidity)) {
    throw new Error(
      "final_artifacts temporal validity does not match the persisted material deadlines",
    );
  }
  return manifestRecords;
}

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

function artifactRecordForJson(fileName, value, status = "written_reviewed") {
  const serialized = serializeJson(value);
  return {
    path: fileName,
    kind: "json",
    status,
    size_bytes: Buffer.byteLength(serialized, "utf8"),
    sha256: crypto.createHash("sha256").update(serialized, "utf8").digest("hex"),
  };
}

function writeJsonBatchAtomic(outputDir, valuesByName) {
  if (!outputDir) return [];
  const transactionId = crypto.randomUUID();
  const staged = [];
  const backups = [];
  const installed = [];
  const names = Object.keys(valuesByName);
  if (new Set(names).size !== names.length) throw new Error("transaction contains duplicate file names");
  try {
    for (const fileName of names) {
      validateRelativeBasename(fileName, `transaction.${fileName}`);
      const outputPath = path.join(outputDir, fileName);
      if (fs.existsSync(outputPath)) {
        const current = fs.lstatSync(outputPath);
        if (!current.isFile() || current.isSymbolicLink()) {
          throw new Error(`refusing to replace non-regular file: ${fileName}`);
        }
      }
      const temporaryPath = path.join(outputDir, `.${fileName}.${transactionId}.tmp`);
      fs.writeFileSync(temporaryPath, serializeJson(valuesByName[fileName]), {
        encoding: "utf8",
        flag: "wx",
        mode: 0o600,
      });
      staged.push({ fileName, outputPath, temporaryPath });
    }
    for (const entry of staged) {
      if (fs.existsSync(entry.outputPath)) {
        const backupPath = path.join(outputDir, `.${entry.fileName}.${transactionId}.bak`);
        fs.renameSync(entry.outputPath, backupPath);
        backups.push({ ...entry, backupPath });
      }
    }
    for (const entry of staged) {
      fs.renameSync(entry.temporaryPath, entry.outputPath);
      installed.push(entry);
      if (process.platform !== "win32") fs.chmodSync(entry.outputPath, 0o600);
    }
    for (const entry of backups) fs.unlinkSync(entry.backupPath);
    return names;
  } catch (error) {
    for (const entry of [...installed].reverse()) {
      if (fs.existsSync(entry.outputPath)) fs.unlinkSync(entry.outputPath);
    }
    for (const entry of [...backups].reverse()) {
      if (fs.existsSync(entry.backupPath)) fs.renameSync(entry.backupPath, entry.outputPath);
    }
    throw error;
  } finally {
    for (const entry of staged) {
      if (fs.existsSync(entry.temporaryPath)) fs.unlinkSync(entry.temporaryPath);
    }
    for (const entry of backups) {
      if (fs.existsSync(entry.backupPath)) fs.unlinkSync(entry.backupPath);
    }
  }
}

function sha256File(filePath) {
  return crypto.createHash("sha256").update(fs.readFileSync(filePath)).digest("hex");
}

function mergeOutputRecords(existing, additions) {
  const byPath = new Map();
  for (const record of Array.isArray(existing) ? existing : []) {
    if (isObject(record) && typeof record.path === "string") {
      byPath.set(record.path, record);
    }
  }
  for (const record of additions) byPath.set(record.path, record);
  return [...byPath.values()].sort((left, right) => left.path.localeCompare(right.path));
}

function outputPackageHash(outputs) {
  return canonicalSha256(
    Object.fromEntries(outputs.map((record) => [record.path, record.sha256 || null])),
  );
}

function buildSavedDecisionManifest(current, review, reviewHash, uiDecisions) {
  const outputs = mergeOutputRecords(current.outputs, [
    artifactRecordForJson("ui_decisions.json", uiDecisions, "written_pending_application"),
  ]);
  const savedAt = new Date().toISOString();
  const decisionByItemId = new Map(
    uiDecisions.decisions.map((decision) => [decision.item_id, decision]),
  );
  const reviewBlockers = buildReviewGateBlockers(review, decisionByItemId);
  const relationshipReviewBlockers = reviewBlockers.filter(
    (blocker) => blocker.scope !== "marketing_use",
  );
  const exportGate = isObject(current.export_gate)
    ? {
        ...current.export_gate,
        evaluated_at: savedAt,
        review_revision: review.review_revision,
        status: "pending_review",
        relationship_ready: false,
        review_blockers: reviewBlockers,
        basis_hashes: { ...review.basis_hashes },
      }
    : null;
  const domainBlockers = exportGate?.domain_blockers || [];
  const artifactBlockers = exportGate?.artifact_blockers || [];
  const relationshipBlockers = [
    ...domainBlockers,
    ...artifactBlockers,
    ...relationshipReviewBlockers,
  ];
  const savedStatus = relationshipBlockers.length > 0 ? "blocked" : "pending_review";
  if (exportGate) exportGate.status = savedStatus;
  return {
    ...current,
    status: exportGate ? savedStatus : current.status,
    review_status: uiDecisions.status,
    review_payload_sha256: reviewHash,
    ...(exportGate
      ? {
          export_gate: exportGate,
          blockers: relationshipBlockers,
        }
      : {}),
    outputs,
    artifacts: outputs,
    package_hash: outputPackageHash(outputs),
    review_application: {
      status: savedStatus,
      decision_count: uiDecisions.decision_count,
      item_count: review.items.length,
      pending_count: review.items.length - uiDecisions.decision_count,
      blocker_count: relationshipBlockers.length,
      review_payload_sha256: reviewHash,
      domain_artifacts_modified: false,
      professional_review_required: true,
      signature_performed: false,
      client_communication_sent: false,
      relationship_activation_performed: false,
    },
    professional_review_required: true,
    signature_performed: false,
    client_communication_sent: false,
    relationship_activation_performed: false,
  };
}

function saveDecisions(input) {
  const payload = validateReview(input);
  const persistent = preparePersistentRun(input, payload);
  const { uiDecisions } = buildUiDecisions(
    input,
    payload,
    persistent.currentUiDecisions,
  );
  uiDecisions.decision_revision = persistent.nextDecisionRevision;
  let finalArtifactsPath = null;
  let outputPath = null;
  if (persistent.outputDir) {
    const refreshed = buildSavedDecisionManifest(
      persistent.currentFinal,
      payload.review_payload,
      payload.review_payload_sha256,
      uiDecisions,
    );
    preflightClientWorkflowRun(
      persistent.outputDir,
      payload.review_payload.run_id,
    );
    writeJsonBatchAtomic(persistent.outputDir, {
      "ui_decisions.json": uiDecisions,
      "final_artifacts.json": refreshed,
    });
    outputPath = "ui_decisions.json";
    finalArtifactsPath = "final_artifacts.json";
  }
  return {
    ok: true,
    validation_type: "new_client_decisions",
    run_id: uiDecisions.run_id,
    review_payload_sha256: uiDecisions.review_payload_sha256,
    decision_count: uiDecisions.decision_count,
    item_count: uiDecisions.item_count,
    status: uiDecisions.status,
    persisted: Boolean(outputPath),
    ui_decisions_path: outputPath,
    final_artifacts_path: finalArtifactsPath,
    message: outputPath
      ? `Saved ${uiDecisions.decision_count} new client decisions.`
      : "Validated decisions. Nothing was written because run_intake.output_dir was not supplied.",
    ui_decisions: publicUiDecisions(uiDecisions, payload.review_payload),
  };
}

function validateCurrentFinalArtifacts(current, review, reviewHash) {
  if (!isObject(current) || Object.keys(current).length === 0) return;
  validateFinalIdentity(current, review);
  if (
    current.review_payload_sha256 != null
    && current.review_payload_sha256 !== reviewHash
  ) {
    throw new Error("final_artifacts.review_payload_sha256 does not match the reviewed payload");
  }
}

function reviewItemScope(item) {
  if (item.item_type === "marketing_consent") return "marketing_use";
  if (item.item_type === "document_applicability") {
    return `document:${safeIdentifier(item.data.topic, `review item ${item.id} topic`)}`;
  }
  return "relationship_export";
}

function decisionBlocker(decision, item) {
  return {
    item_id: decision.item_id,
    item_type: decision.item_type,
    action: decision.action,
    status: decision.status,
    scope: reviewItemScope(item),
    requested_documents: decision.requested_documents || [],
  };
}

function reviewBlockerCode(action) {
  return {
    edit: "professional_revision_required",
    mark_unclear: "professional_review_unclear",
    reject: "professional_review_rejected",
    request_more_documents: "professional_documents_requested",
    skip: "professional_review_skipped",
  }[action] || "professional_review_pending";
}

function buildReviewGateBlockers(review, decisionByItemId) {
  const blockers = [];
  for (const item of review.items) {
    const decision = decisionByItemId.get(item.id);
    if (decision?.action === "accept") continue;
    blockers.push({
      code: reviewBlockerCode(decision?.action),
      reference: item.id,
      scope: reviewItemScope(item),
    });
  }
  return blockers;
}

function applyDecisions(input) {
  const payload = validateReview(input);
  const review = payload.review_payload;
  const reviewHash = payload.review_payload_sha256;
  const persistent = preparePersistentRun(input, payload);
  assertTemporalValidityAtApply(review.temporal_validity);
  const { uiDecisions } = buildUiDecisions(
    input,
    payload,
    persistent.currentUiDecisions,
  );
  const outputDir = persistent.outputDir;
  uiDecisions.decision_revision = persistent.nextDecisionRevision;
  const currentFinal = outputDir
    ? persistent.currentFinal
    : (isObject(input.final_artifacts) ? input.final_artifacts : {});
  validateCurrentFinalArtifacts(currentFinal, review, reviewHash);

  const decidedIds = new Set(uiDecisions.decisions.map((decision) => decision.item_id));
  const pendingItemIds = review.items
    .filter((item) => !decidedIds.has(item.id))
    .map((item) => item.id);
  const reviewItemById = new Map(review.items.map((item) => [item.id, item]));
  const blockerDecisions = uiDecisions.decisions
    .filter((decision) => BLOCKER_ACTIONS.has(decision.action))
    .map((decision) => decisionBlocker(decision, reviewItemById.get(decision.item_id)));
  const relationshipBlockerDecisions = blockerDecisions.filter(
    (blocker) => blocker.scope !== "marketing_use",
  );
  const currentExportGate = isObject(currentFinal.export_gate)
    ? currentFinal.export_gate
    : null;
  const domainBlockers = currentExportGate
    ? currentExportGate.domain_blockers.map((blocker) => ({ ...blocker }))
    : [];
  const artifactBlockers = currentExportGate
    ? currentExportGate.artifact_blockers.map((blocker) => ({ ...blocker }))
    : [];
  const preservedMarketingOnlyBlockers = currentExportGate
    ? currentExportGate.marketing_only_blockers.map((blocker) => ({ ...blocker }))
    : [];
  const existingDomainBlockerCount = domainBlockers.length;
  const existingArtifactBlockerCount = artifactBlockers.length;
  const relationshipItems = review.items.filter(
    (item) => item.item_type !== "marketing_consent",
  );
  const decisionByItemId = new Map(
    uiDecisions.decisions.map((decision) => [decision.item_id, decision]),
  );
  const professionalReviewerBlockers = uiDecisions.reviewer
    || !outputDir
    || relationshipItems.length === 0
    || !relationshipItems.every((item) => {
      const decision = decisionByItemId.get(item.id);
      return decision != null && ["accept", "edit"].includes(decision.action);
    })
    ? []
    : [
        {
          code: "professional_reviewer_required",
          reference: "ui_decisions",
          scope: "relationship_export",
        },
      ];
  const reviewGateBlockers = [
    ...buildReviewGateBlockers(review, decisionByItemId),
    ...professionalReviewerBlockers,
  ];
  const relationshipReviewGateBlockers = reviewGateBlockers.filter(
    (blocker) => blocker.scope !== "marketing_use",
  );
  const marketingReviewGateBlockers = reviewGateBlockers.filter(
    (blocker) => blocker.scope === "marketing_use",
  );
  const allRelationshipItemsAcceptedOrEdited =
    relationshipItems.length > 0
    && relationshipItems.every((item) => {
      const decision = decisionByItemId.get(item.id);
      return decision != null && ["accept", "edit"].includes(decision.action);
    });
  const editedItemIds = uiDecisions.decisions
    .filter((decision) => decision.action === "edit")
    .map((decision) => decision.item_id);
  const relationshipEditedItemIds = uiDecisions.decisions
    .filter(
      (decision) =>
        decision.action === "edit" && decision.item_type !== "marketing_consent",
    )
    .map((decision) => decision.item_id);
  const validatedPersistentOutputs = Boolean(
    outputDir
    && persistent.manifestRecords instanceof Map
    && persistent.manifestRecords.size > 0
    && currentExportGate,
  );
  const professionalReviewerRequiredForReady = Boolean(
    allRelationshipItemsAcceptedOrEdited
    && validatedPersistentOutputs
    && !uiDecisions.reviewer,
  );
  const applicationStatus =
    relationshipBlockerDecisions.length > 0
      || existingDomainBlockerCount > 0
      || existingArtifactBlockerCount > 0
      || professionalReviewerRequiredForReady
      ? "blocked"
      : relationshipEditedItemIds.length > 0
        ? "proposals_ready"
      : allRelationshipItemsAcceptedOrEdited && validatedPersistentOutputs
        && uiDecisions.reviewer
        ? "ready_for_professional_export"
        : "partial_review_applied";
  const exportGateStatus =
    applicationStatus === "ready_for_professional_export"
      ? "ready_for_professional_export"
      : applicationStatus === "blocked"
        ? "blocked"
        : "pending_review";
  const relationshipBlockerCount =
    existingDomainBlockerCount
    + existingArtifactBlockerCount
    + relationshipReviewGateBlockers.length;
  const marketingUseBlockerCount =
    preservedMarketingOnlyBlockers.length + marketingReviewGateBlockers.length;
  const appliedAt = new Date().toISOString();
  const effects = uiDecisions.decisions.map((decision) => ({
    ...decision,
    applied_at: appliedAt,
    application:
      decision.action === "edit"
        ? "revision_instruction_recorded_domain_artifacts_unchanged"
        : "review_status_recorded_domain_artifacts_unchanged",
    domain_artifacts_modified: false,
  }));
  const appliedDecisions = {
    schema_version: review.schema_version,
    contract_version: review.contract_version,
    plugin: PLUGIN_NAME,
    workflow: PLUGIN_NAME,
    run_id: review.run_id,
    review_revision: review.review_revision,
    applied_at: appliedAt,
    decision_source: uiDecisions.decision_source,
    ...(uiDecisions.reviewer ? { reviewer: uiDecisions.reviewer } : {}),
    review_payload_path: "review_payload.json",
    review_payload_sha256: reviewHash,
    decisions: uiDecisions.decisions,
    effects,
    decision_count: uiDecisions.decision_count,
    item_count: review.items.length,
    pending_item_ids: pendingItemIds,
    pending_count: pendingItemIds.length,
    edited_item_ids: editedItemIds,
    relationship_edited_item_ids: relationshipEditedItemIds,
    revision_required: relationshipEditedItemIds.length > 0,
    marketing_revision_required:
      editedItemIds.length > relationshipEditedItemIds.length,
    blockers: blockerDecisions,
    blocker_count: relationshipBlockerCount,
    marketing_use_blocker_count: marketingUseBlockerCount,
    existing_domain_blocker_count: existingDomainBlockerCount,
    existing_artifact_blocker_count: existingArtifactBlockerCount,
    application_status: applicationStatus,
    domain_artifacts_modified: false,
    professional_review_required: true,
    signature_performed: false,
    client_communication_sent: false,
    relationship_activation_performed: false,
  };
  let finalArtifacts = {
    ...currentFinal,
    schema_version: currentFinal.schema_version || review.schema_version,
    contract_version: currentFinal.contract_version || review.contract_version,
    plugin: PLUGIN_NAME,
    workflow: PLUGIN_NAME,
    run_id: review.run_id,
    status: currentExportGate ? exportGateStatus : applicationStatus,
    review_status: applicationStatus,
    review_payload_sha256: reviewHash,
    professional_review_required: true,
    signature_performed: false,
    client_communication_sent: false,
    relationship_activation_performed: false,
    ...(currentExportGate
      ? {
          export_gate: {
            ...currentExportGate,
            evaluated_at: appliedAt,
            review_revision: review.review_revision,
            status: exportGateStatus,
            relationship_ready:
              applicationStatus === "ready_for_professional_export",
            domain_blockers: domainBlockers,
            review_blockers: reviewGateBlockers,
            artifact_blockers: artifactBlockers,
            marketing_only_blockers: preservedMarketingOnlyBlockers,
            basis_hashes: { ...review.basis_hashes },
          },
          blockers: [
            ...domainBlockers,
            ...artifactBlockers,
            ...relationshipReviewGateBlockers,
          ],
        }
      : {}),
    review_application: {
      applied_at: appliedAt,
      status: applicationStatus,
      decision_count: uiDecisions.decision_count,
      item_count: review.items.length,
      pending_count: pendingItemIds.length,
      edited_item_ids: editedItemIds,
      relationship_edited_item_ids: relationshipEditedItemIds,
      revision_required: relationshipEditedItemIds.length > 0,
      marketing_revision_required:
        editedItemIds.length > relationshipEditedItemIds.length,
      blocker_count: relationshipBlockerCount,
      marketing_use_blocker_count: marketingUseBlockerCount,
      applied_decisions_path: "applied_decisions.json",
      history_path: null,
      review_payload_sha256: reviewHash,
      domain_artifacts_modified: false,
      professional_review_required: true,
      signature_performed: false,
      client_communication_sent: false,
      relationship_activation_performed: false,
    },
  };
  if (payloadBytes(appliedDecisions) > MAX_PAYLOAD_BYTES) {
    throw new Error(`applied_decisions payload exceeds ${MAX_PAYLOAD_BYTES} bytes`);
  }

  let uiPath = null;
  let appliedPath = null;
  let historyPath = null;
  if (outputDir) {
    const historyStamp = appliedAt.replace(/[^0-9]/g, "");
    historyPath = `applied_decisions.history.r${review.review_revision}.d${uiDecisions.decision_revision}.${historyStamp}.${crypto.randomUUID().slice(0, 8)}.json`;
    const outputs = mergeOutputRecords(currentFinal.outputs, [
      artifactRecordForJson("ui_decisions.json", uiDecisions),
      artifactRecordForJson("applied_decisions.json", appliedDecisions),
      artifactRecordForJson(historyPath, appliedDecisions, "written_immutable_history"),
    ]);
    finalArtifacts = {
      ...finalArtifacts,
      outputs,
      artifacts: outputs,
      package_hash: outputPackageHash(outputs),
      review_application: {
        ...finalArtifacts.review_application,
        history_path: historyPath,
      },
    };
    validateExportGate(finalArtifacts.export_gate, review);
    preflightClientWorkflowRun(outputDir, review.run_id);
    writeJsonBatchAtomic(outputDir, {
      "ui_decisions.json": uiDecisions,
      "applied_decisions.json": appliedDecisions,
      [historyPath]: appliedDecisions,
      "final_artifacts.json": finalArtifacts,
    });
    uiPath = "ui_decisions.json";
    appliedPath = "applied_decisions.json";
  }
  const finalPath = outputDir ? "final_artifacts.json" : null;
  return {
    ok: true,
    validation_type: "new_client_application",
    run_id: review.run_id,
    review_payload_sha256: reviewHash,
    decision_count: uiDecisions.decision_count,
    item_count: review.items.length,
    pending_count: pendingItemIds.length,
    edited_item_ids: editedItemIds,
    relationship_edited_item_ids: relationshipEditedItemIds,
    revision_required: relationshipEditedItemIds.length > 0,
    marketing_revision_required:
      editedItemIds.length > relationshipEditedItemIds.length,
    blocker_count: relationshipBlockerCount,
    marketing_use_blocker_count: marketingUseBlockerCount,
    application_status: applicationStatus,
    persisted: Boolean(outputDir),
    ui_decisions_path: uiPath,
    applied_decisions_path: appliedPath,
    review_history_path: historyPath,
    final_artifacts_path: finalPath,
    ui_decisions: publicUiDecisions(uiDecisions, review),
    domain_artifacts_modified: false,
    professional_review_required: true,
    signature_performed: false,
    client_communication_sent: false,
    relationship_activation_performed: false,
    message: outputDir
      ? "Review decisions recorded; domain artifacts, signatures, client communications, and relationship activation remain untouched."
      : "Application preview validated. Nothing was written because run_intake.output_dir was not supplied.",
    applied_decisions: appliedDecisions,
    final_artifacts: publicFinalArtifacts(finalArtifacts, review),
  };
}

function callTool(name, input) {
  if (name === TOOL_NAMES.validate) {
    const payload = validateReview(input);
    return {
      ok: true,
      validation_type: "new_client_review",
      run_id: payload.review_payload.run_id,
      item_count: payload.review_payload.item_count,
      review_payload_sha256: payload.review_payload_sha256,
      message: `Review payload is valid; call ${TOOL_NAMES.render} to open it.`,
      review_payload: payload.review_payload,
    };
  }
  if (name === TOOL_NAMES.render) {
    const payload = validateReview(input);
    let persistenceToken = null;
    try {
      persistenceToken = issuePersistenceToken(input, payload);
      payload.decision_policy.can_persist = Boolean(persistenceToken);
      payload.decision_policy.persistence_token = persistenceToken;
      if (payloadBytes(payload) > MAX_PAYLOAD_BYTES) {
        throw new Error(
          `new client widget payload exceeds ${MAX_PAYLOAD_BYTES} bytes`,
        );
      }
      return payload;
    } catch (error) {
      if (persistenceToken) PERSISTENCE_CONTEXTS.delete(persistenceToken);
      throw error;
    }
  }
  if (name === TOOL_NAMES.save) return saveDecisions(input);
  if (name === TOOL_NAMES.apply) return applyDecisions(input);
  throw new Error(`unknown tool: ${name}`);
}

function toolResult(payload, name) {
  const result = {
    content: [{ type: "text", text: JSON.stringify(payload) }],
    structuredContent: payload,
    isError: false,
  };
  if (name === TOOL_NAMES.render) result._meta = toolUiMeta();
  return result;
}

function toolError(error) {
  const payload = {
    ok: false,
    error: error instanceof Error ? error.message : String(error),
  };
  return {
    content: [{ type: "text", text: JSON.stringify(payload) }],
    structuredContent: payload,
    isError: true,
  };
}

function rpcResult(id, result) {
  return { jsonrpc: "2.0", id, result };
}

function rpcError(id, code, message) {
  return { jsonrpc: "2.0", id, error: { code, message } };
}

function handleRpc(message) {
  const id = message.id ?? null;
  const params = isObject(message.params) ? message.params : {};
  try {
    if (message.method === "initialize") {
      return rpcResult(id, {
        protocolVersion: params.protocolVersion || "2024-11-05",
        serverInfo: { name: SERVER_NAME, version: SERVER_VERSION },
        capabilities: { tools: {}, resources: {}, prompts: {} },
        instructions:
          `Call ${TOOL_NAMES.validate} before ${TOOL_NAMES.render}. Save/apply only record professional review decisions; they never modify domain artifacts, sign documents, contact the client, or activate the relationship.`,
      });
    }
    if (message.method === "notifications/initialized") return null;
    if (message.method === "tools/list") return rpcResult(id, { tools: toolDefinitions() });
    if (message.method === "tools/call") {
      if (typeof params.name !== "string") {
        return rpcError(id, -32602, "tools/call requires a tool name");
      }
      if (!isObject(params.arguments)) {
        return rpcError(id, -32602, "tools/call arguments must be an object");
      }
      try {
        return rpcResult(id, toolResult(callTool(params.name, params.arguments), params.name));
      } catch (error) {
        return rpcResult(id, toolError(error));
      }
    }
    if (message.method === "resources/list") return rpcResult(id, { resources: resources() });
    if (message.method === "resources/read") {
      if (typeof params.uri !== "string") {
        return rpcError(id, -32602, "resources/read requires a resource URI");
      }
      return rpcResult(id, {
        contents: [
          {
            uri: params.uri,
            mimeType: WIDGET_MIME_TYPE,
            text: resourceText(params.uri),
            _meta: widgetMeta(),
          },
        ],
      });
    }
    if (message.method === "resources/templates/list") {
      return rpcResult(id, { resourceTemplates: [] });
    }
    if (message.method === "prompts/list") return rpcResult(id, { prompts: [] });
    return rpcError(id, -32601, "method not found");
  } catch (error) {
    return rpcError(id, -32000, error instanceof Error ? error.message : String(error));
  }
}

function send(payload) {
  process.stdout.write(`${JSON.stringify(payload)}\n`);
}

function main() {
  const lines = readline.createInterface({ input: process.stdin, crlfDelay: Infinity });
  lines.on("line", (line) => {
    if (!line.trim()) return;
    let message;
    try {
      message = JSON.parse(line);
    } catch {
      send(rpcError(null, -32700, "parse error"));
      return;
    }
    const response = handleRpc(message);
    if (response !== null && message.id != null) send(response);
  });
}

main();
