Generated banner for Nvflare Autofl Report
Generated logo for Nvflare Autofl Report

Nvflare Autofl Report

NVIDIAOfficialCreated by NVIDIA FLARE Team

  • Skill
  • Content generator
  • Software development
  • Apache-2.0
  • Medium risk
  • Has scripts

From the author

Generate a reproducible final report, literature-outcome synthesis, JSON summary, and refreshed progress plot for a stopped or interrupted NVFLARE Auto-FL campaign.
— the description by NVIDIA FLARE Team

AutoGineer summary

Written by AutoGineer from the skill’s own files, not by the author

The nvflare-autofl-report skill provides a standardized way to synthesize evidence from a stopped or interrupted NVFLARE Auto-FL campaign. It processes campaign artifacts to produce a comprehensive Markdown report, a machine-readable JSON summary, and a refreshed progress plot. The tool ensures data integrity by validating campaign state and ledger entries, providing clear insights into baseline performance, best-retained results, and literature-based outcomes without modifying the original campaign data.

What it does

  • Validates campaign evidence from job directories.
  • Generates a reproducible Markdown final report.
  • Creates a machine-readable JSON summary of campaign results.
  • Refreshes the campaign progress plot as a PNG artifact.
  • Synthesizes literature outcomes and candidate lineage.
  • Provides warnings for metric usage, budget changes, or incomplete state.

When to use it

  • After an NVFLARE Auto-FL campaign has reached a cap or stopped.
  • When a campaign was manually interrupted and requires a final report.
  • When you need to extract findings on what helped or failed during optimization.
  • When you need to document the best candidate and its lineage for reproducibility.

Advantages

  • Ensures deterministic and reproducible reporting.
  • Maintains strict separation between campaign execution and reporting.
  • Provides clear provenance for candidate lineage and literature checkpoints.
  • Safely handles interrupted campaigns with explicit confirmation.
  • Prevents modification of original campaign results or state.

Usage examples

“Generate the final report for my completed Auto-FL campaign in /jobs/experiment_01.”
The tool validates the job directory and produces autofl_final_report.md and autofl_report_summary.json with the campaign's performance metrics.
“My campaign was interrupted, how do I get a report?”
After confirming no active processes remain, run the script with --confirm-interrupted to generate the report based on the available state.

Install

  1. Download the skill as a ZIP:

    https://mp-staging.autogineer.com/agent-skills/nvidia/nvflare-autofl-report/download
  2. Unzip it into the cross-client skills folder of your project:

    mkdir -p .agents/skills && unzip nvflare-autofl-report.zip -d .agents/skills

Documentation by NVIDIA FLARE Team

NVFLARE Auto-FL Report

Purpose

Turn the recorded evidence from a stopped NVFLARE Auto-FL campaign into a reproducible Markdown report, machine-readable JSON summary, and refreshed progress plot without changing the campaign or its results.

Use When

Use this skill after an NVFLARE Auto-FL campaign has stopped, reached an explicit cap, hit a hard blocker, or was manually interrupted. Use it when the user asks for the final report, achieved improvement, literature findings, failed ideas, reproduction details, or a refreshed progress plot.

Do Not Use When

Do not use this skill to start or continue optimization, invent missing results, or finalize a campaign that is still running. Use nvflare-autofl for the active candidate loop. Do not stop an active campaign merely because the user asks for a status snapshot.

Available Scripts

ScriptPurposeArguments
scripts/generate_report.pyValidate stopped campaign evidence and generate the report, JSON summary, and optional progress plot.Campaign job directory; optional evidence/output paths, interruption confirmation, plot settings, and agent context. Run python scripts/generate_report.py --help for the complete CLI.

Run this bundled CLI directly with Python. This skill has no NVFLARE or agent run_script() helper; do not invent or call one. Resolve the script from the directory containing this SKILL.md, as shown in the workflow.

Workflow

  1. Locate the job directory containing results.tsv, autofl.yaml, .nvflare/autofl/campaign_state.json, and candidate manifests.

  2. Confirm execution has stopped. Prefer campaign state with final_response_allowed=true. When a process was abruptly interrupted and state is stale, independently confirm no campaign or job process remains, then use --confirm-interrupted. Before finalizing, confirm that campaign state, results.tsv, and available candidate manifests contain no pending candidate. Finalize or abandon pending work through nvflare-autofl first.

  3. Generate the deterministic report artifacts:

    python "$REPORTER" <job-dir>

    Resolve REPORTER once to the absolute path of scripts/generate_report.py relative to this SKILL.md. Do not assume a provider-specific skill installation directory or $CODEX_HOME.

  4. Read both autofl_final_report.md and autofl_report_summary.json. Check warnings about metric use, executed budget changes, campaign-state/ledger disagreement, missing provenance, and incomplete interruption state.

  5. Give the user the baseline, best score, delta, strongest candidate lineage, concise "what helped" and "what did not help" findings, literature ideas that helped or failed, selection rationale, reliability caveats, and absolute artifact paths.

The helper attempts to refresh progress.png by reusing the product Auto-FL plotter. Plotting is optional evidence: if plotting dependencies are missing or the artifact is not a valid PNG, the helper preserves the artifact, records a warning and artifacts.progress_plot_available=false, and still writes the Markdown and JSON reports without embedding the broken image. It does not modify source, candidate manifests, results.tsv, or campaign state. All relative helper path options, including --plotter, resolve from the campaign job directory rather than the shell's current working directory. The helper holds the campaign lifecycle lock from evidence loading through artifact writes. It refuses a busy campaign and rejects writable output paths that alias campaign evidence, job.py, trust-contract source paths, or another output, including filesystem aliases. Outputs must not match the trust contract's allowed source-creation patterns. A read-only campaign archive is reportable only when its persisted campaign.lock already exists and writable output paths are supplied; the persisted lock target alone does not imply live contention.

Interrupted Campaigns

The report helper must refuse state with final_response_allowed=false unless the human has said the campaign was stopped/interrupted and the agent confirms that execution is no longer active. Only then run:

python "$REPORTER" <job-dir> --confirm-interrupted

This records a reporting-time interruption assertion; it does not rewrite the campaign state or pretend the runner finalized cleanly. It bypasses only stale stop state. A candidate ledger row or pending-candidate state always blocks finalization. Any available candidate manifest whose status is not a recognized terminal value (keep, discard, crash, or abandoned) also blocks. Missing, unknown, or unreadable status is unfinished evidence because completion cannot be established. The agent must finalize or abandon that candidate first.

Troubleshooting

Error or symptomCauseSolution
Reporting is refused because final_response_allowed=false.The campaign may still be active, or persisted state may be stale after an interruption.Confirm no campaign or job process remains. If a human confirmed the interruption, rerun with --confirm-interrupted; otherwise continue or stop the campaign through nvflare-autofl.
Reporting is refused for a pending candidate or unknown manifest status.The recorded evidence cannot establish that candidate execution finished.Finalize or abandon the candidate through nvflare-autofl, then regenerate the report.
The campaign lock is busy.Another campaign or reporting process holds the lifecycle lock.Identify and wait for the active process. Never bypass or delete a live lock.
The JSON says progress_plot_available=false.Plotting dependencies are unavailable or the generated artifact is not a valid PNG.Use the completed Markdown and JSON reports, review their warning, and install the campaign's plotting dependencies before retrying if a plot is required.
An output path is rejected.The path aliases protected campaign evidence, source, or another output.Choose distinct writable output paths outside protected campaign inputs and rerun.

Report Contract

The final report must include:

  • campaign termination reason, objective, metric source, direction, environment, cap, abandoned-candidate count, and declared fixed budget;

  • baseline, best retained result, score delta, runtime, failures, and status counts;

  • selected-candidate rationale, strict retained improvements, representative non-improvements, grouped failures, and outcomes by recorded algorithm family;

  • running-best trajectory selected by first, final, and largest objective improvements, plus a refreshed progress.png when plotting is available, with explicit plot availability in the JSON summary otherwise;

  • best-candidate manifest, patch hash, base-candidate lineage, inherited code changes, artifacts, and exact baseline/best commands;

  • every recorded literature checkpoint, its event ID and source markers, explicitly linked candidates, and whether measured evidence helped, matched, failed, or did not confirm the idea;

  • discarded/crashed ideas and deterministic comparability warnings;

  • optional agent model, reasoning effort, cost, or tooling notes when supplied;

  • absolute paths to autofl.yaml, results.tsv, campaign state, progress.png, autofl_report_summary.json, and autofl_final_report.md.

The report must distinguish imported/declared budget from executed command arguments. It must warn when the best candidate changed training compute or comparison population, when authoritative state disagrees with ledger-derived accounting, or when repeated selection used a test-like metric. Product Auto-FL campaigns retain the imported min or max direction; the report rejects legacy minimization evidence without direction provenance. It must not add PR-specific sections such as "Product Findings" unless the user explicitly requests them. best means a scored retained baseline or keep row; an unretained scored discard may appear only as best_observed. Candidate and crash rows never become retained best results, milestones, or literature improvements. Baseline identity is determined strictly by status=baseline, matching the campaign guard. The report preserves per-run metric name, extraction source, artifact, candidate kind, algorithm family, and literature event linkage. It does not infer algorithm families or mechanisms from candidate names.

Read report-contract.md when interpreting lineage, literature outcomes, budget warnings, or interrupted state.

Limitations

  • The report is only as complete as the persisted ledger, campaign state, and candidate manifests; it does not validate or reconstruct unrecorded claims.

  • Results from a single campaign do not establish robustness or generalization.

  • The helper does not start, stop, resume, or resubmit campaign jobs.

  • Progress plotting remains optional, so Markdown and JSON may be produced without an embeddable PNG.

  • Copied campaign archives may retain only partial provenance when recorded absolute manifest paths are no longer available.

Requirements

  • Treat results.tsv as recorded evidence; never repair scores by guessing.

  • Work without Git. Do not commit or push unless the user separately asks.

  • Preserve the campaign and job sources exactly as found.

  • Never bypass campaign-lock contention or output-path collision checks.

  • Use candidate manifests when available, but still report partial provenance when copied artifacts make old absolute manifest paths unavailable.

  • Keep conclusions proportional to the evidence. A single run is a candidate, not a robustness claim.

  • For POC/production, report standard NVFLARE job IDs and downloaded artifacts already present in the ledger; do not resubmit jobs during reporting.

Files

scripts/generate_report.py

#!/usr/bin/env python3
# Copyright (c) 2026, NVIDIA CORPORATION.  All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Generate a reproducible final report for a stopped Auto-FL campaign."""

from __future__ import annotations

import argparse
import csv
import functools
import importlib.util
import inspect
import json
import math
import os
import re
import shlex
import sys
import tempfile
import textwrap
from collections import Counter
from contextlib import contextmanager
from dataclasses import asdict, dataclass
from datetime import datetime, timezone
from pathlib import Path, PurePosixPath
from typing import Any, Dict, Iterable, Iterator, List, Optional, Sequence, Tuple
from urllib.parse import quote

