"""G32C fail-closed log-H recovery preflight for the AP1-M1 background.

The registered G32B heldout was attempted exactly once and stopped in the
first background integration when a DOP853 trial state left the finite
positive-H chart.  No heldout report, checkpoint, trajectory, or seed was
written.  The consumed interval through relative N=0.10 is therefore never
eligible to become a blind validation result.

This module introduces only the numerical coordinate

    ell_H = log(H / H_reference),

on the already required H > 0 branch.  It reconstructs H as
H_reference * exp(ell_H), so a finite solver state cannot create a negative-H
trial value.  The original equations are recovered exactly through
d ell_H / dN = (dH/dN) / H.  All matter equations, physical parameters,
counterterms, moving split, PV basis, mode transport, fixed-point settings,
and existing gate thresholds remain unchanged.

Building the preflight evaluates algebraic canaries only.  It performs zero
background runs.  The now-known N <= 0.10 interval may be used only as a
non-blind recovery pilot.  A future validation interval is preregistered
strictly beyond it and may be evaluated once only after a new pilot PASS and
separate method-tolerance freeze.
"""
from __future__ import annotations

import argparse
from dataclasses import asdict, dataclass
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
from scipy.integrate import solve_ivp
from scipy.interpolate import CubicSpline

import ap1_m1_balanced_coupled_stage_preflight as g32
import ap1_m1_coupled_moving_split_background as g29


EXPECTED_AUTHORITIES = {
    "AP1/APEIRON_AP1_M1_BALANCED_COUPLED_STAGE_PREFLIGHT_LATEST.json": (
        "5e544bfbe4803fe8668285a80267549ee5d0f3516d6db2ce03f548ed2d0f3333"
    ),
    "AP1/APEIRON_AP1_M1_BALANCED_COUPLED_PRECISION_PREFLIGHT_LATEST.json": (
        "13ef13ad1d30d7640c492630a763ae6e7ddbbe4a0359cd9cee77eb70e50894ea"
    ),
    "AP1/APEIRON_AP1_M1_BALANCED_COUPLED_STAGE_PILOT_LATEST.json": (
        "b6f7f01d3ef30bd2a65a42864c64215cd9f6a4cfd34b51d8ff854d78cbd41c05"
    ),
    "AP1/APEIRON_AP1_M1_BALANCED_COUPLED_STAGE_TOLERANCES_LATEST.json": (
        "c56e37c57699ba2abf8b03b1b981bca2335303b3476991ea8313bbb98b93fef0"
    ),
    "AP1/APEIRON_AP1_M1_BALANCED_COUPLED_STAGE_PILOT_FREEZE_CHECKPOINT_LATEST.md": (
        "40477aa1b645854a7159b0f578d71e47cececb1025ef64dce5e624bf10cee5dd"
    ),
    "AP1/CODE/ap1_m1_coupled_moving_split_background.py": (
        "3b6f2b38d047db3cb9510af9c2c92ef9c80c2c1ca16c40212f7e7c354a76f400"
    ),
    "AP1/CODE/ap1_m1_balanced_coupled_stage_preflight.py": (
        "a2e1c19db0d18c05c38e62254bbe4dc1ef4d9b6488f11e3bcc15640bd3a71da2"
    ),
    "AP1/CODE/ap1_m1_balanced_coupled_precision_preflight.py": (
        "aa9ce3b7aea90ed5101dadc40a23d3cc354d38c329c8f5a73135ba0d2384fa0b"
    ),
    "AP1/CODE/ap1_m1_balanced_coupled_stage_pilot.py": (
        "8cec8164dc0392aab9983d2c93a35c2a68dbb356a5fa257361adbcd1e2be3973"
    ),
    "AP1/CODE/test_ap1_m1_balanced_coupled_stage_pilot.py": (
        "8d0437830a77d6156cf25922712ea2b03f50c2c870543d7f8e260accbcf28d75"
    ),
}

LOGH_CHART = "ell_H=log(H/H_reference), H=H_reference*exp(ell_H)"
DIRECT_H_FAILURE = "background left finite positive-H branch"
H_ABSOLUTE_TOLERANCE = 2.0e-17


