"""First staged logarithmic-N extension of the frozen AP1-M1 G29 reference.

This module does not alter the frozen AME-1 v7.13 or M-1 equations, parameters,
or any pre-existing gate.  It imports the hash-bound G29 coupled moving-split
solver and extends its causal prefix in two predeclared stages.  The pilot is
gated only on N in [0.0225, 0.03]; after a separate tolerance freeze, the
held-out gate is evaluated only on N in [0.0325, 0.04].  Both prefixes retain
the G29 fine and coarse N spacings exactly.

Only aggregate diagnostics may be serialized.  No trajectory row, endpoint
state capsule, new seed, present-anchor claim, response kernel, observable,
curve, fit, or significance is released.
"""
from __future__ import annotations

import argparse
from dataclasses import asdict, dataclass, replace
from datetime import datetime, timezone
from hashlib import sha256
import json
import math
from pathlib import Path
from typing import Any

import numpy as np

import ap1_m1_coupled_moving_split_background as g29


EXPECTED_AUTHORITIES = {
    "AP1/APEIRON_AP1_M1_COUPLED_MOVING_SPLIT_BACKGROUND_PILOT_LATEST.json": (
        "ede9e6def57d7aa017b7883050ae33b181a6d30c7107d212b952cfdd7b8c0da7"
    ),
    "AP1/APEIRON_AP1_M1_COUPLED_MOVING_SPLIT_BACKGROUND_TOLERANCES_LATEST.json": (
        "42aca6dd5678ba0a42219fdc21a2b813f5f96bb661792cb73dc50945a5bdefdb"
    ),
    "AP1/APEIRON_AP1_M1_COUPLED_MOVING_SPLIT_BACKGROUND_HELDOUT_LATEST.json": (
        "ee69c4a0aa8916eec6d078b5c075974a999cea7a6c02447ef2fcd52e1fb0baaa"
    ),
    "AP1/APEIRON_AP1_M1_COUPLED_MOVING_SPLIT_BACKGROUND_REFERENCE_CHECKPOINT_LATEST.json": (
        "c2f90da969a139fbb690b26fc12e821a6dcf82e126dffa0b1794a3ff6fd64cd9"
    ),
    "AP1/CODE/ap1_m1_coupled_moving_split_background.py": (
        "3b6f2b38d047db3cb9510af9c2c92ef9c80c2c1ca16c40212f7e7c354a76f400"
    ),
}


class StagedExtensionError(RuntimeError):
    """A chronology, authority, numerical, or physical stage gate failed."""