try:
    import fcntl
except ImportError:  # pragma: no cover - NVFlare supports POSIX platforms
    fcntl = None

try:
    import yaml
except ImportError:  # pragma: no cover - NVFlare installs PyYAML
    yaml = None


SUMMARY_SCHEMA_VERSION = "nvflare.autofl.report.v1"
PNG_SIGNATURE = b"\x89PNG\r\n\x1a\n"
ATTEMPT_STATUSES = {"candidate", "keep", "discard", "crash"}
FINALIZED_SCORE_STATUSES = {"baseline", "keep", "discard"}
RETAINED_STATUSES = {"baseline", "keep"}
TERMINAL_MANIFEST_STATUSES = {"abandoned", "crash", "discard", "keep"}
CAMPAIGN_LOCK_PATH = ".nvflare/autofl/campaign.lock"
FALLBACK_TRAINING_BUDGET_ARGS = {
    "aggregation_epochs",
    "aggregator",
    "aggregator_data_kind",
    "alpha",
    "batch_size",
    "cross_site_eval",
    "eval_batch_size",
    "final_eval_clients",
    "local_train_steps",
    "max_model_params",
    "min_clients",
    "model_arch",
    "n_clients",
    "num_clients",
    "num_rounds",
    "seed",
}
SPECIAL_COMPARISON_BUDGET_ARGS = {"aggregator_data_kind", "cross_site_eval"}
FIXED_BUDGET_TO_CLI = {
    "min_clients": "min_clients",
    "num_clients": "n_clients",
    "num_rounds": "num_rounds",
}
SOURCE_RE = re.compile(r"\[src:\s*([^\]]+)\]", re.IGNORECASE)
ARXIV_RE = re.compile(r"\barxiv\s*:\s*(\d{4}\.\d{4,5})", re.IGNORECASE)


def product_autofl_script_path(name: str) -> Path:
    return Path(__file__).resolve().parents[2] / "nvflare-autofl" / "scripts" / name


def _load_product_module(script_name: str, module_name: str, *, catch_system_exit: bool = False):
    module_path = product_autofl_script_path(script_name)
    if not module_path.is_file():
        return None
    spec = importlib.util.spec_from_file_location(module_name, module_path)
    if spec is None or spec.loader is None:
        return None
    module = importlib.util.module_from_spec(spec)
    previous_module = sys.modules.get(module_name)
    sys.modules[module_name] = module
    try:
        spec.loader.exec_module(module)
        return module
    except SystemExit:
        if not catch_system_exit:
            raise
        return None
    except Exception:
        return None
    finally:
        if previous_module is None:
            sys.modules.pop(module_name, None)
        else:
            sys.modules[module_name] = previous_module


def load_training_budget_args() -> set:
    """Use the installed producer's comparison vocabulary when it is available."""

    module = _load_product_module("run_job_campaign.py", "nvflare_autofl_report_runner_contract")
    if module is None:
        return set(FALLBACK_TRAINING_BUDGET_ARGS)
    mapping = getattr(module, "COMPARISON_BUDGET_TO_CLI", None)
    fixed_mapping = getattr(module, "FIXED_BUDGET_TO_CLI", None)
    mappings = (mapping, fixed_mapping)
    if not all(
        isinstance(candidate, dict)
        and all(isinstance(key, str) and isinstance(value, str) for key, value in candidate.items())
        for candidate in mappings
    ):
        return set(FALLBACK_TRAINING_BUDGET_ARGS)
    return (
        set(mapping)
        | set(mapping.values())
        | set(fixed_mapping)
        | set(fixed_mapping.values())
        | SPECIAL_COMPARISON_BUDGET_ARGS
    )


def load_campaign_guard_contract():
    """Load product comparison semantics when the Auto-FL producer skill is installed."""

    module = _load_product_module(
        "campaign_guard.py", "nvflare_autofl_report_campaign_guard_contract", catch_system_exit=True
    )
    if module is None or not all(
        callable(getattr(module, name, None)) for name in ("validate_mode", "better", "improvement_over_baseline")
    ):
        return None
    return module


@functools.lru_cache(maxsize=1)
def campaign_guard_contract():
    return load_campaign_guard_contract()


TRAINING_BUDGET_ARGS = load_training_budget_args()


@dataclass(frozen=True)
class RunRecord:
    index: int
    status: str
    name: str
    score: Optional[float]
    runtime_seconds: float
    changed_files: str
    diff_summary: str
    run_command: str
    artifacts: str
    failure_reason: str
    candidate_manifest: str
    base_candidate: str
    patch_sha256: str
    metric_name: str = ""
    metric_source: str = ""
    metric_artifact: str = ""
    candidate_kind: str = ""
    algorithm_family: str = ""
    literature_event_id: str = ""


def parse_args(argv: Optional[Sequence[str]] = None) -> argparse.Namespace:
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("campaign_dir", nargs="?", default=".", help="job directory containing Auto-FL artifacts")
    parser.add_argument("--results", default="results.tsv")
    parser.add_argument("--state", default=".nvflare/autofl/campaign_state.json")
    parser.add_argument("--autofl-yaml", default="autofl.yaml")
    parser.add_argument("--progress", default="progress.png")
    parser.add_argument("--output", default="autofl_final_report.md")
    parser.add_argument("--summary-json", default="autofl_report_summary.json")
    parser.add_argument(
        "--plotter",
        help="override path to nvflare-autofl plot_progress.py; relative paths resolve from campaign_dir",
    )
    parser.add_argument("--metric", help="override optimization metric label")
    parser.add_argument("--confirm-interrupted", action="store_true", help="confirm an abruptly interrupted campaign")
    parser.add_argument("--agent-model")
    parser.add_argument("--reasoning-effort")
    parser.add_argument("--agent-cost")
    parser.add_argument("--agent-context", help="optional JSON or text file with agent/tooling context")
    parser.add_argument("--max-milestones", type=int, default=12)
    parser.add_argument("--max-non-improvements", type=int, default=10)
    return parser.parse_args(argv)


def resolve_path(root: Path, value: str) -> Path:
    try:
        path = Path(value).expanduser()
    except (OSError, RuntimeError) as exc:
        raise ValueError(f"cannot resolve report path {value!r}: {exc}") from exc
    return path if path.is_absolute() else root / path


def canonical_path(path: Path) -> Path:
    try:
        return path.resolve(strict=False)
    except (OSError, RuntimeError) as exc:
        raise ValueError(f"cannot canonicalize report path {path}: {exc}") from exc


def path_comparison_key(path: Path) -> str:
    """Return a portable key that also catches aliases on case-insensitive filesystems."""

    return os.path.normcase(str(canonical_path(path))).casefold()


def paths_alias(left: Path, right: Path) -> bool:
    if path_comparison_key(left) == path_comparison_key(right):
        return True
    try:
        return left.exists() and right.exists() and os.path.samefile(left, right)
    except OSError:
        return False


def validate_output_paths(outputs: Dict[str, Path], protected: Dict[str, Path]) -> None:
    output_items = list(outputs.items())
    for index, (left_name, left_path) in enumerate(output_items):
        for right_name, right_path in output_items[index + 1 :]:
            if paths_alias(left_path, right_path):
                raise ValueError(
                    f"report outputs --{left_name} and --{right_name} must be distinct: {canonical_path(left_path)}"
                )
        for protected_name, protected_path in protected.items():
            if paths_alias(left_path, protected_path):
                raise ValueError(
                    f"report output --{left_name} aliases protected campaign input {protected_name}: "
                    f"{canonical_path(left_path)}"
                )


def campaign_source_contract(root: Path, config: Dict[str, Any]) -> Tuple[Dict[str, Path], List[str]]:
    protected = {"job source": root / "job.py"}
    trust_contract = config.get("trust_contract", {})
    if trust_contract is None:
        trust_contract = {}
    if not isinstance(trust_contract, dict):
        raise ValueError("autofl.yaml trust_contract must be a mapping")

    allowed_edit_paths = trust_contract.get("allowed_edit_paths") or []
    if not isinstance(allowed_edit_paths, list):
        raise ValueError("autofl.yaml trust_contract.allowed_edit_paths must be a list")
    root_resolved = root.resolve()
    for index, value in enumerate(allowed_edit_paths, start=1):
        if not isinstance(value, str) or not value.strip():
            raise ValueError("autofl.yaml trust_contract.allowed_edit_paths must contain non-empty strings")
        candidate = canonical_path(resolve_path(root, value))
        try:
            relative = candidate.relative_to(root_resolved)
        except ValueError as exc:
            raise ValueError(f"trust-contract source path escapes the campaign directory: {value}") from exc
        if not relative.parts:
            raise ValueError(f"trust-contract source path must name a file: {value}")
        protected[f"managed source {index} ({relative.as_posix()})"] = candidate

    allowed_create_patterns = trust_contract.get("allowed_create_patterns") or []
    if not isinstance(allowed_create_patterns, list) or not all(
        isinstance(pattern, str) and pattern for pattern in allowed_create_patterns
    ):
        raise ValueError("autofl.yaml trust_contract.allowed_create_patterns must be a list of patterns")
    return protected, list(dict.fromkeys(allowed_create_patterns))


def validate_output_create_patterns(outputs: Dict[str, Path], root: Path, patterns: Sequence[str]) -> None:
    root_resolved = root.resolve()
    for output_name, output_path in outputs.items():
        try:
            relative = canonical_path(output_path).relative_to(root_resolved)
        except ValueError:
            continue
        posix_path = PurePosixPath(relative.as_posix())
        if any(
            posix_path.match(pattern) or (pattern.startswith("**/") and posix_path.match(pattern[3:]))
            for pattern in patterns
        ):
            raise ValueError(
                f"report output --{output_name} matches trust_contract.allowed_create_patterns: {relative.as_posix()}"
            )


@contextmanager
def locked_campaign_workspace(workspace: Path, action: str) -> Iterator[None]:
    """Serialize report generation with Auto-FL lifecycle actions."""

    if fcntl is None:
        raise ValueError("platform without fcntl is unsupported for Auto-FL reporting")
    lock_path = workspace / CAMPAIGN_LOCK_PATH
    try:
        lock_path.parent.mkdir(parents=True, exist_ok=True)
    except OSError as exc:
        raise ValueError(f"cannot access Auto-FL campaign lock directory {lock_path.parent}: {exc}") from exc
    descriptor = None
    acquired = False
    lock_created = False
    acquisition_complete = False
    try:
        try:
            descriptor = os.open(lock_path, os.O_RDONLY)
        except FileNotFoundError:
            try:
                descriptor = os.open(lock_path, os.O_CREAT | os.O_EXCL | os.O_RDWR, 0o600)
                lock_created = True
            except FileExistsError:
                descriptor = os.open(lock_path, os.O_RDONLY)
        try:
            fcntl.flock(descriptor, fcntl.LOCK_EX | fcntl.LOCK_NB)
            acquired = True
        except BlockingIOError as exc:
            raise ValueError(
                f"Auto-FL campaign workspace is already in use: {workspace}; "
                "wait for the active lifecycle action to finish, then retry"
            ) from exc
        if lock_created:
            os.write(
                descriptor,
                (
                    json.dumps({"pid": os.getpid(), "action": action, "workspace": str(workspace.resolve())}) + "\n"
                ).encode("utf-8"),
            )
        acquisition_complete = True
        yield
    except OSError as exc:
        if acquisition_complete:
            raise
        raise ValueError(f"cannot acquire Auto-FL campaign lock {lock_path}: {exc}") from exc
    finally:
        if descriptor is not None:
            if acquired:
                fcntl.flock(descriptor, fcntl.LOCK_UN)
            os.close(descriptor)