class LogHRecoveryPreflightError(RuntimeError):
    """An authority, chronology, equivalence, or lock gate failed."""


@dataclass(frozen=True)
class LogHRecoveryPlan:
    """Chronology-safe recovery pilot and new prospective heldout geometry."""

    consumed_G32B_span_N: float = 0.10
    recovery_pilot_span_N: float = 0.10
    recovery_pilot_evaluation_start_N: float = 0.085
    recovery_pilot_nodes_coarse: int = 1281
    recovery_pilot_nodes_middle: int = 2561
    recovery_pilot_nodes_fine: int = 5121
    prospective_heldout_span_N: float = 0.12
    prospective_heldout_evaluation_start_N: float = 0.105
    prospective_heldout_nodes_coarse: int = 1537
    prospective_heldout_nodes_middle: int = 3073
    prospective_heldout_nodes_fine: int = 6145
    qcut_over_Lambda: float = 0.6
    adiabatic_support_stride: int = 1
    adiabatic_momentum_support_nodes: int = 257
    fixed_point_steps: int = 10
    relaxation: float = 0.9
    entry_nodes_per_panel: int = 2
    tail_nodes_per_octave: int = 4
    K_over_Lambda: float = 64.0
    max_mode_step_N: float = 1.25e-5
    background_rtol: float = 2.0e-10
    decimal_low_digits: int = 60
    decimal_high_digits: int = 80
    max_recovery_pilot_resolved_modes: int = 10500
    max_prospective_heldout_resolved_modes: int = 12500

    def _level_nodes(self, phase: str) -> tuple[int, int, int]:
        if phase == "recovery_pilot":
            return (
                self.recovery_pilot_nodes_coarse,
                self.recovery_pilot_nodes_middle,
                self.recovery_pilot_nodes_fine,
            )
        if phase == "prospective_heldout":
            return (
                self.prospective_heldout_nodes_coarse,
                self.prospective_heldout_nodes_middle,
                self.prospective_heldout_nodes_fine,
            )
        raise ValueError("phase must be recovery_pilot or prospective_heldout")

    def validate(self) -> None:
        if self.recovery_pilot_span_N != self.consumed_G32B_span_N:
            raise ValueError("the consumed G32B domain is recovery-pilot only")
        if not (
            0.08
            < self.recovery_pilot_evaluation_start_N
            < self.recovery_pilot_span_N
            < self.prospective_heldout_evaluation_start_N
            < self.prospective_heldout_span_N
        ):
            raise ValueError("new heldout must be strictly beyond the consumed domain")
        inherited = g32.BalancedCoupledStagePlan()
        for name in (
            "qcut_over_Lambda",
            "adiabatic_support_stride",
            "adiabatic_momentum_support_nodes",
            "fixed_point_steps",
            "relaxation",
            "entry_nodes_per_panel",
            "tail_nodes_per_octave",
            "K_over_Lambda",
            "max_mode_step_N",
            "background_rtol",
            "decimal_low_digits",
            "decimal_high_digits",
        ):
            if getattr(self, name) != getattr(inherited, name):
                raise ValueError(f"G32B numerical setting changed: {name}")
        fine_steps = []
        for phase, span, budget in (
            (
                "recovery_pilot",
                self.recovery_pilot_span_N,
                self.max_recovery_pilot_resolved_modes,
            ),
            (
                "prospective_heldout",
                self.prospective_heldout_span_N,
                self.max_prospective_heldout_resolved_modes,
            ),
        ):
            coarse, middle, fine = self._level_nodes(phase)
            if not (
                fine - 1 == 2 * (middle - 1)
                and middle - 1 == 2 * (coarse - 1)
            ):
                raise ValueError("three exactly nested recovery levels required")
            if any(nodes < 17 or nodes % 2 != 1 for nodes in (coarse, middle, fine)):
                raise ValueError("odd bounded recovery grids required")
            fine_steps.append(span / (fine - 1))
            resolved_modes = 128 + self.entry_nodes_per_panel * (fine - 1)
            if resolved_modes > budget:
                raise ValueError("recovery resolved-mode resource bound exceeded")
        inherited_step = (
            inherited.heldout_span_N
            / (inherited.heldout_nodes_fine - 1)
        )
        if not all(
            math.isclose(step, inherited_step, rel_tol=0.0, abs_tol=1.0e-18)
            for step in fine_steps
        ):
            raise ValueError("the G32B fine N spacing changed")