@dataclass(frozen=True)
class StageConfig:
    baseline_span_N: float = 0.02
    pilot_span_N: float = 0.03
    pilot_evaluation_start_N: float = 0.0225
    pilot_fine_nodes: int = 1537
    heldout_span_N: float = 0.04
    heldout_evaluation_start_N: float = 0.0325
    heldout_fine_nodes: int = 2049
    qcut_over_Lambda: float = 0.6
    adiabatic_support_stride: int = 1
    adiabatic_momentum_support_nodes: int = 257
    fixed_point_steps: int = 6
    relaxation: float = 0.9
    fine_entry_nodes_per_panel: int = 2
    coarse_entry_nodes_per_panel: int = 1
    fine_tail_nodes_per_octave: int = 4
    coarse_tail_nodes_per_octave: int = 2
    fine_K_over_Lambda: float = 64.0
    coarse_K_over_Lambda: float = 32.0
    fine_mode_step_N: float = 1.25e-5
    coarse_mode_step_N: float = 2.5e-5
    fine_background_rtol: float = 2.0e-10
    coarse_background_rtol: float = 2.0e-9
    decimal_low_digits: int = 60
    decimal_high_digits: int = 80

    def validate(self) -> None:
        if not (
            0.0 < self.baseline_span_N
            < self.pilot_evaluation_start_N
            < self.pilot_span_N
            < self.heldout_evaluation_start_N
            < self.heldout_span_N
        ):
            raise ValueError("ordered disjoint staged evaluation windows required")
        if self.baseline_span_N != g29.CoupledConfig().heldout_span_N:
            raise ValueError("G29 held-out endpoint must be the stage baseline")
        if self.qcut_over_Lambda != 0.6:
            raise ValueError("the frozen moving split is q_cut/Lambda = 0.6")
        for span, nodes in (
            (self.pilot_span_N, self.pilot_fine_nodes),
            (self.heldout_span_N, self.heldout_fine_nodes),
        ):
            if nodes < 17 or nodes % 2 != 1 or (nodes - 1) % 512:
                raise ValueError("integer multiples of the G29 512-panel fine grid required")
            if (nodes - 1) % self.adiabatic_support_stride:
                raise ValueError("adiabatic support must be nested in the fine grid")
        fine_steps = (
            self.pilot_span_N / (self.pilot_fine_nodes - 1),
            self.heldout_span_N / (self.heldout_fine_nodes - 1),
        )
        if not math.isclose(fine_steps[0], fine_steps[1], rel_tol=0.0, abs_tol=1e-18):
            raise ValueError("pilot and held-out fine N spacing must be identical")
        if not math.isclose(
            fine_steps[0],
            g29.CoupledConfig().heldout_span_N
            / (g29.CoupledConfig().heldout_fine_nodes - 1),
            rel_tol=0.0,
            abs_tol=1e-18,
        ):
            raise ValueError("the G29 fine N spacing must remain unchanged")
        frozen = g29.CoupledConfig()
        for name in (
            "adiabatic_support_stride", "adiabatic_momentum_support_nodes",
            "fixed_point_steps", "relaxation", "fine_entry_nodes_per_panel",
            "coarse_entry_nodes_per_panel", "fine_tail_nodes_per_octave",
            "coarse_tail_nodes_per_octave", "fine_K_over_Lambda",
            "coarse_K_over_Lambda", "fine_mode_step_N", "coarse_mode_step_N",
            "fine_background_rtol", "coarse_background_rtol",
            "decimal_low_digits", "decimal_high_digits",
        ):
            if getattr(self, name) != getattr(frozen, name):
                raise ValueError(f"G29 numerical setting changed: {name}")


STAGE_FREEZE_POLICY: dict[str, Any] = {
    "metrics": {
        **{name: dict(policy) for name, policy in g29.FREEZE_POLICY["metrics"].items()},
        "max_stage_overlap_H_relative_delta": {
            "multiplier": 64.0,
            "floor": 1.0e-10,
            "ceiling": 1.0e-5,
        },
    },
    "overlap_H_pre_freeze_cap": 1.0e-5,
    "max_heldout_resolved_modes": 5000,
    "minimum_extension_factor_over_G29": 2.0,
}


def file_sha256(path: Path) -> str:
    digest = sha256()
    with path.open("rb") as stream:
        for block in iter(lambda: stream.read(1024 * 1024), b""):
            digest.update(block)
    return digest.hexdigest()


def _utc_now() -> str:
    return datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")


def _write_pass_json(report: dict[str, Any], output: Path, pass_key: str) -> None:
    if not report.get(pass_key):
        raise StagedExtensionError("NONPASS staged report was not written")
    output.parent.mkdir(parents=True, exist_ok=True)
    output.write_text(json.dumps(report, indent=2) + "\n", encoding="utf-8")


def load_authorities(apeiron_root: Path) -> tuple[dict[str, Any], dict[str, np.ndarray]]:
    root = apeiron_root.resolve()
    for relative, expected in EXPECTED_AUTHORITIES.items():
        path = root / relative
        if file_sha256(path) != expected:
            raise StagedExtensionError(f"authority hash mismatch: {relative}")
    checkpoint_path = root / (
        "AP1/APEIRON_AP1_M1_COUPLED_MOVING_SPLIT_BACKGROUND_"
        "REFERENCE_CHECKPOINT_LATEST.json"
    )
    checkpoint = json.loads(checkpoint_path.read_text(encoding="utf-8"))
    if not checkpoint.get("all_checkpoint_gates_pass"):
        raise StagedExtensionError("G29 checkpoint is not PASS")
    if checkpoint.get("production_background_tolerances_frozen"):
        raise StagedExtensionError("unexpected production tolerance claim in G29")
    _, arrays, _ = g29.load_inputs(root)
    return checkpoint, arrays