def finite_float(value: Any) -> Optional[float]:
    try:
        result = float(value)
    except (TypeError, ValueError):
        return None
    return result if math.isfinite(result) else None


def load_results(path: Path) -> List[RunRecord]:
    if not path.is_file():
        raise ValueError(f"Auto-FL ledger not found: {path}")
    records = []
    with path.open("r", encoding="utf-8", newline="") as f:
        reader = csv.DictReader(f, delimiter="\t")
        if not reader.fieldnames or not {"status", "score"}.issubset(reader.fieldnames):
            raise ValueError(f"Auto-FL ledger is missing required status/score columns: {path}")
        for index, row in enumerate(reader):
            records.append(
                RunRecord(
                    index=index,
                    status=(row.get("status") or "").strip().lower(),
                    name=(row.get("name") or row.get("candidate") or row.get("commit") or f"row_{index}").strip(),
                    score=finite_float(row.get("score")),
                    runtime_seconds=max(0.0, finite_float(row.get("runtime_seconds")) or 0.0),
                    changed_files=(row.get("changed_files") or "").strip(),
                    diff_summary=(row.get("diff_summary") or row.get("description") or "").strip(),
                    run_command=(row.get("run_command") or "").strip(),
                    artifacts=(row.get("artifacts") or "").strip(),
                    failure_reason=(row.get("failure_reason") or "").strip(),
                    candidate_manifest=(row.get("candidate_manifest") or "").strip(),
                    base_candidate=(row.get("base_candidate") or "").strip(),
                    patch_sha256=(row.get("patch_sha256") or "").strip(),
                    metric_name=(row.get("metric_name") or "").strip(),
                    metric_source=(row.get("metric_source") or "").strip(),
                    metric_artifact=(row.get("metric_artifact") or "").strip(),
                    candidate_kind=(row.get("candidate_kind") or "").strip().lower(),
                    algorithm_family=(row.get("algorithm_family") or "").strip().lower(),
                    literature_event_id=(row.get("literature_event_id") or "").strip(),
                )
            )
    if not records:
        raise ValueError(f"Auto-FL ledger has no rows: {path}")
    return records


def load_json(path: Path) -> Dict[str, Any]:
    try:
        value = json.loads(path.read_text(encoding="utf-8"))
    except (OSError, json.JSONDecodeError) as exc:
        raise ValueError(f"cannot read JSON from {path}: {exc}") from exc
    if not isinstance(value, dict):
        raise ValueError(f"expected a JSON object in {path}")
    return value


def load_config(path: Path) -> Dict[str, Any]:
    if not path.is_file():
        return {}
    if yaml is None:
        raise ValueError("PyYAML is required to read autofl.yaml")
    try:
        value = yaml.safe_load(path.read_text(encoding="utf-8")) or {}
    except (OSError, yaml.YAMLError) as exc:
        raise ValueError(f"cannot read Auto-FL config from {path}: {exc}") from exc
    if not isinstance(value, dict):
        raise ValueError(f"expected a YAML mapping in {path}")
    return value


def validate_mode(mode: str) -> str:
    mode = str(mode).strip().lower()
    contract = campaign_guard_contract()
    if contract is not None:
        return contract.validate_mode(mode)
    if mode not in {"min", "max"}:
        raise ValueError(f"objective mode must be 'min' or 'max', but got {mode!r}")
    return mode


def better(score: Optional[float], incumbent: Optional[float], mode: str = "max") -> bool:
    contract = campaign_guard_contract()
    if contract is not None:
        return contract.better(score, incumbent, validate_mode(mode))
    mode = validate_mode(mode)
    if score is None:
        return False
    if incumbent is None:
        return True
    return score < incumbent if mode == "min" else score > incumbent


def normalize_contract_sections(config: Dict[str, Any]) -> Tuple[Dict[str, Any], List[str]]:
    normalized = dict(config)
    warnings = []
    for section in ("objective", "environment", "budget"):
        if section not in normalized:
            continue
        value = normalized[section]
        if not isinstance(value, dict):
            kind = "null" if value is None else type(value).__name__
            warnings.append(
                f"autofl.yaml section '{section}' is {kind}, not a mapping; it was treated as an empty section."
            )
            normalized[section] = {}
    return normalized, warnings


def metric_contract(
    config: Dict[str, Any], state: Dict[str, Any], args: argparse.Namespace
) -> Tuple[str, str, str, str]:
    objective = config.get("objective") if isinstance(config.get("objective"), dict) else {}
    metric = (
        args.metric or objective.get("optimization_metric") or objective.get("metric") or state.get("metric") or "score"
    )
    requested = objective.get("requested_metric") or objective.get("metric") or metric
    measurement_source = objective.get("metric_source") or "NVFlare metric artifacts"
    contract_source = objective.get("metric_contract_source") or "not declared"
    return str(metric), str(requested), str(measurement_source), str(contract_source)


def metric_mode(config: Dict[str, Any], state: Dict[str, Any]) -> str:
    objective = config.get("objective") if isinstance(config.get("objective"), dict) else {}
    direction = objective.get("direction")
    if direction is not None:
        direction = {"maximize": "max", "minimize": "min"}.get(str(direction).strip().lower(), direction)
    declared = [
        ("autofl.yaml objective.mode", objective.get("mode")),
        ("autofl.yaml objective.direction", direction),
        ("campaign state mode", state.get("mode")),
    ]
    resolved = []
    for source, value in declared:
        if value is not None:
            resolved.append((source, validate_mode(value)))
    modes = {value for _, value in resolved}
    if len(modes) > 1:
        details = ", ".join(f"{source}={value!r}" for source, value in resolved)
        raise ValueError(f"metric direction disagrees across campaign artifacts: {details}")
    mode = next(iter(modes), "max")
    if mode == "min" and not objective.get("mode_contract_source"):
        raise ValueError(
            "legacy minimization campaign lacks native metric-direction provenance; initialize a fresh campaign"
        )
    return mode


def verify_stopped(state_path: Path, confirm_interrupted: bool) -> Tuple[Dict[str, Any], str, List[str]]:
    warnings = []
    if not state_path.is_file():
        if not confirm_interrupted:
            raise ValueError(
                f"campaign state not found at {state_path}; use --confirm-interrupted only after confirming execution stopped"
            )
        warnings.append("Campaign state was unavailable; the user explicitly confirmed interruption.")
        return {}, "user_confirmed_interruption", warnings

    state = load_json(state_path)
    if state.get("final_response_allowed") is True:
        return state, str(state.get("reason") or "campaign_stopped"), warnings
    if not confirm_interrupted:
        raise ValueError(
            "campaign state still has final_response_allowed=false; stop the campaign or pass --confirm-interrupted "
            "after independently confirming its processes are no longer running"
        )
    warnings.append("Campaign state remained active; the user explicitly confirmed that execution was interrupted.")
    return state, "user_confirmed_interruption", warnings


def is_baseline(record: RunRecord) -> bool:
    return record.status == "baseline"


def is_finalized_scored_record(record: RunRecord) -> bool:
    return record.score is not None and record.status in FINALIZED_SCORE_STATUSES


def candidate_manifest_paths(campaign_root: Path, records: Sequence[RunRecord]) -> List[Path]:
    candidates_root = campaign_root / ".nvflare" / "autofl" / "candidates"
    paths = set(candidates_root.glob("*/candidate_manifest.json")) if candidates_root.is_dir() else set()
    paths.update(
        resolve_path(campaign_root, record.candidate_manifest) for record in records if record.candidate_manifest
    )
    return sorted(paths)


def candidate_manifest_evidence(
    campaign_root: Path, records: Sequence[RunRecord]
) -> Tuple[List[Tuple[Path, str]], List[Tuple[Path, str]]]:
    unfinished = []
    unreadable = []
    for path in candidate_manifest_paths(campaign_root, records):
        if not path.is_file():
            continue
        try:
            manifest = load_json(path)
        except ValueError as exc:
            unreadable.append((path, str(exc)))
            continue
        status = str(manifest.get("status") or "").strip().lower()
        if status not in TERMINAL_MANIFEST_STATUSES:
            unfinished.append((path, status))
    return unfinished, unreadable


def verify_no_pending_candidates(campaign_root: Path, state: Dict[str, Any], records: Sequence[RunRecord]) -> None:
    evidence = []
    pending_count = finite_float(state.get("pending_candidates"))
    if pending_count is not None and pending_count > 0:
        evidence.append(f"campaign state reports pending_candidates={state.get('pending_candidates')}")
    if state.get("pending_candidate_manifest"):
        evidence.append(f"campaign state names {state['pending_candidate_manifest']}")

    ledger_rows = [record for record in records if record.status == "candidate"]
    if ledger_rows:
        rows = ", ".join(f"row {record.index + 1} ({record.name})" for record in ledger_rows[:5])
        if len(ledger_rows) > 5:
            rows += f", and {len(ledger_rows) - 5} more"
        evidence.append(f"results.tsv contains pending candidate rows: {rows}")

    manifests, unreadable_manifests = candidate_manifest_evidence(campaign_root, records)
    if manifests:
        paths = ", ".join(f"{path.resolve()} (status={status or '<missing>'})" for path, status in manifests[:3])
        if len(manifests) > 3:
            paths += f", and {len(manifests) - 3} more"
        evidence.append(f"candidate manifests have unfinished or unknown status: {paths}")
    if unreadable_manifests:
        details = "; ".join(f"{path.resolve()} ({error})" for path, error in unreadable_manifests[:3])
        if len(unreadable_manifests) > 3:
            details += f"; and {len(unreadable_manifests) - 3} more"
        evidence.append(f"candidate manifests could not be read and may contain unfinished work: {details}")

    if evidence:
        raise ValueError(
            "cannot finalize while candidate evidence is unfinished; finalize or abandon pending candidates first. "
            "--confirm-interrupted bypasses only stale campaign stop state, never unfinished candidate evidence. "
            + " ".join(evidence)
        )


def scored_records(records: Iterable[RunRecord]) -> List[RunRecord]:
    return [record for record in records if is_finalized_scored_record(record)]


def select_baseline(records: Sequence[RunRecord]) -> Optional[RunRecord]:
    return next(
        (record for record in records if record.status == "baseline" and is_finalized_scored_record(record)), None
    )