RECOVERY_POLICY: dict[str, Any] = {
    "registered_G32B_heldout_attempts": 1,
    "G32B_heldout_report_written": False,
    "G32B_heldout_checkpoint_or_seed_released": False,
    "G32B_heldout_result": "NONPASS_before_first_level_completed",
    "direct_H_failure_message": DIRECT_H_FAILURE,
    "consumed_N_le_0p10_domain_is_blind": False,
    "consumed_domain_may_be_used_only_for_recovery_development": True,
    "future_heldout_starts_strictly_after_N_0p10": True,
    "future_heldout_may_run_once_only_after_new_pilot_PASS_and_freeze": True,
    "existing_G29_G31_G32_G32A_G32B_gates_and_thresholds_retained": True,
    "only_background_coordinate_changes_from_H_to_logH": True,
    "no_physical_or_kernel_claim_from_structural_preflight": True,
}


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 verify_authorities(root: Path) -> tuple[dict[str, Any], dict[str, Any]]:
    observed = {name: file_sha256(root / name) for name in EXPECTED_AUTHORITIES}
    if observed != EXPECTED_AUTHORITIES:
        changed = [
            name
            for name, expected in EXPECTED_AUTHORITIES.items()
            if observed.get(name) != expected
        ]
        raise LogHRecoveryPreflightError(f"G32B authority drift: {changed}")
    pilot = json.loads(
        (
            root / "AP1/APEIRON_AP1_M1_BALANCED_COUPLED_STAGE_PILOT_LATEST.json"
        ).read_text(encoding="utf-8")
    )
    freeze = json.loads(
        (
            root
            / "AP1/APEIRON_AP1_M1_BALANCED_COUPLED_STAGE_TOLERANCES_LATEST.json"
        ).read_text(encoding="utf-8")
    )
    if not pilot.get("all_pilot_gates_pass") or pilot.get("heldout_evaluated"):
        raise LogHRecoveryPreflightError("G32B pilot authority is not pre-heldout PASS")
    if not freeze.get("all_tolerance_freeze_gates_pass"):
        raise LogHRecoveryPreflightError("G32B tolerance authority is not PASS")
    if freeze.get("heldout_plan", {}).get("span_N") != 0.10:
        raise LogHRecoveryPreflightError("consumed G32B heldout geometry changed")
    return pilot, freeze


def physical_to_logh_state(state: np.ndarray, H_reference: float) -> np.ndarray:
    values = np.asarray(state, dtype=float)
    reference = float(H_reference)
    if values.shape != (5,) or not np.all(np.isfinite(values)):
        raise LogHRecoveryPreflightError("finite five-component state required")
    if reference <= 0.0 or not math.isfinite(reference) or values[4] <= 0.0:
        raise LogHRecoveryPreflightError("finite positive H chart required")
    transformed = values.copy()
    transformed[4] = math.log(float(values[4]) / reference)
    return transformed


def logh_to_physical_state(state: np.ndarray, H_reference: float) -> np.ndarray:
    values = np.asarray(state, dtype=float)
    reference = float(H_reference)
    if values.shape != (5,) or not np.all(np.isfinite(values)):
        raise LogHRecoveryPreflightError("finite five-component log-H state required")
    if reference <= 0.0 or not math.isfinite(reference):
        raise LogHRecoveryPreflightError("finite positive H reference required")
    try:
        H = reference * math.exp(float(values[4]))
    except OverflowError as exc:
        raise LogHRecoveryPreflightError("log-H reconstruction overflow") from exc
    if H <= 0.0 or not math.isfinite(H):
        raise LogHRecoveryPreflightError("log-H reconstruction left positive branch")
    physical = values.copy()
    physical[4] = H
    return physical


