Consensus

Head slot tracking, justification, finalization, and fork choice analysis for PQ Devnet clients.

This notebook examines:

  • Head slot vs current slot (how far behind each client is)
  • Justified and finalized slot progression
  • Head-to-justified, justified-to-finalized, and head-to-finalized distances
  • Fork choice reorgs
Show code
import json
from pathlib import Path

import pandas as pd
import plotly.graph_objects as go
from plotly.subplots import make_subplots
from IPython.display import display

# Set default renderer for static HTML output
import plotly.io as pio
pio.renderers.default = "notebook"
Show code
# Resolve devnet_id
DATA_DIR = Path("../data")

if devnet_id is None:
    devnets_path = DATA_DIR / "devnets.json"
    if devnets_path.exists():
        with open(devnets_path) as f:
            devnets = json.load(f).get("devnets", [])
        if devnets:
            devnet_id = devnets[-1]["id"]
            print(f"Using latest devnet: {devnet_id}")
    else:
        raise ValueError("No devnets.json found. Run 'just detect-devnets' first.")

DEVNET_DIR = DATA_DIR / devnet_id
print(f"Loading data from: {DEVNET_DIR}")
Loading data from: ../data/pqdevnet-20260709T2210Z
Show code
# Load devnet metadata
with open(DATA_DIR / "devnets.json") as f:
    devnets_data = json.load(f)
    devnet_info = next((d for d in devnets_data["devnets"] if d["id"] == devnet_id), None)

if devnet_info:
    print(f"Devnet: {devnet_info['id']}")
    print(f"Duration: {devnet_info['duration_hours']:.1f} hours")
    print(f"Time: {devnet_info['start_time']} to {devnet_info['end_time']}")
    print(f"Slots: {devnet_info['start_slot']} \u2192 {devnet_info['end_slot']}")
    print(f"Clients: {', '.join(devnet_info['clients'])}")
Devnet: pqdevnet-20260709T2210Z
Duration: 1.6 hours
Time: 2026-07-09T22:10:31+00:00 to 2026-07-09T23:46:09+00:00
Slots: 3276 β†’ 6548
Clients: buildx_buildkit_multiarch0, ethlambda_0, ethlambda_10, ethlambda_2, ethlambda_4, ethlambda_7, ethlambda_8, gean_0, gean_1, gean_2, gean_3, grandine_0, grandine_2, grandine_3, grandine_4, grandine_5, grandine_6, lantern_0, lantern_1, lantern_2, lantern_3, ream_0, ream_1, ream_2, ream_3, ream_4, ream_6, zeam_0, zeam_1, zeam_2, zeam_4, zeam_5, zeam_6

Load DataΒΆ

