"""Fail-closed convergence pilot for the AP1-M1 early junction.

This is a separate reference path layered on the hash-bound junction-bracket
pilot.  It keeps the frozen v7.13 state read only.  Exploratory interpolated
roots are retained only as diagnostics: the candidate rule uses exact stored
HARD-PASS index zero, fixes its physical-N offset by Friedmann inversion, and
tests Raychaudhuri independently.

The 96-shell momentum quadrature is named in the report but deliberately not
evaluated here.  It remains held out until tolerances have been derived from
the exact-state, 4/2/1 curvature-tail, momentum, and Decimal pilots and frozen
in a separate manifest.  Consequently this module cannot release a checkpoint
or seed.
"""
from __future__ import annotations

from dataclasses import asdict, dataclass
from datetime import datetime, timezone
from pathlib import Path
from typing import Iterable
import json

import numpy as np
from scipy.interpolate import CubicSpline

from ap1_m1_background_preflight import decode_hard_pass_state
from ap1_m1_boundary_match import (
    BoundaryMatchError,
    audit_authorities,
    friedmann_matched_physical_N,
    interpolate_sign_change_roots,
)
from ap1_r2c_high_precision_modes import propagate_endpoint_decimal
from ap1_r2c_self_consistent_candidate import classical_terms, standard_stress
from chi_background_closure import (
    ChiParameters,
    ChiTrajectory,
    curvature_uv_tail_n,
    frozen_renormalization_constants,
    order0_uv_tail_n,
    physical_shells,
    portal_terms,
)
from planck2018_neutrino_closure import Planck2018Pilot


class JunctionConvergenceError(RuntimeError):
    """The pre-registered convergence construction cannot be evaluated."""


@dataclass(frozen=True)
class JunctionPilotConfig:
    prefix_intervals: int = 256
    temporal_strides: tuple[int, ...] = (16, 8, 4, 2)
    curvature_tail_strides: tuple[int, ...] = (4, 2, 1)
    temporal_mode_nodes: int = 64
    momentum_nodes: tuple[int, ...] = (24, 32, 48, 64)
    decimal_digits: int = 80
    roundoff_digits: int = 60
    held_out_momentum_nodes: int = 96
    physical_N_lower: float = -100.0
    physical_N_upper: float = 0.0

    def validate(self, frozen_nodes: int) -> None:
        if self.prefix_intervals < 32 or self.prefix_intervals >= frozen_nodes:
            raise ValueError("prefix must be a proper frozen-trajectory prefix")
        if len(self.temporal_strides) < 3:
            raise ValueError("at least three temporal pilot strides required")
        if tuple(sorted(set(self.temporal_strides), reverse=True)) != self.temporal_strides:
            raise ValueError("temporal strides must be unique and strictly decreasing")
        if any(self.prefix_intervals % value for value in self.temporal_strides):
            raise ValueError("every temporal stride must divide the fixed prefix")
        if any(left % right for left, right in zip(self.temporal_strides[:-1], self.temporal_strides[1:])):
            raise ValueError("temporal pilot grids must be exactly nested")
        if self.curvature_tail_strides != (4, 2, 1):
            raise ValueError("fixed 4/2/1 full-trajectory curvature-tail pilot required")
        if len(self.momentum_nodes) < 3:
            raise ValueError("at least three momentum pilot levels required")
        if tuple(sorted(set(self.momentum_nodes))) != self.momentum_nodes:
            raise ValueError("momentum nodes must be unique and strictly increasing")
        if self.temporal_mode_nodes != self.momentum_nodes[-1]:
            raise ValueError("temporal pilot must use the finest pilot momentum rule")
        if self.held_out_momentum_nodes <= self.momentum_nodes[-1]:
            raise ValueError("held-out momentum level must remain strictly finer")
        if min(self.momentum_nodes) < 4:
            raise ValueError("insufficient momentum quadrature")
        if self.decimal_digits < 48 or not 32 <= self.roundoff_digits < self.decimal_digits:
            raise ValueError("invalid independent Decimal precision pair")
        if not self.physical_N_lower < self.physical_N_upper <= 0.0:
            raise ValueError("invalid shared physical-N inversion bracket")