def direct_rhs_to_logh(
    physical_state: np.ndarray, direct_rhs: np.ndarray
) -> np.ndarray:
    state = np.asarray(physical_state, dtype=float)
    derivative = np.asarray(direct_rhs, dtype=float)
    if state.shape != (5,) or derivative.shape != (5,):
        raise LogHRecoveryPreflightError("five-component state and RHS required")
    if not np.all(np.isfinite(state)) or not np.all(np.isfinite(derivative)):
        raise LogHRecoveryPreflightError("finite state and RHS required")
    if state[4] <= 0.0:
        raise LogHRecoveryPreflightError("log-H RHS requires H > 0")
    transformed = derivative.copy()
    transformed[4] = derivative[4] / state[4]
    return transformed


def logh_rhs_to_direct(
    physical_state: np.ndarray, logh_rhs: np.ndarray
) -> np.ndarray:
    state = np.asarray(physical_state, dtype=float)
    derivative = np.asarray(logh_rhs, dtype=float)
    if state.shape != (5,) or derivative.shape != (5,):
        raise LogHRecoveryPreflightError("five-component state and RHS required")
    if not np.all(np.isfinite(state)) or not np.all(np.isfinite(derivative)):
        raise LogHRecoveryPreflightError("finite state and RHS required")
    if state[4] <= 0.0:
        raise LogHRecoveryPreflightError("direct RHS recovery requires H > 0")
    recovered = derivative.copy()
    recovered[4] = state[4] * derivative[4]
    return recovered


def background_rhs_direct_N(
    n_value: float,
    physical_state: np.ndarray,
    source_at_n: dict[str, float],
    N_seed: float,
    pilot: Any,
    parameters: Any,
) -> np.ndarray:
    """Evaluate the unchanged G29 equations in the direct physical-H chart."""

    sigma, sigma_dot, theta, theta_dot, H = map(float, physical_state)
    if not np.all(np.isfinite(physical_state)) or H <= 0.0:
        raise g29.CoupledBackgroundError(DIRECT_H_FAILURE)
    local = g29.classical_terms(np.asarray(physical_state, dtype=float))
    if local["PX"] <= 0.0 or local["K"] <= 0.0:
        raise g29.CoupledBackgroundError("background left hyperbolic branch")
    rho_standard, pressure_standard = g29.standard_stress(
        np.array([N_seed + n_value]), pilot
    )
    _mass2, dm_sigma, dm_theta = g29.portal_terms(
        sigma, theta, parameters
    )
    chi2 = float(source_at_n["chi2"])
    sigma_ddot = (
        -3.0 * H * sigma_dot
        + local["P_sigma"]
        - 0.5 * float(dm_sigma) * chi2
    )
    theta_ddot = (
        local["P_theta"]
        - 0.5 * float(dm_theta) * chi2
        - 3.0 * H * local["PX"] * theta_dot
        - local["PX_sigma"] * sigma_dot * theta_dot
    ) / local["K"]
    Hdot = -0.5 * (
        local["rho"]
        + local["pressure"]
        + float(source_at_n["rho"] + source_at_n["pressure"])
        + float(rho_standard[0] + pressure_standard[0])
    )
    return np.array(
        [
            sigma_dot / H,
            sigma_ddot / H,
            theta_dot / H,
            theta_ddot / H,
            Hdot / H,
        ]
    )