def _coupled_config(config: StageConfig) -> g29.CoupledConfig:
    return replace(
        g29.CoupledConfig(),
        pilot_span_N=config.pilot_span_N,
        pilot_fine_nodes=config.pilot_fine_nodes,
        heldout_span_N=config.heldout_span_N,
        heldout_fine_nodes=config.heldout_fine_nodes,
        heldout_evaluation_start_N=config.heldout_evaluation_start_N,
    )


def _grid_value(run: dict[str, Any], n_value: float, field: str) -> float:
    trajectory = run["trajectory"]
    index = int(np.argmin(np.abs(np.asarray(trajectory.N) - n_value)))
    if abs(float(trajectory.N[index]) - n_value) > 1.0e-14:
        raise StagedExtensionError("stage overlap point is not on the nested grid")
    return float(np.asarray(getattr(trajectory, field))[index])


def _evaluate_stage(
    arrays: dict[str, np.ndarray],
    config: StageConfig,
    *,
    heldout: bool,
    overlap_reference: dict[str, float],
) -> dict[str, Any]:
    coupled = _coupled_config(config)
    span = config.heldout_span_N if heldout else config.pilot_span_N
    fine_nodes = config.heldout_fine_nodes if heldout else config.pilot_fine_nodes
    coarse_nodes = (fine_nodes + 1) // 2
    evaluation_start = (
        config.heldout_evaluation_start_N if heldout
        else config.pilot_evaluation_start_N
    )
    fine_support = (fine_nodes - 1) // config.adiabatic_support_stride + 1
    coarse_support = (coarse_nodes - 1) // config.adiabatic_support_stride + 1
    fine = g29.run_coupled(
        arrays, span, fine_nodes, coupled,
        background_rtol=config.fine_background_rtol,
        max_mode_step=config.fine_mode_step_N,
        entry_nodes_per_panel=config.fine_entry_nodes_per_panel,
        tail_nodes_per_octave=config.fine_tail_nodes_per_octave,
        K_over_Lambda=config.fine_K_over_Lambda,
        adiabatic_support_nodes=fine_support,
        adiabatic_momentum_support_nodes=config.adiabatic_momentum_support_nodes,
    )
    coarse = g29.run_coupled(
        arrays, span, coarse_nodes, coupled,
        background_rtol=config.coarse_background_rtol,
        max_mode_step=config.coarse_mode_step_N,
        entry_nodes_per_panel=config.fine_entry_nodes_per_panel,
        tail_nodes_per_octave=config.fine_tail_nodes_per_octave,
        K_over_Lambda=config.fine_K_over_Lambda,
        adiabatic_support_nodes=coarse_support,
        adiabatic_momentum_support_nodes=config.adiabatic_momentum_support_nodes,
    )
    physical = g29.physical_diagnostics(fine, arrays, evaluation_start)
    time_metrics = g29.time_convergence_metrics(fine, coarse, evaluation_start)
    operator = g29.operator_convergence_metrics(fine, arrays, coupled, evaluation_start)
    precision = g29.decimal_precision_metrics(
        fine, evaluation_start, config.decimal_low_digits, config.decimal_high_digits
    )
    overlap_N = float(overlap_reference["N_relative"])
    overlap_expected = float(overlap_reference["H_Mpl"])
    overlap_observed = _grid_value(fine, overlap_N, "H")
    overlap_delta = abs(overlap_observed - overlap_expected) / max(
        abs(overlap_observed), abs(overlap_expected), 1.0e-300
    )
    source = fine["source_diagnostics"]
    metrics = {
        "max_fixed_point_source_gross_relative_change": float(
            fine["validation_source_gross_relative_change"]
        ),
        "max_background_time_relative_delta": float(
            time_metrics["max_background_time_relative_delta"]
        ),
        "max_source_time_gross_relative_delta": float(
            time_metrics["max_source_time_gross_relative_delta"]
        ),
        "max_entry_quadrature_gross_relative_delta": float(
            operator["max_entry_quadrature_gross_relative_delta"]
        ),
        "max_tail_quadrature_gross_relative_delta": float(
            operator["max_tail_quadrature_gross_relative_delta"]
        ),
        "max_uv_cutoff_gross_relative_delta": float(
            operator["max_uv_cutoff_gross_relative_delta"]
        ),
        "max_friedmann_normalized": float(physical["max_friedmann_normalized"]),
        "max_raychaudhuri_normalized": float(physical["max_raychaudhuri_normalized"]),
        "ward_normalized": float(physical["ward_normalized"]),
        "standard_continuity_normalized": float(
            physical["standard_continuity_normalized"]
        ),
        "max_stage_overlap_H_relative_delta": float(overlap_delta),
    }
    endpoint = {
        "N_relative": float(span),
        "physical_N": float(physical["physical_N_endpoint"]),
        "H_Mpl": float(physical["H_endpoint_Mpl"]),
        "delta_H_over_H": float(physical["delta_H_over_H"]),
    }
    return {
        "phase": "heldout" if heldout else "pilot",
        "gate_interval_N": [float(evaluation_start), float(span)],
        "fine_run_config": fine["config"],
        "coarse_time_run_config": coarse["config"],
        "fine_iteration_log": fine["iteration_log"],
        "coarse_iteration_log": coarse["iteration_log"],
        "physical_diagnostics": physical,
        "time_convergence": time_metrics,
        "operator_convergence": operator,
        "precision": precision,
        "source_diagnostics": source,
        "overlap_audit": {
            "reference_N_relative": overlap_N,
            "reference_H_Mpl": overlap_expected,
            "observed_H_Mpl": overlap_observed,
            "relative_delta": overlap_delta,
        },
        "stage_endpoint_aggregate_not_a_seed": endpoint,
        "metrics_for_freeze_or_gate": metrics,
        "trajectory_rows_persisted": 0,
        "new_AP1_M1_background_runs": 2,
    }