def select_best(records: Sequence[RunRecord], retained_only: bool = False, mode: str = "max") -> Optional[RunRecord]:
    candidates = [
        record
        for record in records
        if is_finalized_scored_record(record)
        and (record.status in RETAINED_STATUSES if retained_only else record.status in FINALIZED_SCORE_STATUSES)
    ]
    if not candidates:
        return None
    selector = min if validate_mode(mode) == "min" else max
    return selector(candidates, key=lambda record: record.score)


def running_best_milestones(records: Sequence[RunRecord], limit: int, mode: str = "max") -> List[Dict[str, Any]]:
    milestones = []
    incumbent = None
    for record in records:
        if not is_finalized_scored_record(record) or not better(record.score, incumbent, mode):
            continue
        previous = incumbent
        incumbent = record.score
        milestones.append(
            {
                "row": record.index + 1,
                "name": record.name,
                "status": record.status,
                "score": record.score,
                "delta_from_previous_best": None if previous is None else record.score - previous,
                "improvement_from_previous_best": (
                    None if previous is None else improvement_amount(record.score, previous, mode)
                ),
                "hypothesis": record.diff_summary,
                "changed_files": split_files(record.changed_files),
                "candidate_kind": inferred_candidate_kind(record),
                "algorithm_family": record.algorithm_family
                or ("baseline" if record.status == "baseline" else "unclassified"),
                "literature_event_id": record.literature_event_id,
            }
        )
    if limit > 0 and len(milestones) > limit:
        if limit == 1:
            return [milestones[-1]]
        selected = {0, len(milestones) - 1}
        middle = sorted(
            range(1, len(milestones) - 1),
            key=lambda index: (
                milestones[index]["improvement_from_previous_best"] or 0.0,
                -milestones[index]["row"],
            ),
            reverse=True,
        )
        selected.update(middle[: max(0, limit - 2)])
        return [milestones[index] for index in sorted(selected)]
    return milestones


def improvement_amount(score: float, incumbent: float, mode: str = "max") -> float:
    contract = campaign_guard_contract()
    if contract is not None:
        return contract.improvement_over_baseline(incumbent, score, validate_mode(mode))
    return incumbent - score if validate_mode(mode) == "min" else score - incumbent


def split_files(value: str) -> List[str]:
    return [item.strip() for item in value.split(",") if item.strip() and item.strip().lower() != "none"]


def candidate_lineage(best: Optional[RunRecord], records: Sequence[RunRecord]) -> Dict[str, Any]:
    if best is None:
        return {"candidates": [], "changed_files": [], "complete": False}
    by_name = {record.name: record for record in records if record.name}
    chain = []
    changed_files = []
    seen = set()
    current = best
    complete = True
    while current:
        if current.name in seen:
            complete = False
            break
        seen.add(current.name)
        chain.append(current.name)
        changed_files.extend(split_files(current.changed_files))
        if not current.base_candidate:
            complete = current.status == "baseline"
            break
        base_candidate = current.base_candidate
        current = by_name.get(base_candidate)
        if current is None:
            chain.append(base_candidate)
            complete = False
            break
    return {
        "candidates": list(reversed(chain)),
        "changed_files": sorted(set(changed_files)),
        "complete": complete,
    }


def parse_sources(text: str) -> List[str]:
    sources = []
    for match in SOURCE_RE.findall(text or ""):
        for item in re.split(r"\s*;\s*", match):
            if item and item not in sources:
                sources.append(item)
    for identifier in ARXIV_RE.findall(text or ""):
        source = f"arXiv:{identifier}"
        if not any(identifier in existing for existing in sources):
            sources.append(source)
    return sources


def is_literature_event(record: RunRecord) -> bool:
    return record.status == "literature"


def inferred_candidate_kind(record: RunRecord) -> str:
    if record.status == "baseline":
        return "baseline"
    if record.candidate_kind:
        return record.candidate_kind
    changed = record.changed_files.strip().lower()
    return "source_edit" if changed and changed != "none" else "argument_only"


def campaign_local_manifest_path(campaign_root: Path, candidate_id: str) -> Optional[Path]:
    candidate_id = candidate_id.strip()
    if (
        not candidate_id
        or candidate_id in {".", ".."}
        or Path(candidate_id).name != candidate_id
        or "/" in candidate_id
        or "\\" in candidate_id
    ):
        return None
    return campaign_root / ".nvflare" / "autofl" / "candidates" / candidate_id / "candidate_manifest.json"


def manifest_summary(record: Optional[RunRecord], campaign_root: Path) -> Dict[str, Any]:
    if record is None or not record.candidate_manifest:
        return {}
    recorded_path = record.candidate_manifest
    path = resolve_path(campaign_root, recorded_path)
    resolution = "recorded"
    if not path.is_file():
        local_path = campaign_local_manifest_path(campaign_root, record.name)
        if local_path is None:
            return {
                "path": str(path),
                "recorded_path": recorded_path,
                "resolved_path": None,
                "resolution": "unavailable",
                "available": False,
                "error": f"candidate name is not a safe manifest identifier: {record.name!r}",
            }
        if not local_path.is_file():
            return {
                "path": str(path),
                "recorded_path": recorded_path,
                "resolved_path": None,
                "resolution": "unavailable",
                "available": False,
            }
        path = local_path
        resolution = "campaign_local"

    resolved_path = str(path.resolve())
    provenance = {
        "path": resolved_path,
        "recorded_path": recorded_path,
        "resolved_path": resolved_path,
        "resolution": resolution,
    }
    try:
        manifest = load_json(path)
    except ValueError as exc:
        return {**provenance, "available": False, "error": str(exc)}
    manifest_candidate_id = str(manifest.get("candidate_id") or "")
    if manifest_candidate_id != record.name:
        return {
            **provenance,
            "available": False,
            "error": (f"candidate manifest ID mismatch: expected {record.name!r}, " f"found {manifest_candidate_id!r}"),
        }
    return {
        **provenance,
        "available": True,
        "schema_version": manifest.get("schema_version"),
        "candidate_id": manifest_candidate_id,
        "base_candidate": manifest.get("base_candidate"),
        "hypothesis": manifest.get("hypothesis"),
        "run_args": manifest.get("run_args") or [],
        "changed_files": manifest.get("changed_files") or [],
        "created_files": manifest.get("created_files") or [],
        "candidate_kind": manifest.get("candidate_kind"),
        "algorithm_family": manifest.get("algorithm_family"),
        "literature_event_id": manifest.get("literature_event_id"),
        "source_sha256": manifest.get("candidate_source_sha256") or manifest.get("base_source_sha256"),
        "budget_sha256": manifest.get("fixed_budget_sha256") or manifest.get("budget_sha256"),
        "patch_sha256": manifest.get("patch_sha256"),
        "status": manifest.get("status"),
        "artifacts": manifest.get("artifacts") or {},
        "result": manifest.get("result") or {},
    }


def validated_exploration_batch(exploration_batch: Any) -> Optional[Tuple[str, int, int]]:
    if not isinstance(exploration_batch, dict):
        return None
    event_id = exploration_batch.get("literature_event_id")
    completed = exploration_batch.get("completed")
    required = exploration_batch.get("required")
    if (
        not isinstance(event_id, str)
        or not event_id
        or not isinstance(completed, int)
        or isinstance(completed, bool)
        or not isinstance(required, int)
        or isinstance(required, bool)
        or completed < 0
        or required <= 0
    ):
        return None
    return event_id, completed, required


def literature_outcomes(
    records: Sequence[RunRecord],
    exploration_batch: Optional[Dict[str, Any]] = None,
    termination_reason: Optional[str] = None,
    mode: str = "max",
) -> List[Dict[str, Any]]:
    events = []
    for index, record in enumerate(records):
        if is_literature_event(record):
            events.append((index, record, record.literature_event_id))
    outcomes = []
    validated_batch = validated_exploration_batch(exploration_batch)
    for start, event, event_id in events:
        before = select_best(records[:start], retained_only=True, mode=mode)
        attempts = [
            record
            for index, record in enumerate(records)
            if record.status in ATTEMPT_STATUSES
            and not is_baseline(record)
            and index > start
            and bool(event_id)
            and record.literature_event_id == event_id
        ]
        scored = [record for record in attempts if is_finalized_scored_record(record)]
        segment_best = select_best(scored, mode=mode)
        completion = None
        if validated_batch and bool(event_id) and validated_batch[0] == event_id:
            _, completed, required = validated_batch
            completion = {
                "completed": completed,
                "required": required,
                "complete": completed >= required,
                "reason": None if completed >= required else termination_reason,
            }
        if completion and not completion["complete"]:
            outcome = "incomplete"
            delta = None if segment_best is None or before is None else segment_best.score - before.score
        elif segment_best is None:
            outcome = "failed" if attempts else "not_evaluated"
            delta = None
        elif before is None or better(segment_best.score, before.score, mode):
            outcome = "helped"
            delta = None if before is None else segment_best.score - before.score
        elif segment_best.score == before.score:
            outcome = "matched"
            delta = 0.0
        else:
            outcome = "not_confirmed"
            delta = segment_best.score - before.score
        outcomes.append(
            {
                "event": event.name,
                "literature_event_id": event_id,
                "row": event.index + 1,
                "hypothesis": event.diff_summary,
                "sources": parse_sources(event.diff_summary),
                "outcome": outcome,
                "incumbent_score": None if before is None else before.score,
                "best_candidate": None if segment_best is None else segment_best.name,
                "best_score": None if segment_best is None else segment_best.score,
                "delta_from_incumbent": delta,
                "improvement_from_incumbent": (
                    None
                    if before is None or segment_best is None
                    else improvement_amount(segment_best.score, before.score, mode)
                ),
                "candidate_attempts": [record.name for record in attempts],
                "completion": completion,
                "candidate_results": [
                    {
                        "name": record.name,
                        "status": record.status,
                        "score": record.score,
                        "candidate_kind": inferred_candidate_kind(record),
                        "algorithm_family": record.algorithm_family,
                        "metric_name": record.metric_name,
                        "metric_source": record.metric_source,
                        "metric_artifact": record.metric_artifact,
                    }
                    for record in attempts
                ],
                "failures": [record.name for record in attempts if record.status == "crash"],
            }
        )
    return outcomes


def literature_completion_warnings(
    reviews: Sequence[Dict[str, Any]],
    exploration_batch: Optional[Dict[str, Any]] = None,
    termination_reason: Optional[str] = None,
) -> List[str]:
    warnings = []
    for review in reviews:
        completion = review.get("completion")
        if not isinstance(completion, dict) or completion.get("complete") is not False:
            continue
        reason = completion.get("reason") or "campaign termination"
        warnings.append(
            f"Literature exploration batch {review['literature_event_id'] or review['event']} was incomplete at "
            f"{reason}: {completion['completed']} of {completion['required']} required candidates completed."
        )
    validated_batch = validated_exploration_batch(exploration_batch)
    review_event_ids = {review.get("literature_event_id") for review in reviews}
    if validated_batch and validated_batch[1] < validated_batch[2] and validated_batch[0] not in review_event_ids:
        event_id, completed, required = validated_batch
        reason = termination_reason or "campaign termination"
        warnings.append(
            f"Literature exploration batch {event_id} was incomplete at {reason}: {completed} of {required} required "
            "candidates completed, but no matching literature checkpoint was found in the results ledger."
        )
    return warnings