Show code
# Unified client list from devnet metadata (includes all containers via cAdvisor)
all_clients = sorted(devnet_info["clients"])
n_cols = min(len(all_clients), 2)
n_rows = -(-len(all_clients) // n_cols)

# Load head slot data
head_df = pd.read_parquet(DEVNET_DIR / "head_slot.parquet")
head_df = head_df.groupby(["client", "metric", "timestamp"], as_index=False)["value"].max()
print(f"Head slot: {len(head_df)} records, clients: {sorted(head_df['client'].unique())}")

# Load finality data
finality_df = pd.read_parquet(DEVNET_DIR / "finality_metrics.parquet")
finality_df = finality_df.groupby(["client", "metric", "timestamp"], as_index=False)["value"].max()
print(f"Finality: {len(finality_df)} records, clients: {sorted(finality_df['client'].unique())}")
print(f"Finality metrics: {sorted(finality_df['metric'].unique())}")

# Load fork choice reorgs
reorgs_df = pd.read_parquet(DEVNET_DIR / "fork_choice_reorgs.parquet")
reorgs_df = reorgs_df.groupby(["client", "timestamp"], as_index=False)["value"].max()
print(f"Reorgs: {len(reorgs_df)} records, clients: {sorted(reorgs_df['client'].unique())}")

print(f"\nAll clients ({len(all_clients)}): {all_clients}")
Head slot: 6102 records, clients: ['ethlambda_0', 'ethlambda_10', 'ethlambda_2', 'ethlambda_4', 'ethlambda_7', 'ethlambda_8', 'gean_0', 'gean_1', 'gean_2', 'gean_3', 'grandine_0', 'grandine_2', 'grandine_3', 'grandine_4', 'grandine_5', 'grandine_6', 'lantern_0', 'lantern_1', 'lantern_2', 'lantern_3', 'ream_0', 'ream_1', 'ream_2', 'ream_3', 'ream_4', 'ream_6', 'zeam_0', 'zeam_1', 'zeam_2', 'zeam_4', 'zeam_5', 'zeam_6']
Finality: 8752 records, clients: ['ethlambda_0', 'ethlambda_10', 'ethlambda_2', 'ethlambda_4', 'ethlambda_7', 'ethlambda_8', 'gean_0', 'gean_1', 'gean_2', 'gean_3', 'grandine_0', 'grandine_2', 'grandine_3', 'grandine_4', 'grandine_5', 'grandine_6', 'lantern_0', 'lantern_1', 'lantern_2', 'lantern_3', 'ream_0', 'ream_1', 'ream_2', 'ream_3', 'ream_4', 'ream_6', 'zeam_0', 'zeam_1', 'zeam_2', 'zeam_4', 'zeam_5', 'zeam_6']
Finality metrics: ['lean_finalized_slot', 'lean_justified_slot', 'lean_latest_finalized_slot', 'lean_latest_justified_slot']
Reorgs: 2494 records, clients: ['ethlambda_0', 'ethlambda_10', 'ethlambda_2', 'ethlambda_4', 'ethlambda_7', 'ethlambda_8', 'gean_0', 'gean_1', 'gean_2', 'gean_3', 'grandine_0', 'grandine_2', 'grandine_3', 'grandine_4', 'grandine_5', 'grandine_6', 'lantern_0', 'lantern_1', 'lantern_2', 'lantern_3', 'zeam_0', 'zeam_1', 'zeam_2', 'zeam_4', 'zeam_5', 'zeam_6']

All clients (33): ['buildx_buildkit_multiarch0', 'ethlambda_0', 'ethlambda_10', 'ethlambda_2', 'ethlambda_4', 'ethlambda_7', 'ethlambda_8', 'gean_0', 'gean_1', 'gean_2', 'gean_3', 'grandine_0', 'grandine_2', 'grandine_3', 'grandine_4', 'grandine_5', 'grandine_6', 'lantern_0', 'lantern_1', 'lantern_2', 'lantern_3', 'ream_0', 'ream_1', 'ream_2', 'ream_3', 'ream_4', 'ream_6', 'zeam_0', 'zeam_1', 'zeam_2', 'zeam_4', 'zeam_5', 'zeam_6']

Head Slot vs Current SlotΒΆ

Comparing each client's head slot (lean_head_slot) against the expected current slot (lean_current_slot). A gap indicates the client is falling behind.

Show code
fig = make_subplots(
    rows=n_rows, cols=n_cols,
    subplot_titles=all_clients,
    vertical_spacing=0.12 / max(n_rows - 1, 1) * 2,
    horizontal_spacing=0.08,
)

colors = {"lean_head_slot": "#636EFA", "lean_current_slot": "#EF553B"}
labels = {"lean_head_slot": "head_slot", "lean_current_slot": "current_slot"}
legend_added = set()

for i, client in enumerate(all_clients):
    row = i // n_cols + 1
    col = i % n_cols + 1
    cdf = head_df[head_df["client"] == client]
    has_data = False
    for metric in ["lean_current_slot", "lean_head_slot"]:
        mdf = cdf[cdf["metric"] == metric].sort_values("timestamp")
        if mdf.empty:
            continue
        has_data = True
        label = labels[metric]
        show_legend = metric not in legend_added
        legend_added.add(metric)
        fig.add_trace(
            go.Scatter(
                x=mdf["timestamp"], y=mdf["value"],
                name=label, legendgroup=metric,
                showlegend=show_legend,
                line=dict(color=colors[metric]),
            ),
            row=row, col=col,
        )
    if not has_data:
        fig.add_trace(
            go.Scatter(x=[None], y=[None], showlegend=False, hoverinfo='skip'),
            row=row, col=col,
        )
        _n = (row - 1) * n_cols + col
        _s = "" if _n == 1 else str(_n)
        fig.add_annotation(
            text="No data available",
            xref=f"x{_s} domain", yref=f"y{_s} domain",
            x=0.5, y=0.5,
            showarrow=False,
            font=dict(size=12, color="#999"),
        )
    fig.update_yaxes(title_text="Slot", row=row, col=col)

fig.update_layout(
    title="Head Slot vs Current Slot by Client",
    height=270 * n_rows,
)
fig.show()

Head, Justified & Finalized SlotsΒΆ

Progression of justified and finalized slots over time. With 3SF, both should track closely behind the head slot.

Show code
jf_metrics = ["lean_latest_justified_slot", "lean_latest_finalized_slot"]
jf_df = finality_df[finality_df["metric"].isin(jf_metrics)].copy()

head_only_all = head_df[head_df["metric"] == "lean_head_slot"].copy()
head_only_all["metric"] = "lean_head_slot"

combined = pd.concat([jf_df, head_only_all], ignore_index=True)

fig = make_subplots(
    rows=n_rows, cols=n_cols,
    subplot_titles=all_clients,
    vertical_spacing=0.12 / max(n_rows - 1, 1) * 2,
    horizontal_spacing=0.08,
)

colors = {
    "lean_head_slot": "#636EFA",
    "lean_latest_justified_slot": "#00CC96",
    "lean_latest_finalized_slot": "#EF553B",
}
labels = {
    "lean_head_slot": "head",
    "lean_latest_justified_slot": "justified",
    "lean_latest_finalized_slot": "finalized",
}
legend_added = set()

for i, client in enumerate(all_clients):
    row = i // n_cols + 1
    col = i % n_cols + 1
    cdf = combined[combined["client"] == client]
    has_data = False
    for metric in ["lean_head_slot", "lean_latest_justified_slot", "lean_latest_finalized_slot"]:
        mdf = cdf[cdf["metric"] == metric].sort_values("timestamp")
        if mdf.empty:
            continue
        has_data = True
        show_legend = metric not in legend_added
        legend_added.add(metric)
        fig.add_trace(
            go.Scatter(
                x=mdf["timestamp"], y=mdf["value"],
                name=labels[metric], legendgroup=metric,
                showlegend=show_legend,
                line=dict(color=colors[metric]),
            ),
            row=row, col=col,
        )
    if not has_data:
        fig.add_trace(
            go.Scatter(x=[None], y=[None], showlegend=False, hoverinfo='skip'),
            row=row, col=col,
        )
        _n = (row - 1) * n_cols + col
        _s = "" if _n == 1 else str(_n)
        fig.add_annotation(
            text="No data available",
            xref=f"x{_s} domain", yref=f"y{_s} domain",
            x=0.5, y=0.5,
            showarrow=False,
            font=dict(size=12, color="#999"),
        )
    fig.update_yaxes(title_text="Slot", row=row, col=col)

fig.update_layout(
    title="Head, Justified & Finalized Slot by Client",
    height=270 * n_rows,
)
fig.show()

Head-to-Finalized DistanceΒΆ

Total distance between head slot and finalized slot. This is the combined gap across justification and finalization, showing the overall finality lag.

finalized
justified
head
current
Show code
head_ts_hf = head_df[head_df["metric"] == "lean_head_slot"][["client", "timestamp", "value"]].rename(columns={"value": "head_slot"})
fin_ts_hf = finality_df[finality_df["metric"] == "lean_latest_finalized_slot"][["client", "timestamp", "value"]].rename(columns={"value": "finalized_slot"})
head_fin_lag = head_ts_hf.merge(fin_ts_hf, on=["client", "timestamp"], how="inner")
head_fin_lag["lag"] = head_fin_lag["head_slot"] - head_fin_lag["finalized_slot"]

fig = make_subplots(
    rows=n_rows, cols=n_cols,
    subplot_titles=all_clients,
    vertical_spacing=0.12 / max(n_rows - 1, 1) * 2,
    horizontal_spacing=0.08,
)

for i, client in enumerate(all_clients):
    row = i // n_cols + 1
    col = i % n_cols + 1
    cdf = head_fin_lag[head_fin_lag["client"] == client].sort_values("timestamp")
    if not cdf.empty:
        fig.add_trace(
            go.Scatter(
                x=cdf["timestamp"], y=cdf["lag"],
                name=client, showlegend=False,
                line=dict(color="#636EFA"),
            ),
            row=row, col=col,
        )
    else:
        fig.add_trace(
            go.Scatter(x=[None], y=[None], showlegend=False, hoverinfo='skip'),
            row=row, col=col,
        )
        _n = (row - 1) * n_cols + col
        _s = "" if _n == 1 else str(_n)
        fig.add_annotation(
            text="No data available",
            xref=f"x{_s} domain", yref=f"y{_s} domain",
            x=0.5, y=0.5,
            showarrow=False,
            font=dict(size=12, color="#999"),
        )
    fig.update_yaxes(title_text="Slots", row=row, col=col)

fig.update_layout(
    title="Head-to-Finalized Distance (head_slot - finalized_slot)",
    height=270 * n_rows,
)
fig.show()

Current-to-Head DistanceΒΆ

Difference between current slot and head slot. A value of 0 means the client is fully synced; higher values indicate falling behind.

finalized
justified
head
current
Show code
current_df = head_df[head_df["metric"] == "lean_current_slot"][["client", "timestamp", "value"]].rename(columns={"value": "current_slot"})
head_only = head_df[head_df["metric"] == "lean_head_slot"][["client", "timestamp", "value"]].rename(columns={"value": "head_slot"})
lag_df = current_df.merge(head_only, on=["client", "timestamp"], how="inner")
lag_df["lag"] = lag_df["current_slot"] - lag_df["head_slot"]

fig = make_subplots(
    rows=n_rows, cols=n_cols,
    subplot_titles=all_clients,
    vertical_spacing=0.12 / max(n_rows - 1, 1) * 2,
    horizontal_spacing=0.08,
)

for i, client in enumerate(all_clients):
    row = i // n_cols + 1
    col = i % n_cols + 1
    cdf = lag_df[lag_df["client"] == client].sort_values("timestamp")
    if not cdf.empty:
        fig.add_trace(
            go.Scatter(
                x=cdf["timestamp"], y=cdf["lag"],
                name=client, showlegend=False,
                line=dict(color="#636EFA"),
            ),
            row=row, col=col,
        )
    else:
        fig.add_trace(
            go.Scatter(x=[None], y=[None], showlegend=False, hoverinfo='skip'),
            row=row, col=col,
        )
        _n = (row - 1) * n_cols + col
        _s = "" if _n == 1 else str(_n)
        fig.add_annotation(
            text="No data available",
            xref=f"x{_s} domain", yref=f"y{_s} domain",
            x=0.5, y=0.5,
            showarrow=False,
            font=dict(size=12, color="#999"),
        )
    fig.update_yaxes(title_text="Slots behind", row=row, col=col)

fig.update_layout(
    title="Current-to-Head Distance (current_slot - head_slot)",
    height=270 * n_rows,
)
fig.show()

Head-to-Justified DistanceΒΆ

Gap between head slot and justified slot. A growing gap means the client's head is advancing but justification is not keeping up.

finalized
justified
head
current
Show code
head_ts = head_df[head_df["metric"] == "lean_head_slot"][["client", "timestamp", "value"]].rename(columns={"value": "head_slot"})
just_ts = finality_df[finality_df["metric"] == "lean_latest_justified_slot"][["client", "timestamp", "value"]].rename(columns={"value": "justified_slot"})
justification_lag = head_ts.merge(just_ts, on=["client", "timestamp"], how="inner")
justification_lag["lag"] = justification_lag["head_slot"] - justification_lag["justified_slot"]

fig = make_subplots(
    rows=n_rows, cols=n_cols,
    subplot_titles=all_clients,
    vertical_spacing=0.12 / max(n_rows - 1, 1) * 2,
    horizontal_spacing=0.08,
)

for i, client in enumerate(all_clients):
    row = i // n_cols + 1
    col = i % n_cols + 1
    cdf = justification_lag[justification_lag["client"] == client].sort_values("timestamp")
    if not cdf.empty:
        fig.add_trace(
            go.Scatter(
                x=cdf["timestamp"], y=cdf["lag"],
                name=client, showlegend=False,
                line=dict(color="#636EFA"),
            ),
            row=row, col=col,
        )
    else:
        fig.add_trace(
            go.Scatter(x=[None], y=[None], showlegend=False, hoverinfo='skip'),
            row=row, col=col,
        )
        _n = (row - 1) * n_cols + col
        _s = "" if _n == 1 else str(_n)
        fig.add_annotation(
            text="No data available",
            xref=f"x{_s} domain", yref=f"y{_s} domain",
            x=0.5, y=0.5,
            showarrow=False,
            font=dict(size=12, color="#999"),
        )
    fig.update_yaxes(title_text="Slots", row=row, col=col)

fig.update_layout(
    title="Head-to-Justified Distance (head_slot - justified_slot)",
    height=270 * n_rows,
)
fig.show()

Justified-to-Finalized DistanceΒΆ

Gap between justified slot and finalized slot. A growing gap means justification is advancing but finalization is stalling.

finalized
justified
head
current
Show code
fin_ts = finality_df[finality_df["metric"] == "lean_latest_finalized_slot"][["client", "timestamp", "value"]].rename(columns={"value": "finalized_slot"})
finality_lag = just_ts.merge(fin_ts, on=["client", "timestamp"], how="inner")
finality_lag["lag"] = finality_lag["justified_slot"] - finality_lag["finalized_slot"]

fig = make_subplots(
    rows=n_rows, cols=n_cols,
    subplot_titles=all_clients,
    vertical_spacing=0.12 / max(n_rows - 1, 1) * 2,
    horizontal_spacing=0.08,
)

for i, client in enumerate(all_clients):
    row = i // n_cols + 1
    col = i % n_cols + 1
    cdf = finality_lag[finality_lag["client"] == client].sort_values("timestamp")
    if not cdf.empty:
        fig.add_trace(
            go.Scatter(
                x=cdf["timestamp"], y=cdf["lag"],
                name=client, showlegend=False,
                line=dict(color="#636EFA"),
            ),
            row=row, col=col,
        )
    else:
        fig.add_trace(
            go.Scatter(x=[None], y=[None], showlegend=False, hoverinfo='skip'),
            row=row, col=col,
        )
        _n = (row - 1) * n_cols + col
        _s = "" if _n == 1 else str(_n)
        fig.add_annotation(
            text="No data available",
            xref=f"x{_s} domain", yref=f"y{_s} domain",
            x=0.5, y=0.5,
            showarrow=False,
            font=dict(size=12, color="#999"),
        )
    fig.update_yaxes(title_text="Slots", row=row, col=col)

fig.update_layout(
    title="Justified-to-Finalized Distance (justified_slot - finalized_slot)",
    height=270 * n_rows,
)
fig.show()

Fork Choice ReorgsΒΆ

Cumulative chain reorgs per client. Reorgs occur when the fork choice rule switches to a different chain head, often caused by late-arriving blocks or attestations.

Show code
fig = make_subplots(
    rows=n_rows, cols=n_cols,
    subplot_titles=all_clients,
    vertical_spacing=0.12 / max(n_rows - 1, 1) * 2,
    horizontal_spacing=0.08,
)

for i, client in enumerate(all_clients):
    row = i // n_cols + 1
    col = i % n_cols + 1
    cdf = reorgs_df[reorgs_df["client"] == client].sort_values("timestamp")
    if not cdf.empty:
        fig.add_trace(
            go.Scatter(
                x=cdf["timestamp"], y=cdf["value"],
                name=client, showlegend=False,
                line=dict(color="#636EFA"),
            ),
            row=row, col=col,
        )
    else:
        fig.add_trace(
            go.Scatter(x=[None], y=[None], showlegend=False, hoverinfo='skip'),
            row=row, col=col,
        )
        _n = (row - 1) * n_cols + col
        _s = "" if _n == 1 else str(_n)
        fig.add_annotation(
            text="No data available",
            xref=f"x{_s} domain", yref=f"y{_s} domain",
            x=0.5, y=0.5,
            showarrow=False,
            font=dict(size=12, color="#999"),
        )
    fig.update_yaxes(title_text="Cumulative reorgs", row=row, col=col)

fig.update_layout(
    title="Fork Choice Reorgs by Client",
    height=270 * n_rows,
)
fig.show()

SummaryΒΆ

Show code
summary_rows = []

for client in all_clients:
    row = {"Client": client}

    # Current-to-head distance
    client_lag = lag_df[lag_df["client"] == client]["lag"]
    if not client_lag.empty:
        row["Avg C-H Dist."] = f"{client_lag.mean():.1f}"
        row["Max C-H Dist."] = f"{client_lag.max():.0f}"

    # Head-to-justified distance
    client_just = justification_lag[justification_lag["client"] == client]["lag"]
    if not client_just.empty:
        row["Avg H-J Dist."] = f"{client_just.mean():.1f}"
        row["Max H-J Dist."] = f"{client_just.max():.0f}"

    # Justified-to-finalized distance
    client_fin = finality_lag[finality_lag["client"] == client]["lag"]
    if not client_fin.empty:
        row["Avg J-F Dist."] = f"{client_fin.mean():.1f}"
        row["Max J-F Dist."] = f"{client_fin.max():.0f}"

    # Head-to-finalized distance
    client_hf = head_fin_lag[head_fin_lag["client"] == client]["lag"]
    if not client_hf.empty:
        row["Avg H-F Dist."] = f"{client_hf.mean():.1f}"
        row["Max H-F Dist."] = f"{client_hf.max():.0f}"

    # Reorgs
    client_reorgs = reorgs_df[reorgs_df["client"] == client]["value"]
    if not client_reorgs.empty:
        row["Reorgs"] = f"{client_reorgs.max():.0f}"

    # Final head slot
    client_head = head_df[(head_df["client"] == client) & (head_df["metric"] == "lean_head_slot")]
    if not client_head.empty:
        row["Final Head Slot"] = f"{client_head['value'].max():.0f}"

    # Final finalized slot
    client_finalized = finality_df[(finality_df["client"] == client) & (finality_df["metric"] == "lean_latest_finalized_slot")]
    if not client_finalized.empty:
        row["Final Finalized Slot"] = f"{client_finalized['value'].max():.0f}"

    summary_rows.append(row)

if summary_rows:
    summary_df = pd.DataFrame(summary_rows).set_index("Client").fillna("-")
    display(summary_df)

print(f"\nDevnet: {devnet_id}")
if devnet_info:
    print(f"Duration: {devnet_info['duration_hours']:.1f} hours")
Avg C-H Dist. Max C-H Dist. Avg H-J Dist. Max H-J Dist. Avg J-F Dist. Max J-F Dist. Avg H-F Dist. Max H-F Dist. Reorgs Final Head Slot Final Finalized Slot
Client
buildx_buildkit_multiarch0 - - - - - - - - - - -
ethlambda_0 3.1 10 2533.1 3245 81.0 81 2614.1 3326 474 6564 3238
ethlambda_10 2.9 9 2535.6 3250 81.0 81 2616.6 3331 482 6569 3238
ethlambda_2 2.7 10 2533.8 3245 81.0 81 2614.8 3326 469 6564 3238
ethlambda_4 2.5 12 2533.0 3245 81.0 81 2614.0 3326 468 6564 3238
ethlambda_7 3.2 9 2536.2 3250 81.0 81 2617.2 3331 473 6569 3238
ethlambda_8 2.3 8 2535.2 3250 81.0 81 2616.2 3331 474 6569 3238
gean_0 2.7 10 2533.8 3245 81.0 81 2614.8 3326 108 6564 3238
gean_1 2.5 10 2534.0 3245 81.0 81 2615.0 3326 103 6564 3238
gean_2 2.8 14 2534.7 3250 81.0 81 2615.7 3331 542 6569 3238
gean_3 3.0 37 2535.4 3250 81.0 81 2616.4 3331 110 6569 3238
grandine_0 90.0 2881 2446.5 3245 81.0 81 2527.5 3326 676 6564 3238
grandine_2 89.9 2162 2447.6 3245 81.0 81 2528.6 3326 652 6564 3238
grandine_3 693.9 3195 1841.6 2853 81.0 81 1922.6 2934 949 6172 3238
grandine_4 576.4 3197 1961.1 2969 81.0 81 2042.1 3050 882 6288 3238
grandine_5 120.9 2583 2416.6 3245 81.0 81 2497.6 3326 792 6564 3238
grandine_6 71.3 2417 2466.2 3250 81.0 81 2547.2 3331 769 6569 3238
lantern_0 5.4 21 2534.1 3245 81.0 81 2615.1 3326 314 6564 3238
lantern_1 8.7 18 2527.7 3242 81.0 81 2608.7 3323 227 6561 3238
lantern_2 4.9 19 2531.4 3245 81.0 81 2612.4 3326 245 6564 3238
lantern_3 5.2 14 2533.3 3245 81.0 81 2614.3 3326 379 6564 3238
ream_0 16.8 1780 2349.9 3234 81.0 81 2430.9 3315 - 6553 3238
ream_1 3.1 295 1356.9 1360 81.0 81 1437.9 1441 - 4679 3238
ream_2 -1.5 6 2515.1 3069 81.0 81 2596.1 3150 - 6388 3238
ream_3 8.0 768 1465.9 2153 81.0 81 1546.9 2234 - 5472 3238
ream_4 0.0 0 1220.9 1392 81.0 81 1301.9 1473 - 4711 3238
ream_6 -1.3 0 2387.1 2954 81.0 81 2468.1 3035 - 6273 3238
zeam_0 4.9 13 2530.6 3242 56.0 56 2586.6 3298 540 6561 3263
zeam_1 4.2 16 2534.3 3250 56.0 56 2590.3 3306 503 6569 3263
zeam_2 606.2 2699 1936.0 3217 53.3 56 1989.3 3273 338 6536 3263
zeam_4 340.0 1972 2197.5 3242 54.9 56 2252.4 3298 148 6561 3263
zeam_5 2.9 12 2535.6 3250 56.0 56 2591.6 3306 104 6569 3263
zeam_6 3.3 12 2529.2 3250 56.0 56 2585.2 3306 573 6569 3263
Devnet: pqdevnet-20260709T2210Z
Duration: 1.6 hours