def _absolute_stage_gates(ensemble: dict[str, Any], config: StageConfig) -> dict[str, bool]:
    base_gates = g29._absolute_gates(ensemble)
    resolved = ensemble["source_diagnostics"]["resolved"]
    return {
        **base_gates,
        "overlap_reproduction_below_predeclared_cap": bool(
            ensemble["metrics_for_freeze_or_gate"]["max_stage_overlap_H_relative_delta"]
            <= STAGE_FREEZE_POLICY["overlap_H_pre_freeze_cap"]
        ),
        "resolved_mode_budget_bounded": bool(
            resolved["final_resolved_modes"]
            <= STAGE_FREEZE_POLICY["max_heldout_resolved_modes"]
        ),
        "no_trajectory_or_seed_materialized": bool(
            ensemble["trajectory_rows_persisted"] == 0
            and resolved["vacuum_resets"] == 0
        ),
        "heldout_horizon_at_least_doubles_G29": bool(
            config.heldout_span_N / config.baseline_span_N
            >= STAGE_FREEZE_POLICY["minimum_extension_factor_over_G29"]
        ),
    }


def build_pilot(
    apeiron_root: Path,
    code_path: Path,
    config: StageConfig | None = None,
) -> dict[str, Any]:
    config = StageConfig() if config is None else config
    config.validate()
    checkpoint, arrays = load_authorities(apeiron_root)
    overlap = {
        "N_relative": config.baseline_span_N,
        "H_Mpl": checkpoint["heldout_physical_diagnostics"]["H_endpoint_Mpl"],
    }
    ensemble = _evaluate_stage(
        arrays, config, heldout=False, overlap_reference=overlap
    )
    absolute = _absolute_stage_gates(ensemble, config)
    numerical = {
        f"{name}_below_predeclared_ceiling": float(value)
        <= float(STAGE_FREEZE_POLICY["metrics"][name]["ceiling"])
        for name, value in ensemble["metrics_for_freeze_or_gate"].items()
    }
    gates = {
        "G29_hash_chain_bound_and_PASS": True,
        "pilot_gate_interval_strictly_beyond_G29": bool(
            config.pilot_evaluation_start_N > config.baseline_span_N
        ),
        "heldout_gate_interval_predeclared_and_disjoint": bool(
            config.heldout_evaluation_start_N > config.pilot_span_N
        ),
        "G29_N_spacing_and_numerics_unchanged": True,
        **absolute,
        **numerical,
        "physical_kernel_present_anchor_and_production_outputs_locked": True,
    }
    passed = bool(all(gates.values()))
    return {
        "schema": "apeiron-ap1-m1-staged-log-background-pilot-v1.0",
        "updated_utc": _utc_now(),
        "classification": (
            "STAGED_LOG_BACKGROUND_EXTENSION_PILOT_PASS_TOLERANCE_FREEZE_OPEN"
            if passed else "STAGED_LOG_BACKGROUND_EXTENSION_PILOT_INCOMPLETE"
        ),
        "authority_sha256": {
            **EXPECTED_AUTHORITIES,
            "AP1/CODE/ap1_m1_staged_log_background_extension.py": file_sha256(code_path),
        },
        "config": asdict(config),
        "predeclared_stage_freeze_policy": STAGE_FREEZE_POLICY,
        "pilot_ensemble": ensemble,
        "gates": gates,
        "all_pilot_gates_pass": passed,
        "heldout_evaluated": False,
        "checkpoint_eligible": False,
        "new_AP1_M1_background_runs": ensemble["new_AP1_M1_background_runs"],
        "production_background_tolerances_frozen": False,
        "trajectory_rows_persisted": 0,
        "seed_released": False,
        "physical_response_kernel_started": False,
        "nonpass_stored_or_used_as_seed": False,
        "equations_changed": False,
        "physics_changed": False,
        "parameters_changed": False,
        "existing_gate_thresholds_changed": False,
        "claim_boundary": "first staged logarithmic prefix pilot only; no production background, present anchor, kernel, observable, curve, fit or significance",
        "next_required": "freeze pilot-derived staged-extension tolerances before evaluating the predeclared disjoint held-out gate interval",
    }