def candidate_outcome_payload(record: RunRecord, incumbent: Optional[RunRecord], mode: str = "max") -> Dict[str, Any]:
    delta = None if incumbent is None or record.score is None else record.score - incumbent.score
    return {
        "row": record.index + 1,
        "name": record.name,
        "status": record.status,
        "score": record.score,
        "incumbent_name": None if incumbent is None else incumbent.name,
        "incumbent_score": None if incumbent is None else incumbent.score,
        "delta_from_incumbent": delta,
        "improvement_from_incumbent": (
            None
            if incumbent is None or record.score is None
            else improvement_amount(record.score, incumbent.score, mode)
        ),
        "hypothesis": record.diff_summary,
        "changed_files": split_files(record.changed_files),
        "candidate_kind": inferred_candidate_kind(record),
        "algorithm_family": record.algorithm_family or "unclassified",
        "literature_event_id": record.literature_event_id,
        "failure_reason": record.failure_reason,
    }


def select_major_improvements(items: Sequence[Dict[str, Any]], limit: int) -> List[Dict[str, Any]]:
    if limit <= 0 or len(items) <= limit:
        return list(items)
    if limit == 1:
        return [items[-1]]
    selected = {0, len(items) - 1}
    middle = sorted(
        range(1, len(items) - 1),
        key=lambda index: (items[index]["improvement_from_incumbent"] or 0.0, -items[index]["row"]),
        reverse=True,
    )
    selected.update(middle[: max(0, limit - 2)])
    return [items[index] for index in sorted(selected)]


def representative_non_improvements(items: Sequence[Dict[str, Any]], limit: int) -> List[Dict[str, Any]]:
    representatives: Dict[Tuple[str, ...], Dict[str, Any]] = {}
    for item in items:
        family = item["algorithm_family"]
        event_id = item["literature_event_id"]
        if family != "unclassified" and event_id:
            key = ("family_literature", family, event_id)
            label = f"family={family}, literature={event_id}"
        elif family != "unclassified":
            key = ("family", family)
            label = f"family={family}"
        elif event_id:
            key = ("literature", event_id)
            label = f"literature={event_id}"
        else:
            key = ("candidate", item["name"])
            label = f"candidate={item['name']}"
        candidate = dict(item)
        candidate["representative_for"] = label
        previous = representatives.get(key)
        candidate_rank = candidate["improvement_from_incumbent"]
        previous_rank = None if previous is None else previous["improvement_from_incumbent"]
        if previous is None or (
            candidate_rank is not None and (previous_rank is None or candidate_rank > previous_rank)
        ):
            representatives[key] = candidate
    ranked = sorted(
        representatives.values(),
        key=lambda item: (
            item["improvement_from_incumbent"] is not None,
            (item["improvement_from_incumbent"] if item["improvement_from_incumbent"] is not None else -math.inf),
            -item["row"],
        ),
        reverse=True,
    )
    return ranked[: max(0, limit)]


def failure_outcomes(items: Sequence[Dict[str, Any]], limit: int) -> List[Dict[str, Any]]:
    groups: Dict[str, List[Dict[str, Any]]] = {}
    for item in items:
        reason = " ".join((item["failure_reason"] or "no score or failure reason recorded").split())
        groups.setdefault(reason, []).append(item)
    outcomes = []
    for reason, failures in groups.items():
        outcomes.append(
            {
                "reason": reason,
                "count": len(failures),
                "candidates": [item["name"] for item in failures],
                "statuses": sorted({item["status"] for item in failures}),
                "algorithm_families": sorted({item["algorithm_family"] for item in failures}),
                "candidate_kinds": sorted({item["candidate_kind"] for item in failures}),
            }
        )
    outcomes.sort(key=lambda item: (-item["count"], item["reason"]))
    return outcomes[: max(0, limit)]


def family_outcomes(records: Sequence[RunRecord], helped_rows: set[int], mode: str = "max") -> List[Dict[str, Any]]:
    families: Dict[str, List[RunRecord]] = {}
    for record in records:
        if record.status not in {"keep", "discard", "crash"}:
            continue
        families.setdefault(record.algorithm_family or "unclassified", []).append(record)
    outcomes = []
    for family, attempts in families.items():
        counts = Counter(record.status for record in attempts)
        helped = [record for record in attempts if record.index in helped_rows]
        scored_discards = [record for record in attempts if record.status == "discard" and record.score is not None]
        scored_unhelpful_keeps = [
            record
            for record in attempts
            if record.status == "keep" and record.score is not None and record.index not in helped_rows
        ]
        unscored_keeps = [record for record in attempts if record.status == "keep" and record.score is None]
        if helped and (counts.get("discard", 0) or counts.get("crash", 0) or unscored_keeps):
            outcome = "mixed"
        elif helped:
            outcome = "helped"
        elif scored_discards or scored_unhelpful_keeps:
            outcome = "not_confirmed"
        else:
            outcome = "failed"
        retained = select_best(
            [record for record in attempts if record.status == "keep"], retained_only=True, mode=mode
        )
        observed = select_best(attempts, mode=mode)
        outcomes.append(
            {
                "algorithm_family": family,
                "outcome": outcome,
                "attempts": len(attempts),
                "kept": counts.get("keep", 0),
                "discarded": counts.get("discard", 0),
                "crashed": counts.get("crash", 0),
                "best_retained_score": None if retained is None else retained.score,
                "best_observed_score": None if observed is None else observed.score,
                "candidate_kinds": sorted({inferred_candidate_kind(record) for record in attempts}),
                "literature_event_ids": sorted(
                    {record.literature_event_id for record in attempts if record.literature_event_id}
                ),
            }
        )
    order = {"helped": 0, "mixed": 1, "not_confirmed": 2, "failed": 3}
    return sorted(outcomes, key=lambda item: (order[item["outcome"]], item["algorithm_family"]))


def literature_digest(reviews: Sequence[Dict[str, Any]]) -> Dict[str, Any]:
    counts = dict(sorted(Counter(review["outcome"] for review in reviews).items()))
    helped = [
        review
        for review in reviews
        if review["outcome"] == "helped" and review["improvement_from_incumbent"] is not None
    ]
    strongest = None
    if helped:
        strongest = max(
            helped,
            key=lambda review: review["improvement_from_incumbent"],
        )
        strongest = {
            "event": strongest["event"],
            "literature_event_id": strongest["literature_event_id"],
            "best_candidate": strongest["best_candidate"],
            "best_score": strongest["best_score"],
            "incumbent_score": strongest["incumbent_score"],
            "delta_from_incumbent": strongest["delta_from_incumbent"],
            "improvement_from_incumbent": strongest["improvement_from_incumbent"],
            "sources": strongest["sources"],
        }
    return {"counts": counts, "strongest_helped": strongest}


def outcome_summary(
    records: Sequence[RunRecord],
    reviews: Sequence[Dict[str, Any]],
    max_milestones: int,
    max_non_improvements: int,
    mode: str = "max",
) -> Dict[str, Any]:
    incumbent = None
    helped = []
    non_improvements = []
    failures = []
    for record in records:
        if record.status in RETAINED_STATUSES and record.score is not None:
            if record.status == "keep" and incumbent is not None and better(record.score, incumbent.score, mode):
                helped.append(candidate_outcome_payload(record, incumbent, mode))
            if incumbent is None or better(record.score, incumbent.score, mode):
                incumbent = record
            continue
        if record.status == "discard":
            payload = candidate_outcome_payload(record, incumbent, mode)
            (non_improvements if record.score is not None else failures).append(payload)
        elif record.status == "crash":
            failures.append(candidate_outcome_payload(record, incumbent, mode))
    helped_rows = {item["row"] - 1 for item in helped}
    return {
        "helped": helped,
        "major_helped": select_major_improvements(helped, max_milestones),
        "not_confirmed": representative_non_improvements(non_improvements, max_non_improvements),
        "failures": failure_outcomes(failures, max_non_improvements),
        "families": family_outcomes(records, helped_rows, mode),
        "literature": literature_digest(reviews),
    }


def selection_summary(
    best: Optional[RunRecord], baseline: Optional[RunRecord], lineage: Dict[str, Any], mode: str = "max"
) -> Optional[Dict[str, Any]]:
    if best is None:
        return None
    delta = None if baseline is None else best.score - baseline.score
    improvement = None if baseline is None else improvement_amount(best.score, baseline.score, mode)
    return {
        "candidate": best.name,
        "score": best.score,
        "delta_from_baseline": delta,
        "improvement_from_baseline": improvement,
        "algorithm_family": best.algorithm_family or ("baseline" if best.status == "baseline" else "unclassified"),
        "candidate_kind": "baseline" if best.status == "baseline" else inferred_candidate_kind(best),
        "hypothesis": best.diff_summary,
        "base_candidate": best.base_candidate,
        "literature_event_id": best.literature_event_id,
        "lineage": lineage,
        "reason": (
            f"It is the best scored retained result under the {mode} objective."
            if best.status != "baseline"
            else "No retained candidate improved on the scored baseline."
        ),
    }


def command_options(command: str) -> Dict[str, Any]:
    if not command:
        return {}
    try:
        tokens = shlex.split(command)
    except ValueError:
        return {}
    options: Dict[str, Any] = {}
    index = 0
    while index < len(tokens):
        token = tokens[index]
        if not token.startswith("--"):
            index += 1
            continue
        key_value = token[2:].split("=", 1)
        key = key_value[0].replace("-", "_")
        if len(key_value) == 2:
            options[key] = key_value[1]
        elif index + 1 < len(tokens) and not tokens[index + 1].startswith("--"):
            options[key] = tokens[index + 1]
            index += 1
        else:
            options[key] = True
        index += 1
    return options


def command_changes(baseline: Optional[RunRecord], best: Optional[RunRecord]) -> Dict[str, Dict[str, Any]]:
    if baseline is None or best is None:
        return {}
    base = command_options(baseline.run_command)
    candidate = command_options(best.run_command)
    changes = {}
    for key in sorted(set(base) | set(candidate)):
        if key == "name" or base.get(key) == candidate.get(key):
            continue
        changes[key] = {"baseline": base.get(key), "best": candidate.get(key)}
    return changes


def values_equal(left: Any, right: Any) -> bool:
    if str(left) == str(right):
        return True
    left_number = finite_float(left)
    right_number = finite_float(right)
    return left_number is not None and right_number is not None and math.isclose(left_number, right_number)