def prefix_indices(prefix_intervals: int, stride: int) -> np.ndarray:
    if prefix_intervals <= 0 or stride <= 0 or prefix_intervals % stride:
        raise ValueError("positive divisible prefix/stride pair required")
    return np.arange(0, prefix_intervals + 1, stride, dtype=int)


def prefix_trajectory(
    frozen: np.ndarray,
    prefix_intervals: int,
    stride: int,
) -> tuple[np.ndarray, ChiTrajectory]:
    indices = prefix_indices(prefix_intervals, stride)
    rows = np.asarray(frozen[indices], dtype=float)
    N = rows[:, 5]
    H = rows[:, 4]
    # Hdot belongs to the one frozen trajectory, not to a separately refitted
    # coarse prefix.  Construct the derivative once from the complete stored
    # HARD-PASS history, then sample it on every nested convergence grid.  This
    # removes a coarse-grid endpoint-spline artefact without changing H(N), the
    # canonical frequency, or any physical input.
    full_N = np.asarray(frozen[:, 5], dtype=float)
    full_H = np.asarray(frozen[:, 4], dtype=float)
    Hdot = H * CubicSpline(full_N, full_H)(N, 1)
    trajectory = ChiTrajectory(
        N=N,
        H=H,
        Hdot=Hdot,
        sigma=rows[:, 0],
        theta=rows[:, 2],
    ).validated()
    return rows, trajectory


def complete_frozen_trajectory(frozen: np.ndarray) -> ChiTrajectory:
    rows = np.asarray(frozen, dtype=float)
    N = rows[:, 5]
    H = rows[:, 4]
    Hdot = H * CubicSpline(N, H)(N, 1)
    return ChiTrajectory(
        N=N,
        H=H,
        Hdot=Hdot,
        sigma=rows[:, 0],
        theta=rows[:, 2],
    ).validated()


def completed_history_with_frozen_curvature_tail(
    trajectory: ChiTrajectory,
    stored_indices: np.ndarray,
    momenta: np.ndarray,
    weights: np.ndarray,
    digits: int,
    full_curvature_tail: dict,
) -> dict:
    """Propagate modes on one grid but sample one authoritative local tail.

    The adiabatic order-2/4 counterterms contain time derivatives.  Re-fitting
    those derivatives independently on every coarse prefix creates a boundary
    artefact.  Their local values are therefore evaluated once on the complete
    frozen trajectory and sampled at the exact stored indices.  The resolved
    mode propagation remains independently time-refined.
    """

    parameters = ChiParameters()
    tr = trajectory.validated()
    stored_indices = np.asarray(stored_indices, dtype=int)
    if len(stored_indices) != len(tr.N):
        raise ValueError("stored indices must match the trajectory")
    result = propagate_endpoint_decimal(
        tr,
        momenta,
        weights,
        digits=digits,
        record_history=True,
    )
    resolved = result["resolved_pv_history"]
    tail0 = order0_uv_tail_n(tr, parameters)
    tail24 = {
        key: np.asarray(full_curvature_tail[key], dtype=float)[stored_indices]
        for key in ("rho", "pressure", "chi2")
    }
    constants = frozen_renormalization_constants(parameters)
    mass2 = np.asarray(
        portal_terms(tr.sigma, tr.theta, parameters)[0], dtype=float
    )
    C = constants["C_chi2"]
    A = constants["A_g"]
    B = constants["B_G_rhs"]
    result["completed_history"] = {
        "rho": np.asarray(resolved["rho"]) + tail0["rho"] + tail24["rho"]
        - 0.5 * C * mass2 + A + 3.0 * B * tr.H**2,
        "pressure": np.asarray(resolved["pressure"])
        + tail0["pressure"]
        + tail24["pressure"]
        + 0.5 * C * mass2
        - A
        - B * (2.0 * tr.Hdot + 3.0 * tr.H**2),
        "chi2": np.asarray(resolved["chi2"]) + tail0["chi2"] + tail24["chi2"] - C,
    }
    return result


def select_earliest_positive_frequency_root(
    roots: Iterable[dict[str, float | int | bool]],
) -> dict[str, float | int | bool] | None:
    admissible = [
        root for root in roots if bool(root["positive_vacuum_frequency_margin"])
    ]
    if not admissible:
        return None
    return min(admissible, key=lambda item: float(item["N_local"]))