def integrate_background_logh(
    arrays: dict[str, np.ndarray],
    relative_N: np.ndarray,
    quantum: dict[str, np.ndarray],
    rtol: float,
) -> tuple[Any, np.ndarray]:
    """Integrate the unchanged background equations in the positive log-H chart."""

    grid = np.asarray(relative_N, dtype=float)
    state6 = np.asarray(arrays["m1_state_physical_N"], dtype=float)
    initial_physical = state6[:5].copy()
    H_reference = float(initial_physical[4])
    initial_logh = physical_to_logh_state(initial_physical, H_reference)
    N_seed = float(state6[5])
    parameters = g29.ChiParameters()
    pilot = g29.Planck2018Pilot()
    splines = {
        key: CubicSpline(grid, np.asarray(quantum[key], dtype=float))
        for key in g29.SOURCE_KEYS
    }

    def rhs(n_value: float, logh_state: np.ndarray) -> np.ndarray:
        physical = logh_to_physical_state(logh_state, H_reference)
        source = {key: float(splines[key](n_value)) for key in g29.SOURCE_KEYS}
        direct = background_rhs_direct_N(
            n_value, physical, source, N_seed, pilot, parameters
        )
        return direct_rhs_to_logh(physical, direct)

    atol_logh = H_ABSOLUTE_TOLERANCE / H_reference
    solution = solve_ivp(
        rhs,
        (float(grid[0]), float(grid[-1])),
        initial_logh,
        t_eval=grid,
        method="DOP853",
        rtol=rtol,
        atol=np.array(
            [2.0e-13, 2.0e-17, 2.0e-13, 2.0e-17, atol_logh]
        ),
        max_step=min(
            2.5e-4,
            float(grid[-1] - grid[0]) / (len(grid) - 1),
        ),
    )
    if not solution.success or solution.y.shape != (5, len(grid)):
        raise g29.CoupledBackgroundError(
            f"log-H background integration failed: {solution.message}"
        )
    rows_logh = solution.y.T
    rows = np.vstack(
        [logh_to_physical_state(row, H_reference) for row in rows_logh]
    )
    Hdot = np.empty(len(grid))
    for index, (n_value, physical) in enumerate(zip(grid, rows)):
        source = {
            key: float(splines[key](float(n_value))) for key in g29.SOURCE_KEYS
        }
        direct = background_rhs_direct_N(
            float(n_value), physical, source, N_seed, pilot, parameters
        )
        Hdot[index] = direct[4] * physical[4]
    Hdot[0] = float(np.asarray(arrays["Hdot_Mpl2"], dtype=float)[0])
    trajectory = g29.ChiTrajectory(
        N=grid,
        H=rows[:, 4],
        Hdot=Hdot,
        sigma=rows[:, 0],
        theta=rows[:, 2],
    ).validated()
    return trajectory, rows


def run_logh_balanced_coupled(
    arrays: dict[str, np.ndarray],
    span_N: float,
    nodes: int,
    plan: LogHRecoveryPlan,
) -> dict[str, Any]:
    """Run the G32B fixed-point/operator path with only the H chart replaced."""

    plan.validate()
    grid = np.linspace(0.0, span_N, nodes)
    stored = np.asarray(arrays["quantum_rho_pressure_chi2"], dtype=float)
    quantum = {
        key: np.full(nodes, stored[index], dtype=float)
        for index, key in enumerate(g29.SOURCE_KEYS)
    }
    iteration_log: list[dict[str, Any]] = []
    for iteration in range(plan.fixed_point_steps):
        used = {key: value.copy() for key, value in quantum.items()}
        trajectory, rows = integrate_background_logh(
            arrays, grid, used, plan.background_rtol
        )
        fresh, diagnostics, internal = g32.assemble_balanced_quantum_sources(
            trajectory,
            arrays,
            plan.entry_nodes_per_panel,
            plan.tail_nodes_per_octave,
            plan.K_over_Lambda,
            plan.max_mode_step_N,
            len(grid),
            plan.adiabatic_momentum_support_nodes,
        )
        change = g29._max_source_gross_relative(fresh, used, internal)
        quantum = {
            key: plan.relaxation * fresh[key]
            + (1.0 - plan.relaxation) * used[key]
            for key in g29.SOURCE_KEYS
        }
        for index, key in enumerate(g29.SOURCE_KEYS):
            quantum[key][0] = stored[index]
        iteration_log.append(
            {
                "iteration": iteration + 1,
                "source_gross_relative_change": float(change),
                "H_endpoint_Mpl": float(trajectory.H[-1]),
                "max_wronskian_relative_error": float(
                    diagnostics["resolved"]["max_wronskian_relative_error"]
                ),
                "dynamic_chart_switches": int(
                    diagnostics["resolved"]["dynamic_chart_switches"]
                ),
                "vacuum_resets": int(
                    diagnostics["resolved"]["vacuum_resets"]
                ),
            }
        )
    used = {key: value.copy() for key, value in quantum.items()}
    trajectory, rows = integrate_background_logh(
        arrays, grid, used, plan.background_rtol
    )
    fresh, diagnostics, internal = g32.assemble_balanced_quantum_sources(
        trajectory,
        arrays,
        plan.entry_nodes_per_panel,
        plan.tail_nodes_per_octave,
        plan.K_over_Lambda,
        plan.max_mode_step_N,
        len(grid),
        plan.adiabatic_momentum_support_nodes,
    )
    return {
        "trajectory": trajectory,
        "background_rows": rows,
        "quantum_used": used,
        "quantum_fresh": fresh,
        "source_diagnostics": diagnostics,
        "source_internal": internal,
        "iteration_log": iteration_log,
        "validation_source_gross_relative_change": float(
            g29._max_source_gross_relative(fresh, used, internal)
        ),
        "config": {
            "span_N": float(span_N),
            "nodes": int(nodes),
            "fixed_point_steps": plan.fixed_point_steps,
            "relaxation": plan.relaxation,
            "background_rtol": plan.background_rtol,
            "H_absolute_tolerance": H_ABSOLUTE_TOLERANCE,
            "ell_H_absolute_tolerance": (
                H_ABSOLUTE_TOLERANCE
                / float(np.asarray(arrays["m1_state_physical_N"])[4])
            ),
            "max_mode_step_N": plan.max_mode_step_N,
            "entry_nodes_per_panel": plan.entry_nodes_per_panel,
            "tail_nodes_per_octave": plan.tail_nodes_per_octave,
            "K_over_Lambda": plan.K_over_Lambda,
            "balanced_mode_chart": True,
            "background_chart": LOGH_CHART,
        },
    }