def build_freeze(pilot_path: Path, code_path: Path) -> dict[str, Any]:
    pilot = json.loads(pilot_path.read_text(encoding="utf-8"))
    if not pilot.get("all_pilot_gates_pass") or pilot.get("heldout_evaluated"):
        raise StagedExtensionError("complete pre-held-out stage pilot required")
    code_hash = file_sha256(code_path)
    if pilot["authority_sha256"].get(
        "AP1/CODE/ap1_m1_staged_log_background_extension.py"
    ) != code_hash:
        raise StagedExtensionError("staged extension code changed after pilot")
    config = StageConfig(**pilot["config"])
    config.validate()
    metrics = pilot["pilot_ensemble"]["metrics_for_freeze_or_gate"]
    thresholds = {
        name: max(float(policy["floor"]), float(policy["multiplier"]) * float(metrics[name]))
        for name, policy in STAGE_FREEZE_POLICY["metrics"].items()
    }
    gates = {
        "pilot_hash_bound_before_heldout": True,
        **{
            f"{name}_threshold_below_predeclared_ceiling": thresholds[name]
            <= float(policy["ceiling"])
            for name, policy in STAGE_FREEZE_POLICY["metrics"].items()
        },
        "heldout_gate_interval_registered_disjoint_and_unseen": bool(
            config.heldout_evaluation_start_N > config.pilot_span_N
        ),
        "production_tolerances_and_physical_kernel_remain_locked": True,
    }
    passed = bool(all(gates.values()))
    pilot_endpoint = pilot["pilot_ensemble"]["stage_endpoint_aggregate_not_a_seed"]
    return {
        "schema": "apeiron-ap1-m1-staged-log-background-freeze-v1.0",
        "updated_utc": _utc_now(),
        "classification": (
            "STAGED_LOG_BACKGROUND_TOLERANCES_FROZEN_HELDOUT_UNSEEN"
            if passed else "STAGED_LOG_BACKGROUND_TOLERANCE_FREEZE_FAILED"
        ),
        "input_sha256": {
            pilot_path.name: file_sha256(pilot_path),
            "AP1/CODE/ap1_m1_staged_log_background_extension.py": code_hash,
        },
        "predeclared_stage_freeze_policy": STAGE_FREEZE_POLICY,
        "thresholds": thresholds,
        "heldout_plan": {
            "span_N": config.heldout_span_N,
            "fine_nodes": config.heldout_fine_nodes,
            "coarse_nodes": (config.heldout_fine_nodes + 1) // 2,
            "evaluation_start_N": config.heldout_evaluation_start_N,
            "overlap_reference": {
                "N_relative": float(pilot_endpoint["N_relative"]),
                "H_Mpl": float(pilot_endpoint["H_Mpl"]),
            },
        },
        "gates": gates,
        "all_tolerance_freeze_gates_pass": passed,
        "tolerances_frozen_before_heldout": passed,
        "heldout_evaluated": False,
        "new_AP1_M1_background_runs": pilot["new_AP1_M1_background_runs"],
        "production_background_tolerances_frozen": False,
        "trajectory_rows_persisted": 0,
        "physical_response_kernel_started": False,
        "claim_boundary": "first-stage numerical tolerance freeze only; heldout, production background, present anchor and physical kernel remain unseen",
        "next_required": "evaluate the registered disjoint held-out gate interval once and persist only a full PASS",
    }


