Drop7 Research
approaches/d4-long-outcome/rollout-veto/d4-d2-rollout-veto-quality-extension.cppC++752 lines · 31.7 KB
#define DROP7_D4_D2_ROLLOUT_VETO_LIBRARY
#include "d4-d2-rollout-veto.cpp"
#undef DROP7_D4_D2_ROLLOUT_VETO_LIBRARY

#include <filesystem>
#include <fstream>
#include <sstream>
#include <thread>

// Measures gameplay quality for the unchanged D4 + D2/s7/h25 veto without
// changing its runtime or deployment eligibility.  It reads the fixed pilot
// and the three fitting seeds immediately following it.
namespace drop7::d4_d2_rollout_veto_quality_extension {

namespace policy = drop7::d4_d2_rollout_veto;
namespace d4 = drop7::fair_only_depth4;

constexpr std::uint32_t kFrozenPilotSeed = 0x3ded'0000u;
constexpr std::array<std::uint32_t, 3> kNewSeeds{{
    0x3ded'0001u,
    0x3ded'0002u,
    0x3ded'0003u,
}};
constexpr int kNewGames = static_cast<int>(kNewSeeds.size());
constexpr int kCombinedGames = 4;
constexpr int kCandidateWorkers = 3;
constexpr int kBaselineWorkers = 3;
constexpr double kWallLimitSeconds = 90.0 * 60.0;
constexpr double kLowerHalfRetention = 0.90;
constexpr int kMinimumNewJointWins = 2;
constexpr int kMinimumLeaveOneOutWins = 3;
constexpr std::uintmax_t kFrozenPilotBytes = 3'670;
constexpr std::string_view kFrozenPilotSha256 =
    "5841c90412d21c0a42ee6adc7a2233b3b585087bcc04f54fab7ef3274d3a607e";

static_assert(kNewSeeds[0] == policy::kFittingSeedStart + 1u);
static_assert(kNewSeeds[2] == policy::kFittingSeedStart + 3u);
static_assert(kNewSeeds[2] < policy::kHeldoutSeedStart);
static_assert((kFrozenPilotSeed >> 24) != 0x7du &&
              (kFrozenPilotSeed >> 24) != 0xd7u);
static_assert((kNewSeeds[0] >> 24) != 0x7du &&
              (kNewSeeds[0] >> 24) != 0xd7u);
static_assert(policy::kRolloutHorizon == 25);
static_assert(policy::kScenarios == 7);
static_assert(policy::kContinuationDepth == 2);
static_assert(policy::kDangerHeight == 4);
static_assert(policy::kMaximumRootQLoss == 7'000.0);
static_assert(policy::kMaximumRssBytes == 128u * 1024u * 1024u);
static_assert(d4::kCandidateDepth == 4);

struct Options {
  std::string frozen_pilot = "/tmp/drop7-d4-d2-rollout-veto.json";
  std::string output =
      "/tmp/drop7-d4-d2-rollout-veto-quality-extension.json";
};

Options parseOptions(int argc, char** argv, int begin) {
  Options result;
  for (int index = begin; index < argc; ++index) {
    const std::string argument = argv[index];
    if (argument == "--frozen-pilot") {
      if (++index >= argc) {
        throw std::invalid_argument("missing --frozen-pilot value");
      }
      result.frozen_pilot = argv[index];
    } else if (argument == "--output") {
      if (++index >= argc) throw std::invalid_argument("missing --output value");
      result.output = argv[index];
    } else {
      throw std::invalid_argument("unknown option " + argument);
    }
  }
  return result;
}

[[noreturn]] void malformed(std::string_view field) {
  throw std::runtime_error("malformed frozen pilot artifact: " +
                           std::string(field));
}

std::size_t requireFind(std::string_view text, std::string_view marker,
                        std::size_t begin = 0) {
  const std::size_t position = text.find(marker, begin);
  if (position == std::string_view::npos) malformed(marker);
  return position + marker.size();
}

std::size_t matchingBrace(std::string_view text, std::size_t open) {
  if (open >= text.size() || text[open] != '{') malformed("object start");
  int depth = 0;
  bool in_string = false;
  bool escaped = false;
  for (std::size_t index = open; index < text.size(); ++index) {
    const char value = text[index];
    if (in_string) {
      if (escaped) {
        escaped = false;
      } else if (value == '\\') {
        escaped = true;
      } else if (value == '"') {
        in_string = false;
      }
    } else if (value == '"') {
      in_string = true;
    } else if (value == '{') {
      ++depth;
    } else if (value == '}' && --depth == 0) {
      return index;
    }
  }
  malformed("unclosed object");
}

std::string_view objectAfter(std::string_view text, std::string_view marker,
                             std::size_t begin = 0) {
  const std::size_t content = requireFind(text, marker, begin);
  const std::size_t open = content - 1;
  const std::size_t close = matchingBrace(text, open);
  return text.substr(open, close - open + 1);
}

long long integerAfter(std::string_view text, std::string_view marker) {
  const std::size_t position = requireFind(text, marker);
  const std::string tail(text.substr(position));
  char* end = nullptr;
  const long long result = std::strtoll(tail.c_str(), &end, 10);
  if (end == tail.c_str()) malformed(marker);
  return result;
}

double doubleAfter(std::string_view text, std::string_view marker) {
  const std::size_t position = requireFind(text, marker);
  const std::string tail(text.substr(position));
  char* end = nullptr;
  const double result = std::strtod(tail.c_str(), &end);
  if (end == tail.c_str() || !std::isfinite(result)) malformed(marker);
  return result;
}

bool boolAfter(std::string_view text, std::string_view marker) {
  const std::size_t position = requireFind(text, marker);
  if (text.substr(position, 4) == "true") return true;
  if (text.substr(position, 5) == "false") return false;
  malformed(marker);
}

template <typename Target>
Target integerField(std::string_view object, std::string_view marker) {
  const long long value = integerAfter(object, marker);
  if (value < 0 ||
      static_cast<unsigned long long>(value) >
          std::numeric_limits<Target>::max()) {
    malformed(marker);
  }
  return static_cast<Target>(value);
}

policy::GameResult parseGame(std::string_view object) {
  policy::GameResult result;
  result.seed = integerField<std::uint32_t>(object, "\"seed\":");
  result.score = integerAfter(object, "\"score\":");
  result.moves = integerField<int>(object, "\"moves\":");
  result.censored = boolAfter(object, "\"censored\":");
  result.numbered_cleared =
      integerField<std::uint64_t>(object, "\"numberedCleared\":");
  result.covers_revealed =
      integerField<std::uint64_t>(object, "\"coversRevealed\":");
  result.maximum_chain = integerField<int>(object, "\"maximumChain\":");
  result.decisions =
      integerField<std::uint64_t>(object, "\"decisions\":");
  result.routed_decisions =
      integerField<std::uint64_t>(object, "\"routedDecisions\":");
  result.switches = integerField<std::uint64_t>(object, "\"switches\":");
  result.passing_alternatives =
      integerField<std::uint64_t>(object, "\"passingAlternatives\":");
  result.survivor_rejections =
      integerField<std::uint64_t>(object, "\"survivorRejections\":");
  result.clear_rejections =
      integerField<std::uint64_t>(object, "\"clearRejections\":");
  result.return_rejections =
      integerField<std::uint64_t>(object, "\"returnRejections\":");
  result.root_q_rejections =
      integerField<std::uint64_t>(object, "\"rootQRejections\":");
  result.d4_work = integerField<std::uint64_t>(object, "\"d4Work\":");
  result.d4_nodes = integerField<std::uint64_t>(object, "\"d4Nodes\":");
  result.d4_cache_hits =
      integerField<std::uint64_t>(object, "\"d4CacheHits\":");
  result.peak_d4_cache_entries =
      integerField<std::size_t>(object, "\"peakD4CacheEntries\":");
  result.synthetic_transitions =
      integerField<std::uint64_t>(object, "\"syntheticTransitions\":");
  result.continuation.calls =
      integerField<std::uint64_t>(object, "\"d2Calls\":");
  result.continuation.work =
      integerField<std::uint64_t>(object, "\"d2Work\":");
  result.continuation.nodes =
      integerField<std::uint64_t>(object, "\"d2Nodes\":");
  result.continuation.cache_hits =
      integerField<std::uint64_t>(object, "\"d2CacheHits\":");
  result.continuation.root_actions =
      integerField<std::uint64_t>(object, "\"d2RootActions\":");
  result.continuation.peak_cache_entries =
      integerField<std::size_t>(object, "\"peakD2CacheEntries\":");
  result.continuation.full_root = boolAfter(object, "\"d2FullRoot\":");
  result.elapsed_seconds = doubleAfter(object, "\"elapsedSeconds\":");
  result.peak_rss_bytes =
      integerField<std::uint64_t>(object, "\"peakRssBytes\":");
  return result;
}

struct FrozenPair {
  policy::GameResult baseline;
  policy::GameResult candidate;
};

FrozenPair loadFrozenPair(const std::string& path) {
  std::error_code error;
  if (std::filesystem::file_size(path, error) != kFrozenPilotBytes || error) {
    throw std::runtime_error("frozen pilot byte count mismatch");
  }
  std::ifstream input(path);
  if (!input) throw std::runtime_error("could not open frozen pilot artifact");
  const std::string document((std::istreambuf_iterator<char>(input)),
                             std::istreambuf_iterator<char>());
  if (document.find("\"pilotPairOnly\":true") == std::string::npos ||
      document.find("\"runtimeStop\":true") == std::string::npos ||
      document.find("\"readGameSeeds\":[\"0x3ded0000\"]") ==
          std::string::npos) {
    malformed("pilot provenance");
  }
  const std::size_t pair_begin = requireFind(document, "\"pairs\":[{");
  const std::string_view baseline =
      objectAfter(document, "\"baseline\":{", pair_begin);
  const std::size_t baseline_offset =
      static_cast<std::size_t>(baseline.data() - document.data());
  const std::string_view candidate = objectAfter(
      document, "\"candidate\":{", baseline_offset + baseline.size());
  FrozenPair result{parseGame(baseline), parseGame(candidate)};

  const std::size_t pilot_begin = requireFind(document, "\"pilot\":{");
  const std::string_view candidate_summary =
      objectAfter(document, "\"candidate\":{", pilot_begin);
  result.candidate.switch_return_lower95_sum =
      doubleAfter(candidate_summary, "\"meanSwitchReturnLower95\":") *
      static_cast<double>(result.candidate.switches);
  result.candidate.switch_clear_advantage_sum =
      doubleAfter(candidate_summary, "\"meanSwitchClearAdvantage\":") *
      static_cast<double>(result.candidate.switches);
  result.candidate.switch_root_q_loss_sum =
      doubleAfter(candidate_summary, "\"meanSwitchRootQLoss\":") *
      static_cast<double>(result.candidate.switches);
  result.candidate.switch_survivor_advantage_sum = static_cast<std::int64_t>(
      std::llround(doubleAfter(
                       candidate_summary,
                       "\"meanSwitchSurvivorAdvantage\":") *
                   static_cast<double>(result.candidate.switches)));

  if (result.baseline.seed != kFrozenPilotSeed ||
      result.candidate.seed != kFrozenPilotSeed ||
      result.baseline.score != 159'616 || result.baseline.moves != 105 ||
      result.candidate.score != 404'047 || result.candidate.moves != 250 ||
      result.candidate.switches != 12 ||
      result.candidate.routed_decisions != 179 ||
      result.baseline.censored || result.candidate.censored) {
    malformed("frozen pair values");
  }
  return result;
}

using Clock = std::chrono::steady_clock;

class WallLimitReached : public std::runtime_error {
 public:
  WallLimitReached() : std::runtime_error("quality-audit wall limit reached") {}
};

bool deadlineReached(Clock::time_point deadline) {
  return Clock::now() >= deadline;
}

policy::GameResult runAuthorizedGame(std::uint32_t seed, bool candidate,
                                     std::string_view label,
                                     Clock::time_point deadline) {
  if (std::find(kNewSeeds.begin(), kNewSeeds.end(), seed) == kNewSeeds.end()) {
    throw std::invalid_argument("game seed is outside quality-audit allowlist");
  }
  const auto started = Clock::now();
  State state = initialHeadlessState(seed);
  policy::GameResult result;
  result.seed = seed;
  while (!state.game_over && state.moves_played < policy::kMaximumMoves) {
    if (deadlineReached(deadline)) throw WallLimitReached{};
    d4::SearchDecision search;
    int action = -1;
    if (!candidate) {
      search = d4::chooseDepth4Action(state);
      action = search.action;
    } else {
      const policy::Decision decision = policy::chooseAction(state);
      search = decision.search;
      action = decision.action;
      result.danger_decisions += decision.danger;
      result.routed_decisions += decision.routed;
      result.switches += decision.switched;
      result.passing_alternatives += decision.passing_alternatives;
      result.survivor_rejections += decision.survivor_rejections;
      result.clear_rejections += decision.clear_rejections;
      result.return_rejections += decision.return_rejections;
      result.root_q_rejections += decision.root_q_rejections;
      if (decision.switched) {
        result.switch_return_lower95_sum +=
            decision.selected_return_lower95;
        result.switch_clear_advantage_sum +=
            decision.selected_clear_advantage;
        result.switch_root_q_loss_sum += decision.selected_root_q_loss;
        result.switch_survivor_advantage_sum +=
            decision.selected_survivor_advantage;
      }
      result.synthetic_transitions += decision.rollout.synthetic_transitions;
      result.continuation.calls += decision.rollout.continuation.calls;
      result.continuation.work += decision.rollout.continuation.work;
      result.continuation.nodes += decision.rollout.continuation.nodes;
      result.continuation.cache_hits +=
          decision.rollout.continuation.cache_hits;
      result.continuation.peak_cache_entries = std::max(
          result.continuation.peak_cache_entries,
          decision.rollout.continuation.peak_cache_entries);
      result.continuation.root_actions +=
          decision.rollout.continuation.root_actions;
      result.continuation.full_root =
          result.continuation.full_root &&
          decision.rollout.continuation.full_root;
    }
    if (deadlineReached(deadline)) throw WallLimitReached{};
    if (!search.complete || search.completed_depth != d4::kCandidateDepth ||
        !isLegal(state.board, action)) {
      throw std::runtime_error("quality-audit policy lost exact D4/legality");
    }
    ++result.decisions;
    result.d4_work += search.work;
    result.d4_nodes += search.nodes;
    result.d4_cache_hits += search.cache_hits;
    result.peak_d4_cache_entries =
        std::max(result.peak_d4_cache_entries, search.cache_entries);
    MoveResult move;
    if (!playHeadlessMove(state, seed, action, move)) {
      throw std::runtime_error("quality-audit real transition failed");
    }
    policy::observeMove(move, result);
  }
  result.score = state.score;
  result.moves = state.moves_played;
  result.censored = !state.game_over;
  result.peak_rss_bytes = d4::peakRssBytes();
  result.elapsed_seconds =
      std::chrono::duration<double>(Clock::now() - started).count();
  if (result.peak_rss_bytes > policy::kMaximumRssBytes ||
      result.peak_d4_cache_entries > d4::kMaximumCacheEntries ||
      result.continuation.peak_cache_entries >
          policy::kWorstD2CacheEntries ||
      !result.continuation.full_root) {
    throw std::runtime_error("quality-audit game exceeded resource proof");
  }
  policy::reportGame(label, result);
  return result;
}

struct ArmResult {
  std::array<policy::GameResult, kNewGames> games{};
  double wall_seconds = 0.0;
};

ArmResult runArm(bool candidate, int workers, Clock::time_point deadline,
                 std::string_view label) {
  if (workers != kNewGames) {
    throw std::invalid_argument("quality audit requires one worker per new game");
  }
  const auto started = Clock::now();
  ArmResult result;
  std::vector<std::future<void>> futures;
  futures.reserve(kNewGames);
  for (int index = 0; index < kNewGames; ++index) {
    futures.push_back(std::async(std::launch::async, [&, index] {
      result.games[static_cast<std::size_t>(index)] = runAuthorizedGame(
          kNewSeeds[static_cast<std::size_t>(index)], candidate,
          std::string(label), deadline);
    }));
  }
  std::exception_ptr exception;
  for (auto& future : futures) {
    try {
      future.get();
    } catch (...) {
      if (exception == nullptr) exception = std::current_exception();
    }
  }
  if (exception != nullptr) std::rethrow_exception(exception);
  result.wall_seconds =
      std::chrono::duration<double>(Clock::now() - started).count();
  return result;
}

struct QualityGate {
  bool mean_score_improved = false;
  bool mean_moves_improved = false;
  int new_score_wins = 0;
  int new_move_wins = 0;
  int new_joint_wins = 0;
  bool new_wins_ok = false;
  bool clears_per_move_improved = false;
  bool reveals_per_move_improved = false;
  bool lower_half_score_retained = false;
  bool lower_half_moves_retained = false;
  int leave_one_out_positive_both = 0;
  bool leave_one_out_ok = false;
  bool passed = false;
};

QualityGate evaluateQualityGate(const policy::Cohort& combined) {
  if (combined.baseline.size() != kCombinedGames ||
      combined.candidate.size() != kCombinedGames) {
    throw std::invalid_argument("quality gate requires four pairs");
  }
  const policy::Summary baseline = policy::summarize(combined.baseline);
  const policy::Summary candidate = policy::summarize(combined.candidate);
  const policy::Paired comparison = policy::paired(combined);
  QualityGate result;
  result.mean_score_improved = candidate.mean_score > baseline.mean_score;
  result.mean_moves_improved = candidate.mean_moves > baseline.mean_moves;
  for (int index = 1; index < kCombinedGames; ++index) {
    const policy::GameResult& left =
        combined.baseline[static_cast<std::size_t>(index)];
    const policy::GameResult& right =
        combined.candidate[static_cast<std::size_t>(index)];
    result.new_score_wins += right.score > left.score;
    result.new_move_wins += right.moves > left.moves;
    result.new_joint_wins +=
        right.score > left.score && right.moves > left.moves;
  }
  result.new_wins_ok = result.new_joint_wins >= kMinimumNewJointWins;
  result.clears_per_move_improved =
      candidate.clears_per_move > baseline.clears_per_move;
  result.reveals_per_move_improved =
      candidate.reveals_per_move > baseline.reveals_per_move;
  result.lower_half_score_retained =
      candidate.lower_half_score >=
      kLowerHalfRetention * baseline.lower_half_score;
  result.lower_half_moves_retained =
      candidate.lower_half_moves >=
      kLowerHalfRetention * baseline.lower_half_moves;
  result.leave_one_out_positive_both =
      comparison.leave_one_out_positive_both;
  result.leave_one_out_ok =
      result.leave_one_out_positive_both >= kMinimumLeaveOneOutWins;
  result.passed =
      result.mean_score_improved && result.mean_moves_improved &&
      result.new_wins_ok && result.clears_per_move_improved &&
      result.reveals_per_move_improved &&
      result.lower_half_score_retained &&
      result.lower_half_moves_retained && result.leave_one_out_ok;
  return result;
}

void expect(bool condition, std::string_view message) {
  if (!condition) throw std::runtime_error(std::string(message));
}

bool selfTest(const Options& options, std::ostream& output) {
  const bool dependency = policy::selfTest(output);
  const FrozenPair frozen = loadFrozenPair(options.frozen_pilot);
  const policy::Summary baseline = policy::summarize({frozen.baseline});
  const policy::Summary candidate = policy::summarize({frozen.candidate});
  expect(baseline.mean_score == 159'616.0 && baseline.mean_moves == 105.0 &&
             candidate.mean_score == 404'047.0 &&
             candidate.mean_moves == 250.0 &&
             candidate.switches == 12,
         "frozen-pair reconstruction");
  expect(!deadlineReached(Clock::now() + std::chrono::seconds(1)) &&
             deadlineReached(Clock::now() - std::chrono::seconds(1)),
         "wall deadline wiring");
  expect(std::adjacent_find(kNewSeeds.begin(), kNewSeeds.end()) ==
                 kNewSeeds.end() &&
             std::all_of(kNewSeeds.begin(), kNewSeeds.end(), [](auto seed) {
               return seed >= 0x3ded'0001u && seed <= 0x3ded'0003u &&
                      (seed >> 24) != 0x7du && (seed >> 24) != 0xd7u;
             }),
         "seed allowlist wiring");

  policy::Cohort synthetic;
  for (int index = 0; index < kCombinedGames; ++index) {
    policy::GameResult left;
    left.seed = static_cast<std::uint32_t>(index);
    left.score = 100;
    left.moves = 10;
    left.numbered_cleared = 10;
    left.covers_revealed = 10;
    policy::GameResult right = left;
    right.score = 110;
    right.moves = 11;
    right.numbered_cleared = 13;
    right.covers_revealed = 13;
    synthetic.baseline.push_back(left);
    synthetic.candidate.push_back(right);
  }
  const QualityGate passing = evaluateQualityGate(synthetic);
  synthetic.candidate[2].score = 50;
  synthetic.candidate[2].moves = 5;
  synthetic.candidate[3].score = 50;
  synthetic.candidate[3].moves = 5;
  const QualityGate failing = evaluateQualityGate(synthetic);
  expect(passing.passed && passing.new_joint_wins == 3 &&
             passing.leave_one_out_positive_both == 4 && !failing.passed &&
             !failing.new_wins_ok,
         "quality gate boundaries");

  // Sanitizer shadow/quarantine memory is intentionally not compared with the
  // runtime 128 MiB cap.  Every optimized game enforces that cap in the game
  // runner, while the included self-test checks the per-search
  // D4/D2 cache and work bounds under either build.
  const bool passed = dependency;
  output << std::setprecision(12)
         << "D4_D2_ROLLOUT_VETO_QUALITY_EXTENSION_SELF_TEST {\"passed\":"
         << (passed ? "true" : "false")
         << ",\"unchangedPolicyDependency\":"
         << (dependency ? "true" : "false")
         << ",\"frozenPilotLoadedNotReplayed\":true"
         << ",\"seedAllowlistExact\":true"
         << ",\"qualityGateWiring\":true"
         << ",\"wallLimitWiring\":true"
         << ",\"productionRssCapEnforcedAtGameBoundary\":true"
         << ",\"newSeeds\":[\"0x3ded0001\",\"0x3ded0002\",\"0x3ded0003\"]"
         << ",\"rolloutHorizon\":" << policy::kRolloutHorizon
         << ",\"scenarios\":" << policy::kScenarios
         << ",\"continuationDepth\":" << policy::kContinuationDepth
         << ",\"peakRssBytes\":" << d4::peakRssBytes() << "}\n";
  return passed;
}

void writeProtocol(std::ostream& output, std::string_view status) {
  output << std::setprecision(12)
         << "{\n  \"experiment\":\"d4-d2-rollout-veto-quality-extension\",\n"
         << "  \"status\":\"" << status << "\",\n"
         << "  \"qualityOnly\":true,\n"
         << "  \"deployable\":false,\n"
         << "  \"runtimeGateStillRejected\":true,\n"
         << "  \"oldRuntimeGateIgnoredOnlyForThisAudit\":true,\n"
         << "  \"policyUnchanged\":{\"root\":\"fair-D4\",\"dangerHeight\":"
         << policy::kDangerHeight
         << ",\"rollout\":\"D2/s7/h25\",\"continuationDepth\":"
         << policy::kContinuationDepth
         << ",\"scenarios\":" << policy::kScenarios
         << ",\"horizon\":" << policy::kRolloutHorizon
         << ",\"rootQLossMaximum\":" << policy::kMaximumRootQLoss
         << ",\"policySourceSha256\":\"2f1018304d9cd2729bdf0c2ac552e2c8e9d0976374675f1be97257c81927f433\"},\n"
         << "  \"frozenPilot\":{\"seed\":\"0x3ded0000\",\"reexecuted\":false"
         << ",\"bytes\":" << kFrozenPilotBytes
         << ",\"sha256\":\"" << kFrozenPilotSha256 << "\"},\n"
         << "  \"newGameplaySeedsAuthorized\":[\"0x3ded0001\",\"0x3ded0002\",\"0x3ded0003\"],\n"
         << "  \"prohibitedUntouched\":[\"0x3dee... heldout\",\"0x3e... all\",\"0x7d... protected\",\"0xd7... protected\"],\n"
         << "  \"workers\":{\"baseline\":" << kBaselineWorkers
         << ",\"candidate\":" << kCandidateWorkers << "},\n"
         << "  \"wallLimitSeconds\":" << kWallLimitSeconds << ",\n"
         << "  \"frozenQualityGate\":{\"meanScoreImproved\":true"
         << ",\"meanMovesImproved\":true"
         << ",\"minimumNewJointScoreAndMoveWins\":"
         << kMinimumNewJointWins
         << ",\"newPairs\":" << kNewGames
         << ",\"clearsPerMoveImproved\":true"
         << ",\"revealsPerMoveImproved\":true"
         << ",\"lowerHalfScoreAndMovesRetention\":"
         << kLowerHalfRetention
         << ",\"minimumLeaveOneOutPositiveScoreAndMoves\":"
         << kMinimumLeaveOneOutWins << ",\"leaveOneOutSubsets\":"
         << kCombinedGames << "}";
}

void writeStatusArtifact(const Options& options, std::string_view status,
                         double wall_seconds) {
  std::ofstream output(options.output);
  if (!output) throw std::runtime_error("could not write quality status artifact");
  writeProtocol(output, status);
  output << ",\n  \"wallSeconds\":" << wall_seconds
         << ",\n  \"peakRssBytes\":" << d4::peakRssBytes() << "\n}\n";
}

void writeGate(std::ostream& output, const QualityGate& gate) {
  output << "{\"meanScoreImproved\":"
         << (gate.mean_score_improved ? "true" : "false")
         << ",\"meanMovesImproved\":"
         << (gate.mean_moves_improved ? "true" : "false")
         << ",\"newScoreWins\":" << gate.new_score_wins
         << ",\"newMoveWins\":" << gate.new_move_wins
         << ",\"newJointScoreAndMoveWins\":" << gate.new_joint_wins
         << ",\"newWinsOk\":" << (gate.new_wins_ok ? "true" : "false")
         << ",\"clearsPerMoveImproved\":"
         << (gate.clears_per_move_improved ? "true" : "false")
         << ",\"revealsPerMoveImproved\":"
         << (gate.reveals_per_move_improved ? "true" : "false")
         << ",\"lowerHalfScoreRetained\":"
         << (gate.lower_half_score_retained ? "true" : "false")
         << ",\"lowerHalfMovesRetained\":"
         << (gate.lower_half_moves_retained ? "true" : "false")
         << ",\"leaveOneOutPositiveScoreAndMoves\":"
         << gate.leave_one_out_positive_both
         << ",\"leaveOneOutOk\":"
         << (gate.leave_one_out_ok ? "true" : "false")
         << ",\"passed\":" << (gate.passed ? "true" : "false") << '}';
}

void writeCompleteArtifact(const Options& options,
                           const policy::Cohort& combined,
                           const ArmResult& baseline_arm,
                           const ArmResult& candidate_arm,
                           const QualityGate& gate, double total_wall) {
  const policy::Summary baseline = policy::summarize(combined.baseline);
  const policy::Summary candidate = policy::summarize(combined.candidate);
  const policy::Paired comparison = policy::paired(combined);
  std::ofstream output(options.output);
  if (!output) throw std::runtime_error("could not write quality artifact");
  writeProtocol(output, "complete");
  output << ",\n  \"newRun\":{\"baselineWallSeconds\":"
         << baseline_arm.wall_seconds
         << ",\"candidateWallSeconds\":" << candidate_arm.wall_seconds
         << ",\"newPairs\":[";
  for (int index = 0; index < kNewGames; ++index) {
    if (index != 0) output << ',';
    output << "{\"baseline\":";
    policy::writeGame(output,
                      baseline_arm.games[static_cast<std::size_t>(index)]);
    output << ",\"candidate\":";
    policy::writeGame(output,
                      candidate_arm.games[static_cast<std::size_t>(index)]);
    output << '}';
  }
  output << "]},\n  \"combinedFourGameFitting\":{\"baseline\":";
  policy::writeSummary(output, baseline);
  output << ",\"candidate\":";
  policy::writeSummary(output, candidate);
  output << ",\"paired\":{\"score\":";
  policy::writeDifference(output, comparison.score);
  output << ",\"moves\":";
  policy::writeDifference(output, comparison.moves);
  output << ",\"leaveOneOutPositiveBoth\":"
         << comparison.leave_one_out_positive_both << "},\"pairs\":[";
  for (int index = 0; index < kCombinedGames; ++index) {
    if (index != 0) output << ',';
    output << "{\"baseline\":";
    policy::writeGame(output,
                      combined.baseline[static_cast<std::size_t>(index)]);
    output << ",\"candidate\":";
    policy::writeGame(output,
                      combined.candidate[static_cast<std::size_t>(index)]);
    output << '}';
  }
  output << "]},\n  \"qualityGate\":";
  writeGate(output, gate);
  output << ",\n  \"qualityConclusion\":\""
         << (gate.passed ? "passed" : "failed") << "\""
         << ",\n  \"deploymentQualified\":false"
         << ",\n  \"totalWallSeconds\":" << total_wall
         << ",\n  \"peakRssBytes\":" << d4::peakRssBytes() << "\n}\n";
}

int run(const Options& options, std::ostream& report) {
  const auto all_started = Clock::now();
  if (!selfTest(options, report)) return EXIT_FAILURE;
  const FrozenPair frozen = loadFrozenPair(options.frozen_pilot);
  writeStatusArtifact(options, "preregistered-before-new-seeds", 0.0);
  const Clock::time_point deadline =
      all_started + std::chrono::duration_cast<Clock::duration>(
                        std::chrono::duration<double>(kWallLimitSeconds));
  try {
    // Baselines are short and are completed first; the three expensive
    // unchanged candidates then run concurrently, one allowlisted tape each.
    const ArmResult baseline_arm =
        runArm(false, kBaselineWorkers, deadline, "quality-baseline");
    if (deadlineReached(deadline)) throw WallLimitReached{};
    const ArmResult candidate_arm =
        runArm(true, kCandidateWorkers, deadline, "quality-candidate");
    const double total_wall =
        std::chrono::duration<double>(Clock::now() - all_started).count();
    if (total_wall > kWallLimitSeconds) throw WallLimitReached{};

    policy::Cohort combined;
    combined.baseline.push_back(frozen.baseline);
    combined.candidate.push_back(frozen.candidate);
    for (int index = 0; index < kNewGames; ++index) {
      combined.baseline.push_back(
          baseline_arm.games[static_cast<std::size_t>(index)]);
      combined.candidate.push_back(
          candidate_arm.games[static_cast<std::size_t>(index)]);
    }
    combined.wall_seconds = total_wall;
    const QualityGate gate = evaluateQualityGate(combined);
    writeCompleteArtifact(options, combined, baseline_arm, candidate_arm,
                          gate, total_wall);
    const policy::Summary baseline = policy::summarize(combined.baseline);
    const policy::Summary candidate = policy::summarize(combined.candidate);
    report << std::fixed << std::setprecision(6)
           << "D4_D2_ROLLOUT_VETO_QUALITY_EXTENSION_RESULT {\"qualityPassed\":"
           << (gate.passed ? "true" : "false")
           << ",\"deploymentQualified\":false"
           << ",\"baselineMeanScore\":" << baseline.mean_score
           << ",\"candidateMeanScore\":" << candidate.mean_score
           << ",\"baselineMeanMoves\":" << baseline.mean_moves
           << ",\"candidateMeanMoves\":" << candidate.mean_moves
           << ",\"newJointWins\":" << gate.new_joint_wins
           << ",\"leaveOneOutPositiveBoth\":"
           << gate.leave_one_out_positive_both
           << ",\"totalWallSeconds\":" << total_wall
           << ",\"peakRssBytes\":" << d4::peakRssBytes()
           << ",\"output\":\"" << options.output << "\"}\n";
    return EXIT_SUCCESS;
  } catch (const WallLimitReached&) {
    const double wall =
        std::chrono::duration<double>(Clock::now() - all_started).count();
    writeStatusArtifact(options, "stopped-at-90-minute-wall-limit", wall);
    report << "D4_D2_ROLLOUT_VETO_QUALITY_EXTENSION_STOP {\"wallLimit\":true,\"deploymentQualified\":false,\"wallSeconds\":"
           << wall << "}\n";
    return 2;
  }
}

}  // namespace drop7::d4_d2_rollout_veto_quality_extension

#ifndef DROP7_D4_D2_ROLLOUT_VETO_QUALITY_EXTENSION_LIBRARY
int main(int argc, char** argv) {
  try {
    if (argc < 2) throw std::invalid_argument("missing mode");
    const std::string mode = argv[1];
    const auto options =
        drop7::d4_d2_rollout_veto_quality_extension::parseOptions(argc, argv,
                                                                  2);
    if (mode == "--self-test") {
      return drop7::d4_d2_rollout_veto_quality_extension::selfTest(
                 options, std::cout)
                 ? EXIT_SUCCESS
                 : EXIT_FAILURE;
    }
    if (mode == "--run") {
      return drop7::d4_d2_rollout_veto_quality_extension::run(options,
                                                               std::cout);
    }
    throw std::invalid_argument(
        "usage: drop7_d4_d2_rollout_veto_quality_extension "
        "--self-test|--run [--frozen-pilot path] [--output path]");
  } catch (const std::exception& error) {
    std::cerr << "drop7 quality-extension error: " << error.what() << '\n';
    return EXIT_FAILURE;
  }
}
#endif