def algebraic_equivalence_canary() -> dict[str, Any]:
    eps = float(np.finfo(float).eps)
    H_reference = 4.3e-7
    roundtrip = []
    positivity = []
    rhs_defects = []
    for ratio in (2.0**-20, 0.25, 0.75, 1.0, 1.5, 4.0, 2.0**20):
        state = np.array([0.2, -3.0e-8, -0.4, 2.0e-8, H_reference * ratio])
        transformed = physical_to_logh_state(state, H_reference)
        recovered = logh_to_physical_state(transformed, H_reference)
        roundtrip.append(
            abs(float(recovered[4] - state[4])) / max(abs(float(state[4])), 1.0e-300)
        )
        positivity.append(bool(recovered[4] > 0.0 and math.isfinite(recovered[4])))
        direct = np.array([1.0e-3, -2.0e-4, 3.0e-3, -4.0e-4, -5.0e-9])
        logh = direct_rhs_to_logh(state, direct)
        restored = logh_rhs_to_direct(state, logh)
        rhs_defects.append(
            float(np.max(np.abs(restored - direct)))
            / max(float(np.max(np.abs(direct))), 1.0e-300)
        )
    N = np.linspace(0.0, 0.12, 129)
    epsilon = 0.73
    ell = -epsilon * N
    reconstructed = H_reference * np.exp(ell)
    analytic = H_reference * np.exp(-epsilon * N)
    toy_defect = float(
        np.max(np.abs(reconstructed - analytic))
        / max(float(np.max(np.abs(analytic))), 1.0e-300)
    )
    return {
        "chart": LOGH_CHART,
        "maximum_H_roundtrip_relative_defect": max(roundtrip),
        "maximum_rhs_reconstruction_relative_defect": max(rhs_defects),
        "analytic_positive_H_toy_relative_defect": toy_defect,
        "all_finite_logH_states_reconstruct_positive_H": bool(all(positivity)),
        "relative_defect_cap": 64.0 * eps,
    }