def build_heldout(
    apeiron_root: Path,
    pilot_path: Path,
    freeze_path: Path,
    code_path: Path,
) -> dict[str, Any]:
    pilot = json.loads(pilot_path.read_text(encoding="utf-8"))
    freeze = json.loads(freeze_path.read_text(encoding="utf-8"))
    if not pilot.get("all_pilot_gates_pass"):
        raise StagedExtensionError("stage pilot is not PASS")
    if not freeze.get("all_tolerance_freeze_gates_pass"):
        raise StagedExtensionError("stage tolerance freeze is not PASS")
    if freeze["input_sha256"].get(pilot_path.name) != file_sha256(pilot_path):
        raise StagedExtensionError("stage pilot changed after tolerance freeze")
    code_hash = file_sha256(code_path)
    if freeze["input_sha256"].get(
        "AP1/CODE/ap1_m1_staged_log_background_extension.py"
    ) != code_hash:
        raise StagedExtensionError("staged extension code changed after tolerance freeze")
    config = StageConfig(**pilot["config"])
    config.validate()
    plan = freeze["heldout_plan"]
    if not (
        plan["span_N"] == config.heldout_span_N
        and plan["fine_nodes"] == config.heldout_fine_nodes
        and plan["evaluation_start_N"] == config.heldout_evaluation_start_N
    ):
        raise StagedExtensionError("held-out stage plan changed after freeze")
    _, arrays = load_authorities(apeiron_root)
    ensemble = _evaluate_stage(
        arrays,
        config,
        heldout=True,
        overlap_reference=plan["overlap_reference"],
    )
    absolute = _absolute_stage_gates(ensemble, config)
    numerical = {
        f"{name}_below_frozen_threshold": float(value)
        <= float(freeze["thresholds"][name])
        for name, value in ensemble["metrics_for_freeze_or_gate"].items()
    }
    gates = {
        "pilot_and_freeze_hash_bound_before_heldout": True,
        "registered_disjoint_heldout_gate_interval_used_once": True,
        **absolute,
        **numerical,
        "no_production_background_kernel_seed_observable_or_curve": True,
    }
    passed = bool(all(gates.values()))
    return {
        "schema": "apeiron-ap1-m1-staged-log-background-heldout-v1.0",
        "updated_utc": _utc_now(),
        "classification": (
            "FIRST_STAGED_LOG_BACKGROUND_HELDOUT_PASS_NEXT_LOG_STAGE_OPEN"
            if passed else "FIRST_STAGED_LOG_BACKGROUND_HELDOUT_NONPASS"
        ),
        "authority_sha256": {
            **EXPECTED_AUTHORITIES,
            pilot_path.name: file_sha256(pilot_path),
            freeze_path.name: file_sha256(freeze_path),
            "AP1/CODE/ap1_m1_staged_log_background_extension.py": code_hash,
        },
        "frozen_thresholds": freeze["thresholds"],
        "heldout_was_unseen_when_tolerances_frozen": True,
        "heldout_ensemble": ensemble,
        "gates": gates,
        "all_heldout_gates_pass": passed,
        "checkpoint_eligible": passed,
        "new_AP1_M1_background_runs": (
            pilot["new_AP1_M1_background_runs"]
            + ensemble["new_AP1_M1_background_runs"]
        ),
        "first_logarithmic_extension_assessed": True,
        "long_background_candidate_assessed": False,
        "production_background_tolerances_frozen": False,
        "trajectory_rows_persisted": 0,
        "physical_response_kernel_started": False,
        "seed_released": False,
        "nonpass_stored_or_used_as_seed": False,
        "equations_changed": False,
        "physics_changed": False,
        "parameters_changed": False,
        "existing_gate_thresholds_changed": False,
        "claim_boundary": "first staged logarithmic extension through relative N=0.04 only; no production background, present anchor, kernel, observable, curve, fit or significance",
        "next_required": (
            "promote the first-stage checkpoint and predeclare the next doubled logarithmic prefix before any further evaluation"
            if passed else "stop fail-closed; do not persist, checkpoint or seed this result"
        ),
    }