def comparability_warnings(
    config: Dict[str, Any],
    records: Sequence[RunRecord],
    baseline: Optional[RunRecord],
    best: Optional[RunRecord],
    metric: str,
    metric_source: str,
) -> Tuple[List[str], Dict[str, Dict[str, Any]]]:
    warnings = []
    changes = command_changes(baseline, best)
    changed_budget = sorted(set(changes).intersection(TRAINING_BUDGET_ARGS))
    if changed_budget:
        warnings.append(
            "The best run changed executed training/comparison arguments relative to baseline: "
            + ", ".join(changed_budget)
            + ". Treat the gain as non-equal-compute unless this was explicitly approved."
        )

    budget = config.get("budget") if isinstance(config.get("budget"), dict) else {}
    fixed = budget.get("fixed_training_budget", {})
    baseline_options = command_options(baseline.run_command) if baseline else {}
    fixed_mismatches = []
    if isinstance(fixed, dict):
        for config_key, expected in fixed.items():
            cli_key = FIXED_BUDGET_TO_CLI.get(config_key, config_key)
            actual = baseline_options.get(cli_key)
            if actual is not None and not values_equal(actual, expected):
                fixed_mismatches.append(f"{config_key}: autofl.yaml={expected}, executed={actual}")
    if fixed_mismatches:
        warnings.append(
            "The imported fixed budget differs from the executed baseline command ("
            + "; ".join(fixed_mismatches)
            + ")."
        )

    scored_candidate_count = sum(
        record.status in FINALIZED_SCORE_STATUSES and record.status != "baseline" and record.score is not None
        for record in records
    )
    metric_text = f"{metric} {metric_source}".lower()
    if scored_candidate_count > 1 and "test" in metric_text:
        warnings.append(
            "Multiple candidates were selected against a test-like metric. Re-evaluate the chosen candidate once on an "
            "untouched holdout before making generalization claims."
        )
    if not config:
        warnings.append(
            "autofl.yaml was unavailable, so the declared objective and fixed-budget contract could not be verified."
        )
    return warnings, changes


def state_accounting(
    state: Dict[str, Any],
    candidate_attempts: int,
    baseline: Optional[RunRecord],
    best: Optional[RunRecord],
    results_path: Path,
    campaign_root: Path,
    mode: str = "max",
) -> Tuple[Dict[str, Any], List[str]]:
    ledger_baseline = None if baseline is None else baseline.score
    ledger_improvement = (
        None if baseline is None or best is None else improvement_amount(best.score, baseline.score, mode)
    )
    ledger_values = {
        "candidate_attempts": candidate_attempts,
        "baseline_score": ledger_baseline,
        "improvement": ledger_improvement,
    }
    state_values = {key: state.get(key) for key in ledger_values}
    state_values["abandoned_candidates"] = state.get("abandoned_candidates")
    state_values["results"] = state.get("results")
    warnings = []

    if "results" in state:
        state_results = state.get("results")
        if not isinstance(state_results, str) or not state_results.strip():
            warnings.append(f"Campaign state results is invalid: {state_results!r}.")
        else:
            try:
                state_results_path = canonical_path(resolve_path(campaign_root, state_results))
            except ValueError as exc:
                warnings.append(f"Campaign state results is invalid: {state_results!r} ({exc}).")
            else:
                parsed_results_path = canonical_path(results_path)
                if state_results_path != parsed_results_path:
                    warnings.append(
                        f"Campaign state names ledger {state_results_path} but the report was generated from "
                        f"{parsed_results_path}."
                    )

    if "candidate_attempts" in state:
        state_attempts = finite_float(state.get("candidate_attempts"))
        if state_attempts is None or not state_attempts.is_integer() or int(state_attempts) != candidate_attempts:
            warnings.append(
                "Campaign state candidate_attempts disagrees with the ledger "
                f"(state={state.get('candidate_attempts')!r}, ledger={candidate_attempts})."
            )

    for field, ledger_value in (("baseline_score", ledger_baseline), ("improvement", ledger_improvement)):
        if field not in state:
            continue
        state_value = finite_float(state.get(field))
        agrees = (
            state_value is None
            and ledger_value is None
            or state_value is not None
            and ledger_value is not None
            and math.isclose(state_value, ledger_value, rel_tol=1e-9, abs_tol=1e-12)
        )
        if not agrees:
            warnings.append(
                f"Campaign state {field} disagrees with the ledger "
                f"(state={state.get(field)!r}, ledger={ledger_value!r})."
            )

    abandoned_candidates = state.get("abandoned_candidates")
    if abandoned_candidates is not None:
        parsed_abandoned = finite_float(abandoned_candidates)
        if parsed_abandoned is None or not parsed_abandoned.is_integer() or parsed_abandoned < 0:
            warnings.append(
                f"Campaign state abandoned_candidates is invalid: {abandoned_candidates!r}; it was not reported."
            )
            abandoned_candidates = None
        else:
            abandoned_candidates = int(parsed_abandoned)

    return {
        "consistent": not warnings,
        "ledger": ledger_values,
        "campaign_state": state_values,
        "abandoned_candidates": abandoned_candidates,
    }, warnings


def default_plotter_path() -> Path:
    return product_autofl_script_path("plot_progress.py")


def refresh_plot(results: Path, output: Path, metric: str, plotter_path: Path, mode: str = "max") -> Optional[str]:
    if not plotter_path.is_file():
        return f"Auto-FL progress plotter not found at {plotter_path}; existing plot was preserved."
    spec = importlib.util.spec_from_file_location("nvflare_autofl_report_plotter", plotter_path)
    if spec is None or spec.loader is None:
        return f"Could not load Auto-FL progress plotter from {plotter_path}; existing plot was preserved."
    module = importlib.util.module_from_spec(spec)
    previous_module = sys.modules.get(spec.name)
    sys.modules[spec.name] = module
    try:
        spec.loader.exec_module(module)
        parameters = inspect.signature(module.plot_progress).parameters
        accepts_mode = "mode" in parameters or any(
            parameter.kind == inspect.Parameter.VAR_KEYWORD for parameter in parameters.values()
        )
        if mode == "min" and not accepts_mode:
            return (
                f"Progress plotter at {plotter_path} cannot represent mode='min'; existing plot was preserved. "
                "Use the current nvflare-autofl plotter to refresh this campaign."
            )
        kwargs = {"metric_label": metric}
        if accepts_mode:
            kwargs["mode"] = mode
        module.plot_progress(module.load_results(results), output, **kwargs)
    except Exception as exc:  # plotting should not destroy an otherwise useful stopped-campaign report
        return f"Could not refresh progress plot ({type(exc).__name__}: {exc}); existing plot was preserved."
    finally:
        if previous_module is None:
            sys.modules.pop(spec.name, None)
        else:
            sys.modules[spec.name] = previous_module
    return None


def is_png(path: Path) -> bool:
    try:
        with path.open("rb") as f:
            return f.read(len(PNG_SIGNATURE)) == PNG_SIGNATURE
    except OSError:
        return False


def compact_lineage(lineage: Sequence[str], edge_items: int = 4) -> str:
    if len(lineage) <= edge_items * 2 + 1:
        return " -> ".join(lineage)
    hidden = len(lineage) - edge_items * 2
    return " -> ".join([*lineage[:edge_items], f"... ({hidden} intermediate)", *lineage[-edge_items:]])


def read_agent_context(path: Optional[Path], args: argparse.Namespace) -> Dict[str, Any]:
    context = {}
    if path:
        if not path.is_file():
            raise ValueError(f"agent context file not found: {path}")
        text = path.read_text(encoding="utf-8").strip()
        try:
            parsed = json.loads(text)
        except json.JSONDecodeError:
            context["notes"] = text
        else:
            context.update(parsed if isinstance(parsed, dict) else {"notes": parsed})
    if args.agent_model:
        context["model"] = args.agent_model
    if args.reasoning_effort:
        context["reasoning_effort"] = args.reasoning_effort
    if args.agent_cost:
        context["cost"] = args.agent_cost
    return context


def atomic_write_text(path: Path, text: str) -> None:
    path.parent.mkdir(parents=True, exist_ok=True)
    temp_path = None
    try:
        with tempfile.NamedTemporaryFile(
            "w", encoding="utf-8", dir=path.parent, prefix=f".{path.name}.", delete=False
        ) as f:
            temp_path = Path(f.name)
            f.write(text)
        os.replace(temp_path, path)
    finally:
        if temp_path and temp_path.exists():
            temp_path.unlink()


def md_cell(value: Any, limit: int = 180) -> str:
    text = " ".join(str(value or "").split()).replace("|", "\\|")
    return text if len(text) <= limit else text[: limit - 3] + "..."


def markdown_artifact_path(artifact: str, report: str) -> str:
    try:
        path = Path(os.path.relpath(artifact, start=Path(report).parent)).as_posix()
    except ValueError:
        path = Path(artifact).as_posix()
    return quote(path, safe="/:")


def literature_outcome_label(review: Dict[str, Any]) -> str:
    completion = review.get("completion")
    if isinstance(completion, dict) and completion.get("complete") is False:
        label = f"incomplete ({completion['completed']}/{completion['required']}"
        if completion.get("reason"):
            label += f"; {md_cell(completion['reason'], 60)}"
        return label + ")"
    return str(review["outcome"])


def format_score(value: Optional[float]) -> str:
    return "n/a" if value is None else f"{value:.6f}"


def format_delta(value: Optional[float]) -> str:
    return "n/a" if value is None else f"{value:+.6f}"


def format_runtime(seconds: float) -> str:
    if seconds <= 0:
        return ""
    if seconds < 3600:
        return f"{seconds / 60:.1f}m"
    return f"{seconds / 3600:.2f}h"


def format_command(command: str) -> str:
    try:
        tokens = shlex.split(command)
    except ValueError:
        return command or "not recorded"
    if not tokens:
        return "not recorded"
    chunks = []
    current = []
    index = 0
    while index < len(tokens):
        token = tokens[index]
        if token.startswith("--") and current:
            chunks.append(current)
            current = []
        current.append(token)
        if token.startswith("--") and index + 1 < len(tokens) and not tokens[index + 1].startswith("--"):
            index += 1
            current.append(tokens[index])
        index += 1
    if current:
        chunks.append(current)
    return " \\\n  ".join(shlex.join(chunk) for chunk in chunks)


def wrap_markdown_bullet(prefix: str, text: str, width: int = 120) -> List[str]:
    return textwrap.wrap(
        f"{prefix}{' '.join(text.split())}",
        width=width,
        subsequent_indent="  ",
        break_long_words=False,
        break_on_hyphens=False,
    )