def exact_initial_junction_probe(
    row: np.ndarray,
    Hdot: float,
    quantum_rho: float,
    quantum_pressure: float,
    quantum_chi2: float,
    physical_N_lower: float = -100.0,
    physical_N_upper: float = 0.0,
) -> dict:
    """Evaluate the algebraic M1 match at stored HARD-PASS index zero."""

    row = np.asarray(row, dtype=float)
    if row.shape != (6,) or not np.all(np.isfinite(row)):
        raise ValueError("one finite six-component frozen row required")
    standard = Planck2018Pilot()
    classical = classical_terms(row[:5])
    required = 3.0 * row[4] ** 2 - classical["rho"] - float(quantum_rho)
    matched_N = friedmann_matched_physical_N(
        required,
        standard,
        physical_N_lower,
        physical_N_upper,
    )
    rho_standard, pressure_standard = standard_stress(
        np.array([matched_N]), standard
    )
    friedmann_residual = (
        3.0 * row[4] ** 2
        - classical["rho"]
        - float(quantum_rho)
        - rho_standard[0]
    )
    enthalpy = (
        classical["rho"]
        + classical["pressure"]
        + float(quantum_rho)
        + float(quantum_pressure)
        + rho_standard[0]
        + pressure_standard[0]
    )
    raychaudhuri = float(Hdot + 0.5 * enthalpy)
    ray_scale = max(
        abs(float(Hdot)),
        abs(float(0.5 * enthalpy)),
        float(row[4] ** 2),
        1.0e-300,
    )
    parameters = ChiParameters()
    frequency_margin = float(
        portal_terms(row[0], row[2], parameters)[0] - 2.25 * row[4] ** 2
    )
    return {
        "stored_index": 0,
        "N_local": float(row[5]),
        "N_physical": float(matched_N),
        "N_offset_physical_minus_local": float(matched_N - row[5]),
        "H_Mpl": float(row[4]),
        "required_standard_density_Mpl4": float(required),
        "standard_pressure_Mpl4": float(pressure_standard[0]),
        "quantum_rho_Mpl4": float(quantum_rho),
        "quantum_pressure_Mpl4": float(quantum_pressure),
        "quantum_chi2_Mpl2": float(quantum_chi2),
        "friedmann_residual_Mpl4": float(friedmann_residual),
        "friedmann_normalized": float(
            friedmann_residual / max(abs(3.0 * row[4] ** 2), 1.0e-300)
        ),
        "raychaudhuri_residual_Mpl2": raychaudhuri,
        "raychaudhuri_normalized": float(raychaudhuri / ray_scale),
        "vacuum_frequency_margin_Mpl2": frequency_margin,
        "positive_vacuum_frequency_margin": bool(frequency_margin > 0.0),
    }


