"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
  for (var name in all)
    __defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
  if (from && typeof from === "object" || typeof from === "function") {
    for (let key of __getOwnPropNames(from))
      if (!__hasOwnProp.call(to, key) && key !== except)
        __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
  }
  return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);

// src/basic-gameplay.ts
var basic_gameplay_exports = {};
__export(basic_gameplay_exports, {
  BASIC_GAMEPLAY_ACTION_TYPES: () => BASIC_GAMEPLAY_ACTION_TYPES,
  BASIC_GAMEPLAY_PROGRESS_CHECK_TYPES: () => BASIC_GAMEPLAY_PROGRESS_CHECK_TYPES,
  RIDDLE_PROOF_BASIC_GAMEPLAY_ASSESSMENT_VERSION: () => RIDDLE_PROOF_BASIC_GAMEPLAY_ASSESSMENT_VERSION,
  RIDDLE_PROOF_BASIC_GAMEPLAY_CATCH_VERSION: () => RIDDLE_PROOF_BASIC_GAMEPLAY_CATCH_VERSION,
  RIDDLE_PROOF_BASIC_GAMEPLAY_VERSION: () => RIDDLE_PROOF_BASIC_GAMEPLAY_VERSION,
  assessBasicGameplayEvidence: () => assessBasicGameplayEvidence,
  assessBasicGameplayProgressionCheck: () => assessBasicGameplayProgressionCheck,
  assessBasicGameplayProgressionChecks: () => assessBasicGameplayProgressionChecks,
  assessBasicGameplayRoute: () => assessBasicGameplayRoute,
  attachBasicGameplayArtifactScreenshotHashes: () => attachBasicGameplayArtifactScreenshotHashes,
  augmentBasicGameplayAssessmentWithProgressionChecks: () => augmentBasicGameplayAssessmentWithProgressionChecks,
  compactBasicGameplayText: () => compactBasicGameplayText,
  createBasicGameplayCatchRecords: () => createBasicGameplayCatchRecords,
  createBasicGameplayCatchSummary: () => createBasicGameplayCatchSummary,
  extractBasicGameplayEvidence: () => extractBasicGameplayEvidence,
  resolveBasicGameplayProgressionCheckWithArtifactScreenshots: () => resolveBasicGameplayProgressionCheckWithArtifactScreenshots,
  sanitizeBasicGameplayJsonString: () => sanitizeBasicGameplayJsonString
});
module.exports = __toCommonJS(basic_gameplay_exports);
var RIDDLE_PROOF_BASIC_GAMEPLAY_VERSION = "riddle-proof.basic-gameplay.v1";
var RIDDLE_PROOF_BASIC_GAMEPLAY_ASSESSMENT_VERSION = "riddle-proof.basic-gameplay.assessment.v1";
var RIDDLE_PROOF_BASIC_GAMEPLAY_CATCH_VERSION = "riddle-proof.basic-gameplay.catch.v1";
var BASIC_GAMEPLAY_ACTION_TYPES = [
  "wait",
  "key",
  "key-down",
  "key-up",
  "hold-key",
  "repeat",
  "click",
  "click-by-text",
  "set-input-value",
  "canvas-click",
  "canvas-pointer-down",
  "canvas-pointer-move",
  "canvas-pointer-up",
  "wait-for-text",
  "window-call",
  "evaluate"
];
var BASIC_GAMEPLAY_PROGRESS_CHECK_TYPES = [
  "selector_count_increases",
  "selector_count_at_least",
  "selector_count_equals",
  "selector_count_equal",
  "selector_count_eq",
  "selector_absent",
  "selector_text_matches",
  "number_increases",
  "number_decreases",
  "number_at_least",
  "number_gte",
  "number_unchanged",
  "number_stays_equal",
  "number_equals",
  "canvas_hash_changes",
  "screenshot_hash_changes",
  "visual_hash_changes",
  "state_changes"
];
var BASIC_GAMEPLAY_CONTAINER_KEYS = [
  "basic_gameplay",
  "basicGameplay",
  "basic_gameplay_evidence",
  "basicGameplayEvidence",
  "gameplay_proof",
  "gameplayProof"
];
var ARTIFACT_VISUAL_CHANGE_CHECKS = /* @__PURE__ */ new Set([
  "canvas_hash_changes",
  "screenshot_hash_changes",
  "state_changes",
  "visual_hash_changes"
]);
var PHASE_SCREENSHOT_SUFFIXES = {
  initial: "before",
  after_action: "after",
  after_continue: "after-continue",
  after_cleanup: "after-cleanup",
  after_restart: "after-restart",
  revisit: "revisit"
};
function assessBasicGameplayEvidence(evidence, options = {}) {
  const run = extractBasicGameplayEvidence(evidence);
  if (!run) {
    return {
      version: RIDDLE_PROOF_BASIC_GAMEPLAY_ASSESSMENT_VERSION,
      evidence_present: false,
      passed: false,
      checked_routes: 0,
      passing_routes: 0,
      failing_routes: [],
      failure_counts: {},
      warning_counts: {},
      route_results: []
    };
  }
  const routeResults = (run.results || []).map((route) => augmentRouteAssessmentWithProgressionChecks(
    assessBasicGameplayRoute(route, options),
    route,
    options
  ));
  const failingRoutes = routeResults.filter((result) => !result.ok).map((result) => ({
    name: result.name,
    path: result.path,
    assessment_mode: result.assessment_mode,
    failures: result.failures,
    warnings: result.warnings,
    suite_failures: result.suite_failures
  }));
  return {
    version: RIDDLE_PROOF_BASIC_GAMEPLAY_ASSESSMENT_VERSION,
    evidence_present: true,
    passed: failingRoutes.length === 0,
    checked_routes: routeResults.length,
    passing_routes: routeResults.filter((result) => result.ok).length,
    failing_routes: failingRoutes,
    failure_counts: countCodes(routeResults.flatMap((result) => result.failures)),
    warning_counts: countCodes(routeResults.flatMap((result) => result.warnings)),
    route_results: routeResults
  };
}
function assessBasicGameplayRoute(route, options = {}) {
  const maxMobileOverflowPx = options.maxMobileOverflowPx ?? 4;
  const minBodyTextLength = options.minBodyTextLength ?? 20;
  const minVisibleLargeNodes = options.minVisibleLargeNodes ?? 3;
  const minSurfaceLargeNodes = options.minSurfaceLargeNodes ?? 8;
  const warnOnMissingResetPath = options.warnOnMissingResetPath ?? true;
  const warnOnConsoleError = options.warnOnConsoleError ?? true;
  const failOnConsoleError = options.failOnConsoleError ?? false;
  const failures = [];
  const warnings = [];
  const assessmentMode = routeAssessmentMode(route);
  const auditAssessment = isAuditAssessmentRoute(route);
  const initial = route.initial || {};
  const timed = route.timed || {};
  const afterAction = route.after_action || route.afterAction || {};
  const afterContinue = route.after_continue || route.afterContinue || {};
  const afterCleanup = route.after_cleanup || route.afterCleanup || {};
  const mobile = route.mobile || {};
  const timedChange = changed(initial, timed);
  const actionChange = changed(timed, afterAction);
  const continuedActionChange = changed(afterAction, afterContinue);
  const cleanupBase = route.after_continue || route.afterContinue ? afterContinue : afterAction;
  const cleanupActionChange = changed(cleanupBase, afterCleanup);
  const interactiveSurfaceVisible = numberValue(initial.visible_canvas_count) > 0 || numberValue(initial.enabled_clickable_count) > 0 || numberValue(initial.visible_large_node_count) >= minSurfaceLargeNodes;
  const auditSurfaceVisible = numberValue(initial.body_text_length) >= minBodyTextLength || numberValue(initial.visible_large_node_count) >= minVisibleLargeNodes || numberValue(initial.enabled_clickable_count) > 0 || numberValue(initial.visible_canvas_count) > 0;
  const surfaceVisible = auditAssessment ? auditSurfaceVisible : interactiveSurfaceVisible;
  const actionResults = listValue(route.action_results || route.actionResults);
  const continuedActionResults = listValue(route.continued_action_results || route.continuedActionResults);
  const continuedCleanupActionResults = listValue(route.continued_cleanup_action_results || route.continuedCleanupActionResults);
  const restartActionResults = listValue(route.restart_action_results || route.restartActionResults);
  const primaryActionResults = [...actionResults, ...continuedActionResults];
  const actionAttempted = primaryActionResults.some((result) => result.ok === true && result.action !== "wait");
  const actionFailed = [...primaryActionResults, ...continuedCleanupActionResults].some((result) => result.ok === false && result.action !== "wait");
  const restartActionAttempted = restartActionResults.some((result) => result.ok === true && result.action !== "wait");
  const stateChangeObserved = actionChange.changed || continuedActionChange.changed || cleanupActionChange.changed || timedChange.changed;
  const resetPathPresent = numberValue(initial.reset_control_count) > 0 || numberValue(timed.reset_control_count) > 0 || numberValue(afterAction.reset_control_count) > 0 || numberValue(afterContinue.reset_control_count) > 0 || numberValue(afterCleanup.reset_control_count) > 0 || restartActionAttempted;
  const responseStatus = firstNumber(route.http_status, route.response_status, route.status);
  const pageErrorCount = numberValue(route.page_error_count);
  const consoleErrorCount = numberValue(route.console_error_count);
  const mobileOverflowPx = horizontalBoundsOverflowPx(mobile);
  if (responseStatus !== null && responseStatus >= 400) failures.push("route_http_error");
  if (pageErrorCount > 0) failures.push("fatal_page_error");
  if (numberValue(initial.body_text_length) < minBodyTextLength && numberValue(initial.visible_large_node_count) < minVisibleLargeNodes) {
    failures.push("route_blank_or_thin");
  }
  if (!surfaceVisible) failures.push("no_game_surface");
  if (mobileOverflowPx > maxMobileOverflowPx) failures.push("mobile_horizontal_overflow");
  if (!auditAssessment && !actionAttempted && !timedChange.changed) failures.push("primary_control_missing");
  if (!auditAssessment && actionAttempted && !stateChangeObserved) failures.push("primary_control_inert");
  if (failOnConsoleError && consoleErrorCount > 0) failures.push("fatal_page_error");
  if (numberValue(initial.visible_canvas_count) > 0 && actionAttempted && !timedChange.canvas_changed && !actionChange.canvas_changed && !actionChange.screenshot_changed) {
    warnings.push("canvas_inert");
  }
  if (actionFailed) warnings.push("some_actions_failed");
  if (!auditAssessment && warnOnMissingResetPath && !resetPathPresent && route.requires_reset !== false && actionAttempted && stateChangeObserved) {
    warnings.push("missing_reset_path");
  }
  if (warnOnConsoleError && consoleErrorCount > 0) warnings.push("critical_console_error");
  return {
    name: route.name,
    path: route.path,
    assessment_mode: assessmentMode,
    ok: failures.length === 0,
    failures,
    warnings,
    signals: {
      route_loaded: !failures.includes("route_http_error") && !failures.includes("route_blank_or_thin"),
      surface_visible: surfaceVisible,
      action_attempted: actionAttempted,
      timed_progression_observed: timedChange.changed,
      first_interaction_observed: actionChange.changed || continuedActionChange.changed,
      state_change_observed: stateChangeObserved,
      mobile_overflow_absent: mobileOverflowPx <= maxMobileOverflowPx,
      reset_path_present: resetPathPresent,
      fatal_errors_absent: pageErrorCount === 0
    },
    diffs: {
      timed: timedChange,
      after_action: actionChange,
      after_continue: continuedActionChange,
      after_cleanup: cleanupActionChange
    }
  };
}
function sanitizeBasicGameplayJsonString(value) {
  const text = String(value ?? "");
  let output = "";
  for (let index = 0; index < text.length; index += 1) {
    const code = text.charCodeAt(index);
    if (code >= 55296 && code <= 56319) {
      const next = text.charCodeAt(index + 1);
      if (next >= 56320 && next <= 57343) {
        output += text[index] + text[index + 1];
        index += 1;
      }
      continue;
    }
    if (code >= 56320 && code <= 57343) continue;
    output += text[index];
  }
  return output;
}
function compactBasicGameplayText(value, max = 160) {
  const compacted = sanitizeBasicGameplayJsonString(value).replace(/\s+/g, " ").trim();
  return Array.from(compacted).slice(0, max).join("");
}
function assessBasicGameplayProgressionCheck(check) {
  const before = metricValue(check.before);
  const after = metricValue(check.after);
  const type = String(check.type || "");
  const hasExplicitResult = typeof check.ok === "boolean";
  let ok = hasExplicitResult ? check.ok === true : true;
  let reason = check.reason ?? null;
  if (type === "selector_count_increases") {
    ok = numberValue(after?.count) > numberValue(before?.count);
    reason = ok ? null : "selector_count_did_not_increase";
  } else if (type === "selector_count_at_least") {
    if (numericValue(check.min) === null && hasExplicitResult) return resolveBasicGameplayProgressionCheckWithArtifactScreenshots({ ...check, ok, reason });
    ok = numberValue(after?.count) >= numberValue(check.min);
    reason = ok ? null : "selector_count_below_min";
  } else if (type === "selector_count_equals" || type === "selector_count_equal" || type === "selector_count_eq") {
    const expected = numericValue(check.expected ?? check.count ?? check.value);
    if (expected === null && hasExplicitResult) return resolveBasicGameplayProgressionCheckWithArtifactScreenshots({ ...check, ok, reason });
    ok = numericValue(after?.count) !== null && expected !== null && numberValue(after?.count) === expected;
    reason = ok ? null : "selector_count_did_not_equal_expected";
  } else if (type === "selector_absent") {
    ok = !after?.present || numberValue(after?.count) === 0;
    reason = ok ? null : "selector_still_present";
  } else if (type === "selector_text_matches") {
    if (!check.pattern && hasExplicitResult) return resolveBasicGameplayProgressionCheckWithArtifactScreenshots({ ...check, ok, reason });
    ok = after?.pattern_matched === true || textMatches(after?.text, check.pattern, check.flags);
    reason = ok ? null : "selector_text_did_not_match";
  } else if (type === "number_increases") {
    ok = numericValue(before?.number) !== null && numericValue(after?.number) !== null && numberValue(after?.number) > numberValue(before?.number);
    reason = ok ? null : "number_did_not_increase";
  } else if (type === "number_decreases") {
    ok = numericValue(before?.number) !== null && numericValue(after?.number) !== null && numberValue(after?.number) < numberValue(before?.number);
    reason = ok ? null : "number_did_not_decrease";
  } else if (type === "number_at_least" || type === "number_gte") {
    const min = numericValue(check.min ?? check.expected ?? check.value);
    if (min === null && hasExplicitResult) return resolveBasicGameplayProgressionCheckWithArtifactScreenshots({ ...check, ok, reason });
    ok = numericValue(after?.number) !== null && min !== null && numberValue(after?.number) >= min;
    reason = ok ? null : "number_below_minimum";
  } else if (type === "number_unchanged" || type === "number_stays_equal") {
    ok = numericValue(before?.number) !== null && numericValue(after?.number) !== null && numberValue(after?.number) === numberValue(before?.number);
    reason = ok ? null : "number_changed";
  } else if (type === "number_equals") {
    const expected = numericValue(check.expected ?? check.value);
    if (expected === null && hasExplicitResult) return resolveBasicGameplayProgressionCheckWithArtifactScreenshots({ ...check, ok, reason });
    ok = numericValue(after?.number) !== null && expected !== null && numberValue(after?.number) === expected;
    reason = ok ? null : "number_did_not_equal_expected";
  } else if (type === "canvas_hash_changes") {
    ok = hashChanged(before, after, ["first_canvas_hash", "first_canvas_visual_sample_hash", "visual_sample_hash"]);
    reason = ok ? null : "canvas_or_visual_sample_hash_did_not_change";
  } else if (type === "screenshot_hash_changes") {
    ok = hashChanged(before, after, ["screenshot_hash", "visual_sample_hash", "artifact_screenshot_hash"]);
    reason = ok ? null : "screenshot_hash_did_not_change";
  } else if (type === "visual_hash_changes") {
    ok = hashChanged(before, after, ["visual_sample_hash", "screenshot_hash", "artifact_screenshot_hash"]);
    reason = ok ? null : "visual_hash_did_not_change";
  } else if (type === "state_changes") {
    ok = hashChanged(before, after, [
      "dom_signature_hash",
      "body_text_hash",
      "screenshot_hash",
      "visual_sample_hash",
      "artifact_screenshot_hash",
      "first_canvas_hash",
      "first_canvas_visual_sample_hash"
    ]);
    reason = ok ? null : "state_hash_did_not_change";
  }
  const assessed = {
    ...check,
    ok,
    reason
  };
  return resolveBasicGameplayProgressionCheckWithArtifactScreenshots(assessed);
}
function assessBasicGameplayProgressionChecks(route) {
  return progressionChecksForRoute(route).map((check) => assessBasicGameplayProgressionCheck(check));
}
function augmentBasicGameplayAssessmentWithProgressionChecks(assessment, evidence, options = {}) {
  const run = extractBasicGameplayEvidence(evidence);
  if (!run?.results?.length) return assessment;
  const routeResults = assessment.route_results.map(
    (result, index) => augmentRouteAssessmentWithProgressionChecks(result, run.results?.[index] || {}, options)
  );
  const failingRoutes = routeResults.filter((result) => !result.ok).map((result) => ({
    name: result.name,
    path: result.path,
    assessment_mode: result.assessment_mode,
    failures: result.failures,
    warnings: result.warnings,
    suite_failures: result.suite_failures
  }));
  return {
    ...assessment,
    passed: failingRoutes.length === 0,
    passing_routes: routeResults.filter((result) => result.ok).length,
    failing_routes: failingRoutes,
    failure_counts: countCodes(routeResults.flatMap((result) => result.failures)),
    warning_counts: countCodes(routeResults.flatMap((result) => result.warnings)),
    route_results: routeResults
  };
}
function resolveBasicGameplayProgressionCheckWithArtifactScreenshots(check) {
  if (check.ok !== false) return check;
  if (!ARTIFACT_VISUAL_CHANGE_CHECKS.has(String(check.type || ""))) return check;
  const beforeHash = stringValue(metricValue(check.before)?.artifact_screenshot_hash);
  const afterHash = stringValue(metricValue(check.after)?.artifact_screenshot_hash);
  if (!beforeHash || !afterHash || beforeHash === afterHash) return check;
  return {
    ...check,
    ok: true,
    reason: null,
    artifact_resolution: {
      source: "riddle_screenshot_artifacts",
      before_hash: beforeHash,
      after_hash: afterHash
    }
  };
}
function attachBasicGameplayArtifactScreenshotHashes(evidence, routesOrOptions = {}, maybeArtifacts = []) {
  const routes = Array.isArray(routesOrOptions) ? routesOrOptions : routesOrOptions.routes || [];
  const artifacts = Array.isArray(routesOrOptions) ? maybeArtifacts : routesOrOptions.artifacts || [];
  if (!evidence?.results?.length || !artifacts.length) return evidence;
  const artifactsByName = screenshotArtifactIndex(artifacts);
  if (!artifactsByName.size) return evidence;
  for (const [index, routeEvidence] of evidence.results.entries()) {
    const routeContract = routes[index] || {};
    for (const phase of Object.keys(PHASE_SCREENSHOT_SUFFIXES)) {
      attachPhaseArtifactHash(routeEvidence, routeContract, phase, artifactsByName);
    }
    const checks = progressionChecksForRoute(routeEvidence);
    const resolvedChecks = checks.map((check) => {
      const fromPhase = check.from_phase || "initial";
      const toPhase = check.to_phase || "after_action";
      if (check.before) attachPhaseArtifactHash({ ...routeEvidence, [fromPhase]: check.before }, routeContract, fromPhase, artifactsByName);
      if (check.after) attachPhaseArtifactHash({ ...routeEvidence, [toPhase]: check.after }, routeContract, toPhase, artifactsByName);
      return resolveBasicGameplayProgressionCheckWithArtifactScreenshots(check);
    });
    if (routeEvidence.progression_checks) routeEvidence.progression_checks = resolvedChecks;
    else if (routeEvidence.progressionChecks) routeEvidence.progressionChecks = resolvedChecks;
    routeEvidence.progression_failure_count = resolvedChecks.filter((check) => check.ok === false).length;
  }
  return evidence;
}
function createBasicGameplayCatchRecords(assessment, evidence, options = {}) {
  const run = extractBasicGameplayEvidence(evidence);
  if (!run?.results?.length) return [];
  const catches = [];
  for (const route of run.results) {
    if (numberValue(route.page_error_count) > 0) {
      catches.push({
        site: run.site || null,
        route: route.name,
        path: route.path,
        code: "fatal_page_error",
        label: "page runtime error",
        type: "page_error",
        selector: null,
        reason: stringValue(route.first_page_error) || "page_error",
        page_errors: listValue(route.page_errors),
        summary: `${route.name || route.path || "Route"}: page runtime error (${stringValue(route.first_page_error) || "page_error"})`
      });
    }
    if (numberValue(route.console_error_count) > 0) {
      catches.push({
        site: run.site || null,
        route: route.name,
        path: route.path,
        code: "critical_console_error",
        label: "console error",
        type: "console_error",
        selector: null,
        reason: stringValue(route.first_console_error) || "console_error",
        console_errors: listValue(route.console_errors),
        summary: `${route.name || route.path || "Route"}: console error (${stringValue(route.first_console_error) || "console_error"})`
      });
    }
    for (const [group, phase] of [
      ["action_results", "after_action"],
      ["continued_action_results", "after_continue"],
      ["continued_cleanup_action_results", "after_cleanup"],
      ["restart_action_results", "after_restart"]
    ]) {
      for (const actionResult of listValue(route[group])) {
        if (!actionResult || actionResult.ok !== false) continue;
        const action = stringValue(actionResult.action) || group;
        const reason = stringValue(actionResult.reason) || "action_failed";
        catches.push({
          site: run.site || null,
          route: route.name,
          path: route.path,
          code: "action_failed",
          label: action,
          type: action,
          selector: stringValue(actionResult.selector),
          reason,
          phase,
          action_result: actionResult,
          summary: `${route.name || route.path || "Route"}: ${action} failed (${reason})`
        });
      }
    }
    for (const failure of responsiveSetupFailures(route)) {
      catches.push({
        site: run.site || null,
        route: route.name,
        path: route.path,
        code: failure.code,
        label: failure.label,
        type: failure.action_result?.action || "responsive_setup",
        selector: stringValue(failure.action_result?.selector),
        reason: failure.reason || "responsive_setup_failed",
        phase: failure.viewport?.phase || void 0,
        viewport: failure.viewport,
        action_result: failure.action_result,
        summary: `${route.name || route.path || "Route"}: responsive setup failed on ${failure.viewport?.label || "viewport"} (${failure.reason || "responsive_setup_failed"})`
      });
    }
    for (const failure of responsiveBoundsFailures(route, options.maxMobileOverflowPx ?? 4)) {
      catches.push({
        site: run.site || null,
        route: route.name,
        path: route.path,
        code: failure.code,
        label: failure.label,
        type: "responsive_bounds",
        selector: null,
        reason: failure.reason || "responsive_bounds_clipped",
        phase: failure.viewport?.phase || void 0,
        viewport: failure.viewport,
        overflow_px: failure.overflow_px,
        max_allowed_px: failure.max_allowed_px,
        offenders: failure.offenders,
        summary: `${route.name || route.path || "Route"}: responsive bounds exceeded ${failure.max_allowed_px}px on ${failure.viewport?.label || "viewport"} (${failure.overflow_px}px)`
      });
    }
    for (const check of assessBasicGameplayProgressionChecks(route).filter((item) => item.ok === false)) {
      catches.push({
        site: run.site || null,
        route: route.name,
        path: route.path,
        code: "progression_assertion_failed",
        label: check.label,
        type: String(check.type || ""),
        selector: check.selector || null,
        state_path: check.state_path || null,
        state_call: check.state_call || null,
        property_path: check.property_path || null,
        reason: check.reason || "progression_assertion_failed",
        from_phase: check.from_phase,
        to_phase: check.to_phase,
        before: compactCatchMetric(check.before),
        after: compactCatchMetric(check.after),
        summary: `${route.name || route.path || "Route"}: ${check.label || check.type || "progression check"} failed (${check.reason || "progression_assertion_failed"})`
      });
    }
  }
  if (!catches.length && assessment.failing_routes.length) {
    for (const route of assessment.failing_routes) {
      for (const code of route.failures || []) {
        catches.push({
          site: run.site || null,
          route: route.name,
          path: route.path,
          code,
          label: code,
          type: code,
          reason: code,
          summary: `${route.name || route.path || "Route"}: ${code}`
        });
      }
    }
  }
  return catches;
}
function createBasicGameplayCatchSummary(input, options = {}) {
  const before = summarizeAssessment(assessBasicGameplayEvidence(input.before, options));
  const after = input.after === void 0 ? void 0 : summarizeAssessment(assessBasicGameplayEvidence(input.after, options));
  const fixed = Boolean(after && before.notable_codes.length > 0 && after.passed && after.notable_codes.length === 0);
  const title = input.title || [
    input.site || "Basic gameplay",
    input.route ? `${input.route} proof catch` : "proof catch"
  ].join(" ");
  const beforeCodes = before.notable_codes.length ? before.notable_codes.join(", ") : "no failing or warning codes";
  const afterCodes = after ? after.notable_codes.length ? after.notable_codes.join(", ") : "no failing or warning codes" : "not verified";
  const summaryLines = [
    `Before: ${before.checked_routes} checked, ${before.passing_routes} passing, codes: ${beforeCodes}.`,
    after ? `After: ${after.checked_routes} checked, ${after.passing_routes} passing, codes: ${afterCodes}.` : "After: not provided."
  ];
  if (input.fix?.summary) summaryLines.push(`Fix: ${input.fix.summary}`);
  if (input.artifacts?.length) summaryLines.push(`Artifacts: ${input.artifacts.length} attached.`);
  return {
    version: RIDDLE_PROOF_BASIC_GAMEPLAY_CATCH_VERSION,
    title,
    site: input.site,
    route: input.route,
    detected_at: input.detected_at || (/* @__PURE__ */ new Date()).toISOString(),
    before,
    after,
    fixed,
    fix: input.fix,
    artifacts: input.artifacts || [],
    summary_lines: summaryLines,
    marketing_summary: fixed ? `${title}: Riddle Proof caught ${beforeCodes}; after the fix, ${afterCodes}.` : `${title}: Riddle Proof caught ${beforeCodes}; after evidence is ${after ? "not yet clean" : "not yet attached"}.`
  };
}
function extractBasicGameplayEvidence(...sources) {
  const seen = /* @__PURE__ */ new Set();
  for (const source of sources) {
    const found = findBasicGameplayEvidence(source, seen);
    if (found) return found;
  }
  return null;
}
function summarizeAssessment(assessment) {
  return {
    evidence_present: assessment.evidence_present,
    passed: assessment.passed,
    checked_routes: assessment.checked_routes,
    passing_routes: assessment.passing_routes,
    failing_routes: assessment.failing_routes,
    failure_counts: assessment.failure_counts,
    warning_counts: assessment.warning_counts,
    notable_codes: [
      ...Object.keys(assessment.failure_counts),
      ...Object.keys(assessment.warning_counts)
    ].sort()
  };
}
function findBasicGameplayEvidence(value, seen, depth = 0) {
  if (depth > 6 || value === null || value === void 0) return null;
  if (typeof value === "string") {
    const parsed = parseJson(value);
    return parsed === null ? null : findBasicGameplayEvidence(parsed, seen, depth + 1);
  }
  if (Array.isArray(value)) {
    if (seen.has(value)) return null;
    seen.add(value);
    if (value.some((item) => hasRouteShape(recordValue(item)))) {
      return { results: value.filter((item) => hasRouteShape(recordValue(item))) };
    }
    for (const item of value) {
      const found = findBasicGameplayEvidence(item, seen, depth + 1);
      if (found) return found;
    }
    return null;
  }
  const record = recordValue(value);
  if (!record || seen.has(record)) return null;
  seen.add(record);
  if (record.version === RIDDLE_PROOF_BASIC_GAMEPLAY_VERSION || Array.isArray(record.results)) {
    return {
      ...record,
      results: listValue(record.results).filter((item) => Boolean(recordValue(item)))
    };
  }
  if (hasRouteShape(record)) {
    return { results: [record] };
  }
  for (const key of BASIC_GAMEPLAY_CONTAINER_KEYS) {
    if (Object.prototype.hasOwnProperty.call(record, key)) {
      const nested = findBasicGameplayEvidence(record[key], seen, depth + 1);
      if (nested) return nested;
    }
  }
  for (const item of Object.values(record)) {
    const found = findBasicGameplayEvidence(item, seen, depth + 1);
    if (found) return found;
  }
  return null;
}
function hasRouteShape(record) {
  return Boolean(record && (record.initial || record.after_action || record.afterAction || record.after_cleanup || record.afterCleanup || record.action_results || record.actionResults || record.continued_cleanup_action_results || record.continuedCleanupActionResults || record.responsive_viewports || record.responsiveViewports) && (record.path || record.name));
}
function changed(before, after) {
  const bodyTextChanged = Boolean(before.body_text_hash && after.body_text_hash && before.body_text_hash !== after.body_text_hash);
  const screenshotChanged = Boolean(before.screenshot_hash && after.screenshot_hash && before.screenshot_hash !== after.screenshot_hash);
  const beforeCanvasHashes = canvasHashes(before.canvases);
  const afterCanvasHashes = canvasHashes(after.canvases);
  const canvasChanged = Boolean(beforeCanvasHashes && afterCanvasHashes && beforeCanvasHashes !== afterCanvasHashes);
  return {
    body_text_changed: bodyTextChanged,
    screenshot_changed: screenshotChanged,
    canvas_changed: canvasChanged,
    changed: bodyTextChanged || screenshotChanged || canvasChanged
  };
}
function canvasHashes(canvases) {
  return (canvases || []).map((canvas) => canvas.hash).filter(Boolean).join("|");
}
function augmentRouteAssessmentWithProgressionChecks(result, route, options = {}) {
  const progressionFailures = assessBasicGameplayProgressionChecks(route).filter((check) => check.ok === false);
  const responsiveFailures = responsiveSetupFailures(route);
  const responsiveBounds = responsiveBoundsFailures(route, options.maxMobileOverflowPx ?? 4);
  if (!progressionFailures.length && !responsiveFailures.length && !responsiveBounds.length) return result;
  const failures = new Set(result.failures);
  if (progressionFailures.length) failures.add("progression_assertion_failed");
  for (const failure of responsiveFailures) failures.add(failure.code);
  for (const failure of responsiveBounds) failures.add(failure.code);
  return {
    ...result,
    ok: false,
    failures: [...failures],
    suite_failures: [
      ...result.suite_failures || [],
      ...progressionFailures.map((check) => ({
        code: "progression_assertion_failed",
        label: check.label,
        reason: check.reason,
        selector: check.selector || null,
        state_path: check.state_path || null,
        state_call: check.state_call || null,
        property_path: check.property_path || null
      })),
      ...responsiveFailures,
      ...responsiveBounds
    ]
  };
}
function responsiveSetupFailures(route) {
  const failures = [];
  for (const viewport of responsiveViewportsForRoute(route)) {
    for (const actionResult of listValue(viewport.setup_action_results || viewport.setupActionResults)) {
      if (!actionResult || actionResult.ok !== false) continue;
      failures.push({
        code: "responsive_setup_failed",
        label: `${viewport.label || "responsive"} ${actionResult.action || "setup action"}`,
        reason: stringValue(actionResult.reason) || "responsive_setup_failed",
        selector: stringValue(actionResult.selector),
        viewport: {
          label: viewport.label || null,
          width: numberValue(viewport.width) || null,
          height: numberValue(viewport.height) || null,
          phase: viewport.phase || null
        },
        action_result: actionResult
      });
    }
  }
  return failures;
}
function responsiveBoundsFailures(route, maxOverflowPx) {
  const failures = [];
  for (const viewport of responsiveViewportsForRoute(route)) {
    const overflowPx = horizontalBoundsOverflowPx(viewport);
    if (overflowPx <= maxOverflowPx) continue;
    failures.push({
      code: "responsive_bounds_clipped",
      label: `${viewport.label || "responsive"} horizontal bounds`,
      reason: `horizontal bounds overflow ${overflowPx}px exceeded ${maxOverflowPx}px`,
      overflow_px: overflowPx,
      max_allowed_px: maxOverflowPx,
      offenders: boundsOffendersForEvidence(viewport).slice(0, 10),
      viewport: {
        label: viewport.label || null,
        width: numberValue(viewport.width) || null,
        height: numberValue(viewport.height) || null,
        phase: viewport.phase || null
      }
    });
  }
  return failures;
}
function responsiveViewportsForRoute(route) {
  return listValue(route.responsive_viewports || route.responsiveViewports).filter((item) => Boolean(recordValue(item)));
}
function progressionChecksForRoute(route) {
  return listValue(route.progression_checks || route.progressionChecks).filter((item) => Boolean(recordValue(item)));
}
function metricValue(value) {
  return recordValue(value);
}
function horizontalBoundsOverflowPx(value) {
  const record = recordValue(value);
  if (!record) return 0;
  let max = maxPositiveNumber(
    record.overflow_px,
    record.overflow,
    record.bounds_overflow_px,
    record.horizontal_overflow_px,
    record.left_overflow_px,
    record.right_overflow_px
  );
  for (const offender of boundsOffendersForEvidence(record)) {
    max = Math.max(max, horizontalOffenderOverflowPx(offender));
  }
  return roundPixels(max);
}
function horizontalOffenderOverflowPx(value) {
  const record = recordValue(value);
  if (!record) return 0;
  let max = maxPositiveNumber(
    record.overflow,
    record.overflow_px,
    record.bounds_overflow_px,
    record.horizontal_overflow_px,
    record.left_overflow_px,
    record.right_overflow_px,
    record.leftOverflowPx,
    record.rightOverflowPx
  );
  const clipped = recordValue(record.clipped || record.clip || record.clipping);
  if (clipped) {
    max = Math.max(max, maxPositiveNumber(
      clipped.left,
      clipped.right,
      clipped.left_px,
      clipped.right_px,
      clipped.leftPx,
      clipped.rightPx
    ));
  }
  const rect = recordValue(record.rect || record.bounds || record.bounding_rect || record.boundingRect);
  const viewportWidth = numericValue(record.viewport_width ?? record.viewportWidth);
  if (rect && viewportWidth !== null) {
    const left = numericValue(rect.left);
    const right = numericValue(rect.right);
    if (left !== null && left < 0) max = Math.max(max, Math.abs(left));
    if (right !== null && right > viewportWidth) max = Math.max(max, right - viewportWidth);
  }
  return roundPixels(max);
}
function boundsOffendersForEvidence(value) {
  const record = recordValue(value);
  if (!record) return [];
  const offenders = [
    ...listValue(record.overflow_offenders),
    ...listValue(record.overflowOffenders),
    ...listValue(record.bounds_offenders),
    ...listValue(record.boundsOffenders),
    ...listValue(record.clipped_elements),
    ...listValue(record.clippedElements),
    ...listValue(record.clipping_offenders),
    ...listValue(record.clippingOffenders)
  ];
  return offenders.filter((item) => Boolean(recordValue(item))).sort((a, b) => horizontalOffenderOverflowPx(b) - horizontalOffenderOverflowPx(a));
}
function maxPositiveNumber(...values) {
  let max = 0;
  for (const value of values) {
    const number = numericValue(value);
    if (number !== null && number > max) max = number;
  }
  return max;
}
function roundPixels(value) {
  return Math.round(value * 100) / 100;
}
function textMatches(text, pattern, flags) {
  if (!pattern) return Boolean(text);
  try {
    return new RegExp(String(pattern), typeof flags === "string" ? flags : "i").test(String(text ?? ""));
  } catch {
    return false;
  }
}
function hashChanged(before, after, keys) {
  if (!before || !after) return false;
  return keys.some((key) => {
    const beforeValue = stringValue(before[key]);
    const afterValue = stringValue(after[key]);
    return Boolean(beforeValue && afterValue && beforeValue !== afterValue);
  });
}
function screenshotArtifactIndex(artifacts) {
  const index = /* @__PURE__ */ new Map();
  for (const artifact of artifacts || []) {
    const hash = stringValue(artifact.sha256 || artifact.hash);
    if (!hash) continue;
    if (String(artifact.kind || "").toLowerCase() !== "screenshot" && !/\.png($|\?)/i.test(`${artifact.name || ""} ${artifact.path || ""} ${artifact.url || ""}`)) continue;
    const filename = artifactBasename(artifact.path || artifact.name || artifact.url);
    if (!filename) continue;
    index.set(filename.toLowerCase(), {
      ...artifact,
      sha256: hash
    });
  }
  return index;
}
function attachPhaseArtifactHash(routeEvidence, routeContract, phase, artifactsByName) {
  const suffix = PHASE_SCREENSHOT_SUFFIXES[phase];
  if (!suffix) return null;
  const metric = metricValue(routeEvidence[phase]);
  if (!metric) return null;
  const filename = `${routeArtifactSlug(routeEvidence, routeContract)}-${suffix}.png`.toLowerCase();
  const artifact = artifactsByName.get(filename);
  if (!artifact?.sha256) return null;
  metric.artifact_screenshot_hash = artifact.sha256;
  metric.artifact_screenshot = {
    name: artifact.name || artifactBasename(artifact.path || filename),
    path: artifact.path,
    url: artifact.url,
    kind: artifact.kind,
    sha256: artifact.sha256
  };
  return artifact.sha256;
}
function routeArtifactSlug(routeEvidence, routeContract) {
  return slug(routeContract.name || routeEvidence.name || routeContract.path || routeEvidence.path || "route");
}
function artifactBasename(value) {
  const raw = stringValue(value);
  if (!raw) return "";
  try {
    const url = new URL(raw);
    return url.pathname.split("/").filter(Boolean).pop() || "";
  } catch {
    return raw.split(/[\\/]/).filter(Boolean).pop() || raw;
  }
}
function compactCatchMetric(metric) {
  const value = metricValue(metric);
  if (!value) return null;
  return {
    phase: value.phase,
    text: value.text,
    number: value.number,
    count: value.count,
    present: value.present,
    state_path: value.state_path,
    state_call: value.state_call,
    property_path: value.property_path,
    screenshot_hash: value.screenshot_hash,
    visual_sample_hash: value.visual_sample_hash,
    viewport_screenshot_hash: value.viewport_screenshot_hash,
    artifact_screenshot_hash: value.artifact_screenshot_hash,
    dom_signature_hash: value.dom_signature_hash,
    body_text_hash: value.body_text_hash,
    first_canvas_hash: value.first_canvas_hash,
    first_canvas_visual_sample_hash: value.first_canvas_visual_sample_hash,
    visible_canvas_count: value.visible_canvas_count
  };
}
function slug(value) {
  return String(value || "artifact").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 96) || "artifact";
}
function countCodes(codes) {
  const counts = {};
  for (const code of codes) counts[code] = (counts[code] || 0) + 1;
  return counts;
}
function routeAssessmentMode(route) {
  const raw = route.assessment_mode ?? route.assessmentMode ?? route.proof_mode ?? route.proofMode ?? null;
  if (raw === null || raw === void 0) return route.audit === true ? "audit" : null;
  const mode = String(raw).trim().toLowerCase().replace(/_/g, "-");
  return mode || (route.audit === true ? "audit" : null);
}
function isAuditAssessmentRoute(route) {
  const mode = routeAssessmentMode(route);
  return route.audit === true || [
    "audit",
    "audit-only",
    "no-diff",
    "not-required",
    "profile",
    "report-only",
    "static"
  ].includes(String(mode || ""));
}
function firstNumber(...values) {
  for (const value of values) {
    const number = numericValue(value);
    if (number !== null) return number;
  }
  return null;
}
function numberValue(value) {
  return numericValue(value) ?? 0;
}
function numericValue(value) {
  if (typeof value === "number" && Number.isFinite(value)) return value;
  if (typeof value === "string" && value.trim()) {
    const parsed = Number(value);
    return Number.isFinite(parsed) ? parsed : null;
  }
  return null;
}
function stringValue(value) {
  return typeof value === "string" && value.length ? value : null;
}
function recordValue(value) {
  return value && typeof value === "object" && !Array.isArray(value) ? value : null;
}
function listValue(value) {
  return Array.isArray(value) ? value : [];
}
function parseJson(value) {
  const trimmed = value.trim();
  if (!trimmed || !/^[{[]/.test(trimmed)) return null;
  try {
    return JSON.parse(trimmed);
  } catch {
    return null;
  }
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
  BASIC_GAMEPLAY_ACTION_TYPES,
  BASIC_GAMEPLAY_PROGRESS_CHECK_TYPES,
  RIDDLE_PROOF_BASIC_GAMEPLAY_ASSESSMENT_VERSION,
  RIDDLE_PROOF_BASIC_GAMEPLAY_CATCH_VERSION,
  RIDDLE_PROOF_BASIC_GAMEPLAY_VERSION,
  assessBasicGameplayEvidence,
  assessBasicGameplayProgressionCheck,
  assessBasicGameplayProgressionChecks,
  assessBasicGameplayRoute,
  attachBasicGameplayArtifactScreenshotHashes,
  augmentBasicGameplayAssessmentWithProgressionChecks,
  compactBasicGameplayText,
  createBasicGameplayCatchRecords,
  createBasicGameplayCatchSummary,
  extractBasicGameplayEvidence,
  resolveBasicGameplayProgressionCheckWithArtifactScreenshots,
  sanitizeBasicGameplayJsonString
});