def report_markdown(summary: Dict[str, Any], records: Sequence[RunRecord]) -> str:
    baseline = summary["baseline"]
    best = summary["best"]
    counts = summary["status_counts"]
    runtime_label = format_runtime(summary["runtime_seconds"]) or "n/a"
    lines = [
        "# Auto-FL Final Report",
        "",
        "## Executive Summary",
        "",
        f"The stopped campaign optimized `{summary['objective']['optimization_metric']}` with "
        f"`{summary['objective']['mode']}` direction in the `{summary['environment']}` environment. "
        f"It evaluated `{summary['candidate_attempts']}` candidate attempts "
        f"across `{len(records)}` ledger rows in `{runtime_label}`.",
        "",
    ]
    if best:
        baseline_score = baseline["score"] if baseline else None
        improvement = (
            None
            if baseline_score is None
            else improvement_amount(best["score"], baseline_score, summary["objective"]["mode"])
        )
        lines.append(
            f"Best retained candidate: `{best['name']}` at "
            f"`{summary['objective']['optimization_metric']}={format_score(best['score'])}` "
            f"(baseline `{format_score(baseline_score)}`, objective improvement `{format_delta(improvement)}`)."
        )
    else:
        observed = summary["best_observed"]
        if observed:
            lines.append(
                "No scored result was retained. Best unretained observed result: "
                f"`{observed['name']}` at "
                f"`{summary['objective']['optimization_metric']}={format_score(observed['score'])}` "
                f"with status `{observed['status']}`."
            )
        else:
            lines.append("No finalized scored result was available.")
    progress_lines = (
        [
            "![Auto-FL progress]("
            f"{markdown_artifact_path(summary['artifacts']['progress_plot'], summary['artifacts']['report'])})"
        ]
        if summary["artifacts"]["progress_plot_available"]
        else ["Progress plot unavailable; see the validation and comparability warnings below."]
    )
    selection = summary["selection"]
    outcomes = summary["outcome_summary"]
    lines.extend(
        [
            "",
            f"Termination: `{summary['termination']['reason']}`. "
            f"Final campaign state allowed: `{summary['termination']['state_allowed_final_response']}`.",
            "",
            "## Selected Candidate And Why",
            "",
        ]
    )
    if selection:
        lines.append(
            f"Select `{selection['candidate']}` at `{summary['objective']['optimization_metric']}="
            f"{format_score(selection['score'])}`. {selection['reason']}"
        )
        lines.extend(
            [
                "",
                f"- Objective improvement from baseline: `{format_delta(selection['improvement_from_baseline'])}`",
                f"- Recorded algorithm family: `{selection['algorithm_family']}`",
                f"- Candidate kind: `{selection['candidate_kind']}`",
                f"- Hypothesis: {md_cell(selection['hypothesis'], 300) or 'not recorded'}",
                f"- Retained lineage: `{compact_lineage(selection['lineage']['candidates']) or 'unavailable'}`",
                f"- Cumulative changed files: `{', '.join(selection['lineage']['changed_files']) or 'none recorded'}`",
            ]
        )
    else:
        lines.append("No retained scored result was available for selection.")

    lines.extend(
        [
            "",
            "## What Helped",
            "",
            "The following candidates made strict measured improvements over the retained incumbent and were kept:",
            "",
        ]
    )
    if outcomes["major_helped"]:
        lines.extend(
            [
                "| Candidate | Family | Kind | Score | Objective improvement vs incumbent | Evidence |",
                "| --- | --- | --- | ---: | ---: | --- |",
            ]
        )
        for item in outcomes["major_helped"]:
            lines.append(
                f"| `{md_cell(item['name'], 50)}` | `{md_cell(item['algorithm_family'], 40)}` | "
                f"`{md_cell(item['candidate_kind'], 30)}` | {format_score(item['score'])} | "
                f"{format_delta(item['improvement_from_incumbent'])} | {md_cell(item['hypothesis'])} |"
            )
    else:
        lines.append("No candidate produced a strict retained improvement over the baseline.")

    lines.extend(["", "## What Did Not Help", ""])
    if outcomes["not_confirmed"]:
        lines.extend(
            [
                "Representative scored candidates that did not produce a retained improvement:",
                "",
                "| Candidate | Represents | Score | Delta vs incumbent | Evidence |",
                "| --- | --- | ---: | ---: | --- |",
            ]
        )
        for item in outcomes["not_confirmed"]:
            lines.append(
                f"| `{md_cell(item['name'], 50)}` | `{md_cell(item['representative_for'], 70)}` | "
                f"{format_score(item['score'])} | {format_delta(item['delta_from_incumbent'])} | "
                f"{md_cell(item['hypothesis'])} |"
            )
    else:
        lines.append("No scored discarded candidates were recorded.")
    if outcomes["failures"]:
        lines.extend(["", "Grouped failed or unscored attempts:", ""])
        for item in outcomes["failures"]:
            candidates = ", ".join(item["candidates"])
            lines.extend(
                wrap_markdown_bullet(
                    f"- **{item['count']} attempt(s):** ",
                    f"{item['reason']} Candidates: `{md_cell(candidates, 240)}`.",
                )
            )

    if outcomes["families"]:
        lines.extend(
            [
                "",
                "Outcome by recorded algorithm family (`unclassified` means the ledger did not declare one):",
                "",
                "| Family | Outcome | Attempts | Kept | Discarded | Crashed |",
                "| --- | --- | ---: | ---: | ---: | ---: |",
            ]
        )
        for item in outcomes["families"]:
            lines.append(
                f"| `{md_cell(item['algorithm_family'], 50)}` | {item['outcome']} | {item['attempts']} | "
                f"{item['kept']} | {item['discarded']} | {item['crashed']} |"
            )

    lines.extend(
        [
            "",
            "## Campaign Contract",
            "",
            f"- Requested metric: `{summary['objective']['requested_metric']}`",
            f"- Optimization metric: `{summary['objective']['optimization_metric']}`",
            f"- Metric source: `{summary['objective']['metric_source']}`",
            f"- Metric contract source: `{summary['objective']['metric_contract_source']}`",
            f"- Direction: `{summary['objective']['mode']}`",
            f"- Candidate cap: `{summary['candidate_cap']}`",
            f"- Declared fixed budget: `{json.dumps(summary['declared_fixed_budget'], sort_keys=True)}`",
            "",
            "## Progress",
            "",
            *progress_lines,
            "",
            "## Optimization Trajectory",
            "",
            "Major running-best milestones are selected by first, final, and largest measured objective improvements.",
            "",
            "| Row | Candidate | Status | Family | Kind | Score | Delta from previous best | Hypothesis |",
            "| ---: | --- | --- | --- | --- | ---: | ---: | --- |",
        ]
    )
    for item in summary["milestones"]:
        lines.append(
            f"| {item['row']} | `{md_cell(item['name'], 50)}` | {item['status']} | "
            f"`{md_cell(item['algorithm_family'], 40)}` | `{md_cell(item['candidate_kind'], 30)}` | "
            f"{format_score(item['score'])} | {format_delta(item['delta_from_previous_best'])} | "
            f"{md_cell(item['hypothesis'])} |"
        )
    if not summary["milestones"]:
        lines.append("|  |  |  |  |  |  |  | No scored milestones. |")

    lines.extend(["", "## Best Candidate Provenance", ""])
    if best:
        lines.extend(
            [
                f"- Candidate: `{best['name']}`",
                f"- Status: `{best['status']}`",
                f"- Base lineage: `{compact_lineage(summary['best_lineage']['candidates']) or 'unavailable'}`",
                f"- Cumulative changed files: `{', '.join(summary['best_lineage']['changed_files']) or 'none recorded'}`",
                f"- Candidate manifest (recorded): `{best['candidate_manifest'] or 'not recorded'}`",
                f"- Candidate manifest (resolved): "
                f"`{summary['best_manifest'].get('resolved_path') or 'not available'}`",
                f"- Manifest resolution: `{summary['best_manifest'].get('resolution') or 'not available'}`",
                f"- Manifest available: `{summary['best_manifest'].get('available', False)}`",
                f"- Manifest budget SHA-256: `{summary['best_manifest'].get('budget_sha256') or 'not recorded'}`",
                f"- Patch SHA-256: `{best['patch_sha256'] or 'not recorded'}`",
                f"- Candidate kind: `{best['candidate_kind'] or 'not recorded'}`",
                f"- Algorithm family: `{best['algorithm_family'] or 'not recorded'}`",
                f"- Literature event: `{best['literature_event_id'] or 'not linked'}`",
                f"- Recorded metric: `{best['metric_name'] or summary['objective']['optimization_metric']}`",
                f"- Metric extraction source: `{best['metric_source'] or 'not recorded'}`",
                f"- Metric artifact: `{best['metric_artifact'] or 'not recorded'}`",
                f"- Artifacts: `{best['artifacts'] or 'not recorded'}`",
                "",
                "Executed baseline command:",
                "",
                "```text",
                format_command(baseline["run_command"]) if baseline else "not recorded",
                "```",
                "",
                "Executed best-candidate command:",
                "",
                "```text",
                format_command(best["run_command"]),
                "```",
            ]
        )
    else:
        lines.append("No retained scored candidate provenance was available.")

    lines.extend(["", "## Literature Review Outcomes", ""])
    if summary["literature_reviews"]:
        literature_digest = outcomes["literature"]
        lines.append(f"Outcome counts: `{json.dumps(literature_digest['counts'], sort_keys=True)}`.")
        strongest = literature_digest["strongest_helped"]
        if strongest:
            lines.append(
                f"Strongest literature-linked improvement: `{strongest['event']}` led to "
                f"`{strongest['best_candidate']}` with objective improvement "
                f"`{format_delta(strongest['improvement_from_incumbent'])}` versus its incumbent."
            )
        lines.extend(
            [
                "",
                "| Checkpoint | Sources | Outcome | Candidate evidence | Delta vs incumbent |",
                "| --- | --- | --- | --- | ---: |",
            ]
        )
        for item in summary["literature_reviews"]:
            evidence_items = []
            for candidate in item["candidate_results"]:
                if candidate["status"] == "crash":
                    result = "crash"
                else:
                    result = format_score(candidate["score"])
                evidence_items.append(f"{candidate['name']}={result}")
            evidence = "; ".join(evidence_items) or "no candidate recorded"
            outcome_label = literature_outcome_label(item)
            lines.append(
                f"| `{md_cell(item['event'], 45)}` (`{item['literature_event_id'] or 'not recorded'}`) | "
                f"{md_cell('; '.join(item['sources']) or 'not recorded', 100)} | "
                f"{outcome_label} | `{md_cell(evidence, 130)}` | {format_delta(item['delta_from_incumbent'])} |"
            )
        lines.extend(["", "Checkpoint hypotheses and decisions:", ""])
        for item in summary["literature_reviews"]:
            candidates = ", ".join(item["candidate_attempts"]) or "none recorded"
            outcome_label = literature_outcome_label(item)
            lines.extend(
                wrap_markdown_bullet(
                    f"- **{item['event']} ({outcome_label}):** ",
                    f"{item['hypothesis']} Measured candidates: `{md_cell(candidates, 300)}`.",
                )
            )
        lines.extend(
            [
                "",
                "Source labels above are campaign-recorded identifiers and were not independently verified by the report helper.",
            ]
        )
    else:
        lines.append("No literature checkpoints were recorded in the ledger.")

    lines.extend(
        [
            "",
            "## Runtime And Reliability",
            "",
            f"- Total recorded runtime: `{runtime_label}`",
            f"- Status counts: `{json.dumps(counts, sort_keys=True)}`",
            f"- Scored comparable runs: `{summary['scored_runs']}`",
            f"- Crashes: `{counts.get('crash', 0)}`",
            f"- Abandoned candidates: "
            f"`{summary['abandoned_candidates'] if summary['abandoned_candidates'] is not None else 'not recorded'}`",
        ]
    )

    lines.extend(["", "## Validation And Comparability Notes", ""])
    if summary["warnings"]:
        lines.extend(f"- {warning}" for warning in summary["warnings"])
    else:
        lines.append("- No deterministic comparability warning was detected from the available artifacts.")
    if summary["best_command_changes"]:
        lines.extend(["", "Executed argument changes from baseline to best:", ""])
        for key, value in summary["best_command_changes"].items():
            lines.append(f"- `{key}`: `{value['baseline']}` -> `{value['best']}`")

    lines.extend(["", "## Agent And Tooling Context", ""])
    if summary["agent_context"]:
        lines.append("```json")
        lines.append(json.dumps(summary["agent_context"], indent=2, sort_keys=True))
        lines.append("```")
    else:
        lines.append("Agent model, reasoning effort, and cost were not supplied to the report generator.")

    lines.extend(
        [
            "",
            "## Reproduction Recommendations",
            "",
            "1. Re-run the baseline and selected candidate from the exact commands above in the same NVFLARE environment.",
            "2. Confirm the candidate on additional seeds or sites before treating a single-run improvement as robust.",
            "3. When a test-like metric guided selection, perform one final evaluation on an untouched holdout.",
            "4. Preserve the candidate manifest, patch hash, ledger, campaign config, and downloaded NVFLARE artifacts.",
            "",
            "## Report Artifacts",
            "",
            f"- Auto-FL config: `{summary['artifacts']['autofl_yaml']}`",
            f"- Results ledger: `{summary['artifacts']['results']}`",
            f"- Campaign state: `{summary['artifacts']['campaign_state']}`",
            f"- Progress plot: `{summary['artifacts']['progress_plot']}`",
            f"- Progress plot available: `{summary['artifacts']['progress_plot_available']}`",
            f"- Machine-readable summary: `{summary['artifacts']['summary_json']}`",
            f"- Markdown report: `{summary['artifacts']['report']}`",
            "",
            "## Technical Appendix",
            "",
            f"Generated at `{summary['generated_at']}` with schema `{summary['schema_version']}`.",
        ]
    )
    return "\n".join(lines) + "\n"