def evaluate_prefix_level(
    frozen: np.ndarray,
    prefix_intervals: int,
    stride: int,
    mode_nodes: int,
    digits: int,
    full_curvature_tail: dict | None = None,
    physical_N_lower: float = -100.0,
    physical_N_upper: float = 0.0,
) -> dict:
    indices = prefix_indices(prefix_intervals, stride)
    rows, trajectory = prefix_trajectory(frozen, prefix_intervals, stride)
    parameters = ChiParameters()
    standard = Planck2018Pilot()
    momenta, weights = physical_shells(parameters.Lambda, nodes=mode_nodes)
    if full_curvature_tail is None:
        full_curvature_tail = curvature_uv_tail_n(
            complete_frozen_trajectory(frozen),
            parameters,
            nodes_per_octave=6,
        )
    mode_result = completed_history_with_frozen_curvature_tail(
        trajectory,
        indices,
        momenta,
        weights,
        digits,
        full_curvature_tail,
    )
    quantum = mode_result["completed_history"]
    count = len(rows)
    N_physical = np.full(count, np.nan)
    raychaudhuri = np.full(count, np.nan)
    normalized = np.full(count, np.nan)
    required_density = np.full(count, np.nan)
    frequency_margin = np.asarray(
        portal_terms(rows[:, 0], rows[:, 2], parameters)[0], dtype=float
    ) - 2.25 * rows[:, 4] ** 2

    for index, row in enumerate(rows):
        classical = classical_terms(row[:5])
        required = 3.0 * row[4] ** 2 - classical["rho"] - quantum["rho"][index]
        required_density[index] = required
        try:
            matched_N = friedmann_matched_physical_N(
                float(required),
                standard,
                physical_N_lower,
                physical_N_upper,
            )
        except BoundaryMatchError:
            continue
        rho_standard, pressure_standard = standard_stress(
            np.array([matched_N]), standard
        )
        enthalpy = (
            classical["rho"]
            + classical["pressure"]
            + quantum["rho"][index]
            + quantum["pressure"][index]
            + rho_standard[0]
            + pressure_standard[0]
        )
        residual = float(trajectory.Hdot[index] + 0.5 * enthalpy)
        scale = max(
            abs(float(trajectory.Hdot[index])),
            abs(float(0.5 * enthalpy)),
            float(row[4] ** 2),
            1.0e-300,
        )
        N_physical[index] = matched_N
        raychaudhuri[index] = residual
        normalized[index] = residual / scale

    roots = interpolate_sign_change_roots(
        rows[:, 5],
        N_physical,
        raychaudhuri,
        normalized,
        frequency_margin,
        rows[:, 4],
        required_density,
    )
    selected = select_earliest_positive_frequency_root(roots)
    finite_quantum = all(
        np.all(np.isfinite(np.asarray(quantum[key], dtype=float)))
        for key in ("rho", "pressure", "chi2")
    )
    finite_match_count = int(np.sum(np.isfinite(N_physical)))
    initial_probe = exact_initial_junction_probe(
        rows[0],
        float(trajectory.Hdot[0]),
        float(quantum["rho"][0]),
        float(quantum["pressure"][0]),
        float(quantum["chi2"][0]),
        physical_N_lower,
        physical_N_upper,
    )
    return {
        "prefix_intervals": prefix_intervals,
        "stride": stride,
        "nodes": len(rows),
        "mode_nodes": mode_nodes,
        "decimal_digits": digits,
        "first_stored_index": 0,
        "last_stored_index": prefix_intervals,
        "last_N_local": float(rows[-1, 5]),
        "friedmann_density_matches": finite_match_count,
        "simultaneous_sign_change_brackets": len(roots),
        "positive_frequency_brackets": sum(
            bool(root["positive_vacuum_frequency_margin"]) for root in roots
        ),
        "selected_earliest_admissible": selected,
        "exact_initial_HARD_PASS_junction_probe": initial_probe,
        "initial_raychaudhuri_residual_Mpl2": (
            float(raychaudhuri[0]) if np.isfinite(raychaudhuri[0]) else None
        ),
        "initial_normalized_residual": (
            float(normalized[0]) if np.isfinite(normalized[0]) else None
        ),
        "wronskian_relative_error": float(mode_result["wronskian_relative_error"]),
        "finite": bool(np.all(np.isfinite(rows)) and finite_quantum),
        "root_candidates": roots,
    }


def selection_deltas(levels: list[dict]) -> list[dict[str, float | int]]:
    output: list[dict[str, float | int]] = []
    for coarse, fine in zip(levels[:-1], levels[1:]):
        left = coarse["selected_earliest_admissible"]
        right = fine["selected_earliest_admissible"]
        if left is None or right is None:
            continue
        output.append(
            {
                "coarse_nodes": int(coarse["nodes"]),
                "fine_nodes": int(fine["nodes"]),
                "coarse_stride": int(coarse["stride"]),
                "fine_stride": int(fine["stride"]),
                "coarse_mode_nodes": int(coarse["mode_nodes"]),
                "fine_mode_nodes": int(fine["mode_nodes"]),
                "abs_delta_N_local": abs(float(left["N_local"]) - float(right["N_local"])),
                "abs_delta_N_physical": abs(float(left["N_physical"]) - float(right["N_physical"])),
                "relative_delta_H": abs(float(left["H_Mpl"]) - float(right["H_Mpl"]))
                / max(abs(float(right["H_Mpl"])), 1.0e-300),
                "relative_delta_required_standard_density": abs(
                    float(left["required_standard_density_Mpl4"])
                    - float(right["required_standard_density_Mpl4"])
                )
                / max(abs(float(right["required_standard_density_Mpl4"])), 1.0e-300),
            }
        )
    return output