def build_preflight(root: Path, code_path: Path, test_path: Path) -> dict[str, Any]:
    pilot, freeze = verify_authorities(root)
    plan = LogHRecoveryPlan()
    plan.validate()
    canary = algebraic_equivalence_canary()
    cap = float(canary["relative_defect_cap"])
    g32b_plan = g32.BalancedCoupledStagePlan()
    gates = {
        "G29_through_G32B_authority_chain_hash_exact": True,
        "G32B_pilot_and_preheldout_tolerance_freeze_PASS": bool(
            pilot["all_pilot_gates_pass"]
            and freeze["all_tolerance_freeze_gates_pass"]
        ),
        "G32B_heldout_attempt_consumed_without_report_checkpoint_or_seed": bool(
            RECOVERY_POLICY["registered_G32B_heldout_attempts"] == 1
            and not RECOVERY_POLICY["G32B_heldout_report_written"]
            and not RECOVERY_POLICY["G32B_heldout_checkpoint_or_seed_released"]
        ),
        "G33_denied_after_G32B_heldout_NONPASS": True,
        "logH_chart_roundtrip_below_machine_cap": bool(
            canary["maximum_H_roundtrip_relative_defect"] <= cap
        ),
        "direct_and_logH_RHS_algebraically_equivalent_below_machine_cap": bool(
            canary["maximum_rhs_reconstruction_relative_defect"] <= cap
        ),
        "analytic_positive_H_toy_exact_below_machine_cap": bool(
            canary["analytic_positive_H_toy_relative_defect"] <= cap
        ),
        "finite_logH_chart_reconstructs_only_positive_H": bool(
            canary["all_finite_logH_states_reconstruct_positive_H"]
        ),
        "physical_H_absolute_tolerance_mapped_without_change": bool(
            H_ABSOLUTE_TOLERANCE == 2.0e-17
        ),
        "G32B_operator_and_fixed_point_settings_retained_exactly": bool(
            plan.fixed_point_steps == g32b_plan.fixed_point_steps
            and plan.relaxation == g32b_plan.relaxation
            and plan.max_mode_step_N == g32b_plan.max_mode_step_N
            and plan.background_rtol == g32b_plan.background_rtol
        ),
        "consumed_domain_demoted_to_nonblind_recovery_pilot": bool(
            plan.recovery_pilot_span_N == plan.consumed_G32B_span_N == 0.10
        ),
        "new_heldout_registered_strictly_beyond_consumed_domain": bool(
            plan.prospective_heldout_evaluation_start_N
            > plan.consumed_G32B_span_N
        ),
        "three_nested_levels_and_fine_spacing_retained": True,
        "new_heldout_locked_until_new_pilot_PASS_and_freeze": True,
        "equations_physics_parameters_counterterms_and_existing_thresholds_unchanged": True,
        "zero_background_kernel_trajectory_seed_curve_fit_or_significance": True,
    }
    passed = bool(all(gates.values()))
    return {
        "schema": "apeiron-ap1-m1-logh-positive-branch-recovery-preflight-v1.0",
        "updated_utc": _utc_now(),
        "classification": (
            "M1_LOGH_POSITIVE_BRANCH_RECOVERY_PREFLIGHT_PASS_CONSUMED_DOMAIN_PILOT_OPEN_NEW_HELDOUT_LOCKED_PHYSICAL_KERNEL_BLOCKED"
            if passed
            else "M1_LOGH_POSITIVE_BRANCH_RECOVERY_PREFLIGHT_NONPASS"
        ),
        "authority_sha256": {
            **EXPECTED_AUTHORITIES,
            "AP1/CODE/ap1_m1_logh_positive_branch_recovery_preflight.py": file_sha256(
                code_path
            ),
            "AP1/CODE/test_ap1_m1_logh_positive_branch_recovery_preflight.py": file_sha256(
                test_path
            ),
        },
        "failed_G32B_heldout_audit": dict(RECOVERY_POLICY),
        "recovery_plan": asdict(plan),
        "coordinate_equivalence": canary,
        "gates": gates,
        "all_preflight_gates_pass": passed,
        "checkpoint_eligible": passed,
        "recovery_pilot_eligible": passed,
        "prospective_heldout_eligible": False,
        "background_runs": 0,
        "physical_response_kernel_runs": 0,
        "trajectory_rows_persisted": 0,
        "seed_released": False,
        "G33_released": False,
        "production_background_tolerances_frozen": False,
        "nonpass_report_stored": False,
        "nonpass_used_as_seed": False,
        "equations_changed": False,
        "physics_changed": False,
        "parameters_changed": False,
        "existing_gate_thresholds_changed": False,
        "AP1_status": "ORANGE",
        "claim_boundary": (
            "numerical-coordinate recovery preflight only; the consumed G32B "
            "heldout did not pass and can never validate this method; no accepted "
            "recovery pilot, new tolerance freeze, prospective heldout, production "
            "background, present anchor, response kernel, observable, curve, fit, "
            "or significance"
        ),
        "next_required": (
            "recompute the three N<=0.10 levels as an explicitly non-blind log-H "
            "recovery pilot; only a full pilot PASS may freeze new method tolerances "
            "before the preregistered N=0.105..0.12 heldout is evaluated once"
            if passed
            else "stop fail-closed; do not run, persist, checkpoint, freeze, or seed"
        ),
    }