def record_payload(record: Optional[RunRecord]) -> Optional[Dict[str, Any]]:
    return asdict(record) if record else None


def generate_locked(args: argparse.Namespace, root: Path) -> Dict[str, Any]:
    results_path = resolve_path(root, args.results)
    state_path = resolve_path(root, args.state)
    config_path = resolve_path(root, args.autofl_yaml)
    progress_path = resolve_path(root, args.progress)
    report_path = resolve_path(root, args.output)
    summary_path = resolve_path(root, args.summary_json)
    agent_context_path = resolve_path(root, args.agent_context) if args.agent_context else None
    plotter_path = resolve_path(root, args.plotter).resolve() if args.plotter else default_plotter_path()

    records = load_results(results_path)
    state, termination_reason, warnings = verify_stopped(state_path, args.confirm_interrupted)
    config = load_config(config_path)
    config, config_warnings = normalize_contract_sections(config)
    warnings.extend(config_warnings)
    protected_sources, allowed_create_patterns = campaign_source_contract(root, config)
    protected_paths = {
        "results ledger": results_path,
        "campaign state": state_path,
        "Auto-FL config": config_path,
        "campaign lock": root / CAMPAIGN_LOCK_PATH,
        "progress plotter": plotter_path,
    }
    protected_paths.update(protected_sources)
    if agent_context_path:
        protected_paths["agent context"] = agent_context_path
    for index, manifest_path in enumerate(candidate_manifest_paths(root, records), start=1):
        protected_paths[f"candidate manifest {index}"] = manifest_path
    pending_manifest = state.get("pending_candidate_manifest")
    if isinstance(pending_manifest, str) and pending_manifest:
        protected_paths["state pending-candidate manifest"] = resolve_path(root, pending_manifest)
    outputs = {
        "progress": progress_path,
        "output": report_path,
        "summary-json": summary_path,
    }
    validate_output_paths(outputs, protected_paths)
    validate_output_create_patterns(outputs, root, allowed_create_patterns)
    verify_no_pending_candidates(root, state, records)
    mode = metric_mode(config, state)
    metric, requested_metric, metric_source, metric_contract_source = metric_contract(config, state, args)
    baseline = select_baseline(records)
    best = select_best(records, retained_only=True, mode=mode)
    observed_best = select_best(records, mode=mode)
    if best is None and observed_best is not None:
        warnings.append(
            f"Best observed row {observed_best.name} was not retained; no scored baseline or kept candidate is available."
        )
    elif best and observed_best and best.name != observed_best.name:
        warnings.append(
            f"Best observed row {observed_best.name} was not retained; the report identifies retained candidate {best.name}."
        )

    plot_warning = refresh_plot(
        results_path,
        progress_path,
        metric,
        plotter_path,
        mode,
    )
    if plot_warning:
        warnings.append(plot_warning)
    progress_plot_available = is_png(progress_path)
    if not progress_plot_available:
        warnings.append(
            f"Progress plot at {progress_path} is missing or is not a valid PNG; the report was generated without "
            "embedding it."
        )

    comparison_warnings, changes = comparability_warnings(config, records, baseline, best, metric, metric_source)
    warnings.extend(comparison_warnings)
    status_counts = dict(sorted(Counter(record.status or "unknown" for record in records).items()))
    scored = scored_records(records)
    candidate_attempts = len(
        [record for record in records if record.status in ATTEMPT_STATUSES and not is_baseline(record)]
    )
    accounting, accounting_warnings = state_accounting(
        state,
        candidate_attempts,
        baseline,
        best,
        results_path,
        root,
        mode,
    )
    warnings.extend(accounting_warnings)
    environment = config.get("environment") if isinstance(config.get("environment"), dict) else {}
    budget = config.get("budget") if isinstance(config.get("budget"), dict) else {}
    fixed_budget = budget.get("fixed_training_budget", {})
    cap = state.get("candidate_cap")
    cap_label = "uncapped" if cap in {None, "", 0} else cap
    lineage = candidate_lineage(best, records)
    reviews = literature_outcomes(
        records,
        exploration_batch=state.get("exploration_batch"),
        termination_reason=termination_reason,
        mode=mode,
    )
    warnings.extend(
        literature_completion_warnings(
            reviews,
            exploration_batch=state.get("exploration_batch"),
            termination_reason=termination_reason,
        )
    )
    summary = {
        "schema_version": SUMMARY_SCHEMA_VERSION,
        "generated_at": datetime.now(timezone.utc).isoformat(),
        "termination": {
            "reason": termination_reason,
            "state_allowed_final_response": state.get("final_response_allowed") is True,
            "user_confirmed_interruption": bool(args.confirm_interrupted),
        },
        "objective": {
            "requested_metric": requested_metric,
            "optimization_metric": metric,
            "metric_source": metric_source,
            "metric_contract_source": metric_contract_source,
            "mode": mode,
            "mode_contract_source": str(config.get("objective", {}).get("mode_contract_source") or "not recorded"),
        },
        "environment": str(environment.get("requested") or state.get("environment") or "not declared"),
        "candidate_cap": cap_label,
        "candidate_cap_source": state.get("candidate_cap_source") or "not recorded",
        "abandoned_candidates": accounting["abandoned_candidates"],
        "declared_fixed_budget": fixed_budget if isinstance(fixed_budget, dict) else {},
        "candidate_attempts": candidate_attempts,
        "state_accounting": accounting,
        "scored_runs": len(scored),
        "runtime_seconds": sum(record.runtime_seconds for record in records),
        "status_counts": status_counts,
        "baseline": record_payload(baseline),
        "best": record_payload(best),
        "best_observed": record_payload(observed_best),
        "best_lineage": lineage,
        "best_manifest": manifest_summary(best, root),
        "best_command_changes": changes,
        "milestones": running_best_milestones(records, args.max_milestones, mode),
        "selection": selection_summary(best, baseline, lineage, mode),
        "outcome_summary": outcome_summary(
            records,
            reviews,
            args.max_milestones,
            args.max_non_improvements,
            mode,
        ),
        "literature_reviews": reviews,
        "warnings": warnings,
        "agent_context": read_agent_context(agent_context_path, args),
        "artifacts": {
            "autofl_yaml": str(config_path.resolve()),
            "results": str(results_path.resolve()),
            "campaign_state": str(state_path.resolve()),
            "progress_plot": str(progress_path.resolve()),
            "progress_plot_available": progress_plot_available,
            "summary_json": str(summary_path.resolve()),
            "report": str(report_path.resolve()),
        },
    }
    atomic_write_text(report_path, report_markdown(summary, records))
    atomic_write_text(summary_path, json.dumps(summary, indent=2, sort_keys=True) + "\n")
    return summary


def generate(args: argparse.Namespace) -> Dict[str, Any]:
    root = Path(args.campaign_dir).expanduser().resolve()
    if not root.is_dir():
        raise ValueError(f"Auto-FL campaign directory not found: {root}")
    results_path = resolve_path(root, args.results)
    if not results_path.is_file():
        raise ValueError(f"Auto-FL ledger not found: {results_path}")
    with locked_campaign_workspace(root, "report"):
        return generate_locked(args, root)


def main(argv: Optional[Sequence[str]] = None) -> int:
    args = parse_args(argv)
    try:
        summary = generate(args)
    except (OSError, ValueError) as exc:
        print(f"error: {exc}", file=sys.stderr)
        return 2
    print(json.dumps({"status": "ok", "artifacts": summary["artifacts"], "best": summary["best"]}, sort_keys=True))
    return 0


if __name__ == "__main__":
    raise SystemExit(main())

Requirements and compatibility

Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta

“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.

Needs: Needs Python

The author says: Requires NVFLARE 2.9.0+, Python, and artifacts from an NVFLARE Auto-FL campaign.

Security

What our scanner noticed· Medium risk

  • Large base64-encoded blobs
  • Scripts that your agent may run

This skill includes scripts that your agent may run. Read them before you use it.

A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.

Attribution and licence

Created by
NVIDIA FLARE Team
Publisher
NVIDIA (Official)
Source
NVIDIA/skills · skills/nvflare-autofl-report · commit fce3cca
Licence
Apache License 2.0 (redistribution allowed)
  • LICENSE (added by AutoGineer from LICENSE-APACHE)
  • ATTRIBUTION.md (written by AutoGineer)
Synced

AutoGineer is not affiliated with or endorsed by NVIDIA FLARE Team or NVIDIA.

Download ZIP

The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download nvflare-autofl-report.zip