def single_selection_delta(left_level: dict, right_level: dict) -> dict[str, float]:
    left = left_level["selected_earliest_admissible"]
    right = right_level["selected_earliest_admissible"]
    if left is None or right is None:
        raise JunctionConvergenceError("both comparison levels require a selection")
    return {
        "abs_delta_N_local": abs(float(left["N_local"]) - float(right["N_local"])),
        "abs_delta_N_physical": abs(float(left["N_physical"]) - float(right["N_physical"])),
        "relative_delta_H": abs(float(left["H_Mpl"]) - float(right["H_Mpl"]))
        / max(abs(float(right["H_Mpl"])), 1.0e-300),
        "relative_delta_required_standard_density": abs(
            float(left["required_standard_density_Mpl4"])
            - float(right["required_standard_density_Mpl4"])
        )
        / max(abs(float(right["required_standard_density_Mpl4"])), 1.0e-300),
    }


def initial_probe_delta(left_level: dict, right_level: dict) -> dict[str, float]:
    left = left_level["exact_initial_HARD_PASS_junction_probe"]
    right = right_level["exact_initial_HARD_PASS_junction_probe"]
    if left is None or right is None:
        raise JunctionConvergenceError("both levels require an exact initial probe")
    H2 = max(abs(float(right["H_Mpl"])) ** 2, 1.0e-300)
    chi_scale = max(
        abs(float(right["quantum_chi2_Mpl2"])),
        abs(float(frozen_renormalization_constants(ChiParameters())["C_chi2"])),
        1.0e-300,
    )
    density_scale = max(
        abs(float(right["required_standard_density_Mpl4"])), 1.0e-300
    )
    return {
        "abs_delta_N_physical": abs(
            float(left["N_physical"]) - float(right["N_physical"])
        ),
        "relative_delta_required_standard_density": abs(
            float(left["required_standard_density_Mpl4"])
            - float(right["required_standard_density_Mpl4"])
        )
        / density_scale,
        "abs_delta_quantum_rho_over_H2": abs(
            float(left["quantum_rho_Mpl4"]) - float(right["quantum_rho_Mpl4"])
        )
        / H2,
        "abs_delta_quantum_pressure_over_H2": abs(
            float(left["quantum_pressure_Mpl4"])
            - float(right["quantum_pressure_Mpl4"])
        )
        / H2,
        "abs_delta_quantum_chi2_over_scale": abs(
            float(left["quantum_chi2_Mpl2"])
            - float(right["quantum_chi2_Mpl2"])
        )
        / chi_scale,
        "abs_delta_friedmann_normalized": abs(
            float(left["friedmann_normalized"])
            - float(right["friedmann_normalized"])
        ),
        "abs_delta_raychaudhuri_normalized": abs(
            float(left["raychaudhuri_normalized"])
            - float(right["raychaudhuri_normalized"])
        ),
    }


def successive_initial_probe_deltas(levels: list[dict]) -> list[dict]:
    output = []
    for left, right in zip(levels[:-1], levels[1:]):
        delta = initial_probe_delta(left, right)
        delta.update(
            {
                "coarse_nodes": int(left["nodes"]),
                "fine_nodes": int(right["nodes"]),
                "coarse_stride": int(left["stride"]),
                "fine_stride": int(right["stride"]),
                "coarse_mode_nodes": int(left["mode_nodes"]),
                "fine_mode_nodes": int(right["mode_nodes"]),
            }
        )
        output.append(delta)
    return output