def build_checkpoint(
    pilot_path: Path,
    freeze_path: Path,
    heldout_path: Path,
    code_path: Path,
) -> dict[str, Any]:
    pilot = json.loads(pilot_path.read_text(encoding="utf-8"))
    freeze = json.loads(freeze_path.read_text(encoding="utf-8"))
    heldout = json.loads(heldout_path.read_text(encoding="utf-8"))
    if not (
        pilot.get("all_pilot_gates_pass")
        and freeze.get("all_tolerance_freeze_gates_pass")
        and heldout.get("all_heldout_gates_pass")
    ):
        raise StagedExtensionError("full stage pilot/freeze/heldout PASS chain required")
    expected = {
        pilot_path.name: file_sha256(pilot_path),
        freeze_path.name: file_sha256(freeze_path),
        "AP1/CODE/ap1_m1_staged_log_background_extension.py": file_sha256(code_path),
    }
    for name, digest in expected.items():
        if heldout["authority_sha256"].get(name) != digest:
            raise StagedExtensionError(f"held-out stage authority mismatch: {name}")
    return {
        "schema": "apeiron-ap1-m1-staged-log-background-checkpoint-v1.0",
        "updated_utc": _utc_now(),
        "classification": "M1_FIRST_STAGED_LOG_BACKGROUND_EXTENSION_PASS_NEXT_LOG_STAGE_OPEN_PHYSICAL_KERNEL_BLOCKED",
        "authority_sha256": {
            pilot_path.name: file_sha256(pilot_path),
            freeze_path.name: file_sha256(freeze_path),
            heldout_path.name: file_sha256(heldout_path),
            "AP1/CODE/ap1_m1_staged_log_background_extension.py": file_sha256(code_path),
        },
        "frozen_stage_thresholds": freeze["thresholds"],
        "heldout_metrics": heldout["heldout_ensemble"]["metrics_for_freeze_or_gate"],
        "heldout_physical_diagnostics": heldout["heldout_ensemble"]["physical_diagnostics"],
        "heldout_source_diagnostics": heldout["heldout_ensemble"]["source_diagnostics"],
        "heldout_precision": heldout["heldout_ensemble"]["precision"],
        "heldout_overlap_audit": heldout["heldout_ensemble"]["overlap_audit"],
        "gates": {
            "pilot_complete": True,
            "stage_tolerances_frozen_before_heldout": True,
            "heldout_full_PASS": True,
            "only_PASS_promoted": True,
            "no_production_background_kernel_seed_or_observable": True,
        },
        "all_checkpoint_gates_pass": True,
        "checkpoint_eligible": True,
        "new_AP1_M1_background_runs": heldout["new_AP1_M1_background_runs"],
        "validated_relative_N_endpoint": 0.04,
        "extension_factor_over_G29": 2.0,
        "long_background_candidate_assessed": False,
        "production_background_tolerances_frozen": False,
        "trajectory_rows_persisted": 0,
        "physical_response_kernel_started": False,
        "seed_released": False,
        "nonpass_stored_or_used_as_seed": False,
        "equations_changed": False,
        "physics_changed": False,
        "parameters_changed": False,
        "existing_gate_thresholds_changed": False,
        "AP1_status": "ORANGE",
        "next_required": "predeclare and evaluate the next doubled logarithmic prefix; production tolerances, present anchor and physical kernel remain locked",
        "claim_boundary": "first staged logarithmic prefix extension only; no production trajectory, present anchor, response kernel, observable, curve, fit or significance",
    }