def checkpoint_markdown(report: dict[str, Any]) -> str:
    plan = report["recovery_plan"]
    canary = report["coordinate_equivalence"]
    return f"""# Apeiron AP1 – G32C log-H positive-branch recovery preflight

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

The registered G32B heldout was attempted exactly once.  It stopped in the
first coarse background integration with `{DIRECT_H_FAILURE}` before a level,
mode/operator audit, or frozen-tolerance comparison completed.  No heldout
report, checkpoint, trajectory, G33 promotion, or seed was written.  The
interval through relative `N=0.10` is consumed and can never be claimed as a
blind validation result.

## Pure numerical-coordinate recovery

The new background chart is `{LOGH_CHART}`.  On `H>0`,
`d ell_H/dN = (dH/dN)/H`, so reconstructing `H` recovers the unchanged direct
equation.  The physical `H` absolute tolerance remains
`{H_ABSOLUTE_TOLERANCE}` and is mapped to the dimensionless chart as
`H_atol/H_reference`.  Equations, matter content, parameters, counterterms,
PV basis, moving split, mode transport, fixed-point settings, and every
existing threshold remain unchanged.

- Maximum H round-trip defect: `{canary['maximum_H_roundtrip_relative_defect']}`
- Maximum RHS reconstruction defect: `{canary['maximum_rhs_reconstruction_relative_defect']}`
- Positive-H analytic toy defect: `{canary['analytic_positive_H_toy_relative_defect']}`
- Machine-scaled cap: `{canary['relative_defect_cap']}`
- Structural gates: `{sum(report['gates'].values())}/{len(report['gates'])}` PASS

## Chronology

- Non-blind recovery pilot: `N={plan['recovery_pilot_evaluation_start_N']}..{plan['recovery_pilot_span_N']}` on `{plan['recovery_pilot_nodes_coarse']}/{plan['recovery_pilot_nodes_middle']}/{plan['recovery_pilot_nodes_fine']}` nodes.
- New prospective heldout: `N={plan['prospective_heldout_evaluation_start_N']}..{plan['prospective_heldout_span_N']}` on `{plan['prospective_heldout_nodes_coarse']}/{plan['prospective_heldout_nodes_middle']}/{plan['prospective_heldout_nodes_fine']}` nodes.
- The new heldout remains locked until the recovery pilot passes completely and
  new method tolerances are frozen separately.

This checkpoint contains zero background runs and no physical response-kernel
run.  AP1 remains ORANGE; G03, G11/G12, AP2/AP3, production curves, fits,
observables, and significances remain locked.
"""


def write_pass_preflight(
    report: dict[str, Any], output: Path, checkpoint: Path
) -> None:
    if not report.get("all_preflight_gates_pass"):
        raise LogHRecoveryPreflightError("NONPASS recovery preflight was not written")
    if output.exists() or checkpoint.exists():
        raise FileExistsError("recovery preflight output already exists")
    output.parent.mkdir(parents=True, exist_ok=True)
    checkpoint.parent.mkdir(parents=True, exist_ok=True)
    with output.open("x", encoding="utf-8") as stream:
        stream.write(json.dumps(report, indent=2, allow_nan=False) + "\n")
    with checkpoint.open("x", encoding="utf-8") as stream:
        stream.write(checkpoint_markdown(report))


def main() -> None:
    parser = argparse.ArgumentParser()
    parser.add_argument("apeiron_root", type=Path)
    parser.add_argument("--test-path", type=Path, required=True)
    parser.add_argument("--output", type=Path, required=True)
    parser.add_argument("--checkpoint", type=Path, required=True)
    args = parser.parse_args()
    report = build_preflight(
        args.apeiron_root, Path(__file__).resolve(), args.test_path
    )
    write_pass_preflight(report, args.output, args.checkpoint)


if __name__ == "__main__":
    main()