def build_junction_convergence_pilot(
    apeiron_root: Path,
    config: JunctionPilotConfig | None = None,
) -> dict:
    config = JunctionPilotConfig() if config is None else config
    authorities = audit_authorities(apeiron_root)
    frozen = decode_hard_pass_state(apeiron_root / "HANDOFF/CURRENT_STATE_LATEST.npz")
    config.validate(len(frozen))
    full_curvature_tail = curvature_uv_tail_n(
        complete_frozen_trajectory(frozen),
        ChiParameters(),
        nodes_per_octave=6,
    )

    temporal_levels = [
        evaluate_prefix_level(
            frozen,
            config.prefix_intervals,
            stride,
            config.temporal_mode_nodes,
            config.decimal_digits,
            full_curvature_tail,
            config.physical_N_lower,
            config.physical_N_upper,
        )
        for stride in config.temporal_strides
    ]
    finest_temporal = temporal_levels[-1]
    momentum_levels = [
        evaluate_prefix_level(
            frozen,
            config.prefix_intervals,
            config.temporal_strides[-1],
            nodes,
            config.decimal_digits,
            full_curvature_tail,
            config.physical_N_lower,
            config.physical_N_upper,
        )
        for nodes in config.momentum_nodes[:-1]
    ] + [finest_temporal]
    roundoff_low = evaluate_prefix_level(
        frozen,
        config.prefix_intervals,
        config.temporal_strides[-1],
        config.temporal_mode_nodes,
        config.roundoff_digits,
        full_curvature_tail,
        config.physical_N_lower,
        config.physical_N_upper,
    )

    baseline_probe = finest_temporal["exact_initial_HARD_PASS_junction_probe"]
    full_tail_initial = {
        key: float(full_curvature_tail[key][0])
        for key in ("rho", "pressure", "chi2")
    }
    curvature_tail_levels = []
    for stride in config.curvature_tail_strides:
        rows, trajectory = prefix_trajectory(frozen, len(frozen) - 1, stride)
        if stride == 1:
            tail = full_curvature_tail
        else:
            tail = curvature_uv_tail_n(
                trajectory,
                ChiParameters(),
                nodes_per_octave=6,
            )
        quantum = {
            key: float(baseline_probe[f"quantum_{key}_Mpl4"])
            + float(tail[key][0])
            - full_tail_initial[key]
            for key in ("rho", "pressure")
        }
        quantum["chi2"] = (
            float(baseline_probe["quantum_chi2_Mpl2"])
            + float(tail["chi2"][0])
            - full_tail_initial["chi2"]
        )
        probe = exact_initial_junction_probe(
            rows[0],
            float(trajectory.Hdot[0]),
            quantum["rho"],
            quantum["pressure"],
            quantum["chi2"],
            config.physical_N_lower,
            config.physical_N_upper,
        )
        curvature_tail_levels.append(
            {
                "stride": stride,
                "nodes": len(rows),
                "mode_nodes": config.temporal_mode_nodes,
                "decimal_digits": config.decimal_digits,
                "Hdot0_Mpl2": float(trajectory.Hdot[0]),
                "curvature_tail_initial": {
                    key: float(tail[key][0])
                    for key in ("rho", "pressure", "chi2")
                },
                "last_octave_rho_over_total": float(
                    tail["last_octave_rho_over_total"]
                ),
                "exact_initial_HARD_PASS_junction_probe": probe,
            }
        )

    all_levels = temporal_levels + momentum_levels[:-1] + [roundoff_low]
    initial_probes = [
        level["exact_initial_HARD_PASS_junction_probe"] for level in all_levels
    ] + [
        level["exact_initial_HARD_PASS_junction_probe"]
        for level in curvature_tail_levels
    ]
    initial_probe_finite = all(
        probe is not None
        and all(
            isinstance(value, (bool, int, float))
            and (isinstance(value, bool) or np.isfinite(value))
            for value in probe.values()
        )
        for probe in initial_probes
    )
    nested = all(
        np.array_equal(
            prefix_indices(config.prefix_intervals, fine)[:: coarse // fine],
            prefix_indices(config.prefix_intervals, coarse),
        )
        for coarse, fine in zip(config.temporal_strides[:-1], config.temporal_strides[1:])
    )
    wronskian_pass = all(
        level["wronskian_relative_error"] < 1.0e-11 for level in all_levels
    )
    gates = {
        "authority_hashes_exact": True,
        "fixed_prefix_inside_frozen_HARD_PASS_state": True,
        "temporal_grids_exactly_nested": bool(nested),
        "all_pilot_levels_finite": all(level["finite"] for level in all_levels),
        "exact_initial_HARD_PASS_junction_probe_finite_each_level": initial_probe_finite,
        "positive_required_standard_density_each_level": all(
            probe["required_standard_density_Mpl4"] > 0.0
            for probe in initial_probes
        ),
        "positive_initial_inherited_frequency_margin_each_level": all(
            probe["positive_vacuum_frequency_margin"] for probe in initial_probes
        ),
        "curvature_tail_4_2_1_time_pilot_completed": len(curvature_tail_levels) == 3,
        "Wronskian_below_existing_1e_11_each_pilot_level": wronskian_pass,
        "independent_roundoff_pair_completed": True,
        "held_out_momentum_level_not_evaluated": True,
        "pilot_candidate_not_released_as_checkpoint_or_seed": True,
        "old_solver_or_physical_map_not_called": True,
    }
    completed = bool(all(gates.values()))
    return {
        "schema": "apeiron-ap1-m1-junction-convergence-pilot-v1.1",
        "updated_utc": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"),
        "classification": (
            "M1_EXACT_INITIAL_JUNCTION_PILOT_COMPLETE_TOLERANCE_FREEZE_OPEN"
            if completed
            else "M1_JUNCTION_CONVERGENCE_PILOT_NOT_COMPLETE"
        ),
        "config": asdict(config),
        "authority_sha256": authorities,
        "junction_rule": {
            "rule": "use exact stored HARD-PASS index 0; determine the physical-N offset by Friedmann inversion; test Raychaudhuri independently against a pre-frozen pilot-derived tolerance",
            "rationale": "implements the specified earliest junction without interpolation or mode reinitialization, maximizes distance from the tachyonic onset, and avoids ill-conditioned interpolated-root families",
            "frozen_before_held_out_evaluation": True,
        },
        "exploratory_root_scan_role": "independent diagnostic only; no interpolated root is a candidate, checkpoint, or seed",
        "Hdot_sampling_rule": "differentiate the complete frozen 15361-point H(N) cubic spline once and sample that single derivative on every nested prefix grid",
        "local_curvature_tail_sampling_rule": "evaluate adiabatic orders 2+4 once on the complete frozen trajectory and sample the same local values on every nested prefix grid",
        "temporal_pilot": {
            "levels": temporal_levels,
            "successive_exact_initial_probe_deltas": successive_initial_probe_deltas(temporal_levels),
            "root_scan_diagnostic_successive_selection_deltas": selection_deltas(temporal_levels),
        },
        "curvature_tail_time_pilot": {
            "levels": curvature_tail_levels,
            "successive_exact_initial_probe_deltas": successive_initial_probe_deltas(curvature_tail_levels),
        },
        "momentum_pilot": {
            "levels": momentum_levels,
            "successive_exact_initial_probe_deltas": successive_initial_probe_deltas(momentum_levels),
            "root_scan_diagnostic_successive_selection_deltas": selection_deltas(momentum_levels),
        },
        "roundoff_pilot": {
            "low_precision_level": roundoff_low,
            "high_precision_level": finest_temporal,
            "exact_initial_probe_delta": initial_probe_delta(roundoff_low, finest_temporal),
            "root_scan_diagnostic_selection_delta": single_selection_delta(roundoff_low, finest_temporal),
        },
        "held_out": {
            "momentum_nodes": config.held_out_momentum_nodes,
            "evaluated": False,
        },
        "gates": gates,
        "all_structural_pilot_gates_pass": completed,
        "matching_tolerances_frozen": False,
        "checkpoint_released": False,
        "seed_released": False,
        "new_AP1_M1_background_started": 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,
        "next_required": "derive and version a junction tolerance manifest from the exact-state, 4/2/1 curvature-tail, momentum, and roundoff pilot budgets; hash-freeze it, then evaluate 96 momentum shells exactly once as held-out validation",
        "claim_boundary": "convergence pilot only; no accepted M1 junction state, background, response kernel, observable, fit or significance",
    }


def main() -> None:
    import argparse

    parser = argparse.ArgumentParser()
    parser.add_argument("apeiron_root", type=Path)
    parser.add_argument("--output", type=Path)
    args = parser.parse_args()
    report = build_junction_convergence_pilot(args.apeiron_root)
    rendered = json.dumps(report, indent=2) + "\n"
    if args.output is None:
        print(rendered, end="")
    else:
        args.output.write_text(rendered, encoding="utf-8")


if __name__ == "__main__":
    main()