def checkpoint_markdown(report: dict[str, Any]) -> str:
    metrics = report["heldout_metrics"]
    physical = report["heldout_physical_diagnostics"]
    source = report["heldout_source_diagnostics"]
    overlap = report["heldout_overlap_audit"]
    return f"""# Apeiron AP1 – G30 first staged logarithmic background extension

**Updated UTC:** {report['updated_utc']}  
**Classification:** `{report['classification']}`  
**AP1 status:** ORANGE

The frozen G29 solver was extended without changing equations, physics,
parameters, or pre-existing thresholds.  The pilot gate interval lay strictly
beyond G29.  Only after a separate pilot-derived freeze was the disjoint
held-out gate interval evaluated.  Fine and coarse N spacings remain exactly
those of G29.

## Held-out aggregate

- validated relative endpoint: `{report['validated_relative_N_endpoint']}`
- extension factor over G29: `{report['extension_factor_over_G29']}`
- fixed-point source gross-relative closure: `{metrics['max_fixed_point_source_gross_relative_change']:.17g}`
- background time-grid delta: `{metrics['max_background_time_relative_delta']:.17g}`
- source time-grid gross-relative delta: `{metrics['max_source_time_gross_relative_delta']:.17g}`
- entry quadrature gross-relative delta: `{metrics['max_entry_quadrature_gross_relative_delta']:.17g}`
- tail quadrature gross-relative delta: `{metrics['max_tail_quadrature_gross_relative_delta']:.17g}`
- UV cutoff gross-relative delta: `{metrics['max_uv_cutoff_gross_relative_delta']:.17g}`
- Friedmann residual: `{physical['max_friedmann_normalized']:.17g}`
- Raychaudhuri residual: `{physical['max_raychaudhuri_normalized']:.17g}`
- flux-balanced Ward residual: `{physical['ward_normalized']:.17g}`
- standard continuity residual: `{physical['standard_continuity_normalized']:.17g}`
- Wronskian error: `{source['resolved']['max_wronskian_relative_error']:.17g}`
- pilot-prefix overlap H delta at N={overlap['reference_N_relative']}: `{overlap['relative_delta']:.17g}`

All frozen first-stage held-out gates pass.  Four new resolution integrations
were performed across pilot and heldout.  Zero trajectory rows and zero new
seeds were released.

## Boundary

This is only the first doubled logarithmic prefix through relative N=0.04.  It
is not the long or production background and does not reach the present anchor.
Production tolerances, AP2/AP3, production curves, observables, and the physical
two-time response kernel remain locked.  No fit or significance is claimed.
"""


def main() -> None:
    parser = argparse.ArgumentParser()
    sub = parser.add_subparsers(dest="command", required=True)
    pilot_parser = sub.add_parser("pilot")
    pilot_parser.add_argument("apeiron_root", type=Path)
    pilot_parser.add_argument("--output", type=Path, required=True)
    freeze_parser = sub.add_parser("freeze")
    freeze_parser.add_argument("pilot", type=Path)
    freeze_parser.add_argument("--output", type=Path, required=True)
    heldout_parser = sub.add_parser("heldout")
    heldout_parser.add_argument("apeiron_root", type=Path)
    heldout_parser.add_argument("pilot", type=Path)
    heldout_parser.add_argument("freeze", type=Path)
    heldout_parser.add_argument("--output", type=Path, required=True)
    checkpoint_parser = sub.add_parser("checkpoint")
    checkpoint_parser.add_argument("pilot", type=Path)
    checkpoint_parser.add_argument("freeze", type=Path)
    checkpoint_parser.add_argument("heldout", type=Path)
    checkpoint_parser.add_argument("--json-output", type=Path, required=True)
    checkpoint_parser.add_argument("--md-output", type=Path, required=True)
    args = parser.parse_args()
    code_path = Path(__file__).resolve()
    if args.command == "pilot":
        report = build_pilot(args.apeiron_root, code_path)
        _write_pass_json(report, args.output, "all_pilot_gates_pass")
    elif args.command == "freeze":
        report = build_freeze(args.pilot, code_path)
        _write_pass_json(report, args.output, "all_tolerance_freeze_gates_pass")
    elif args.command == "heldout":
        report = build_heldout(
            args.apeiron_root, args.pilot, args.freeze, code_path
        )
        _write_pass_json(report, args.output, "all_heldout_gates_pass")
    else:
        report = build_checkpoint(
            args.pilot, args.freeze, args.heldout, code_path
        )
        _write_pass_json(report, args.json_output, "all_checkpoint_gates_pass")
        args.md_output.write_text(checkpoint_markdown(report), encoding="utf-8")


if __name__ == "__main__":
    main()
