Why TRO increased while B10+ remained flat, 2023–2026

ESC31 diagnostic note

The question

The assessment table shows median relative total reproductive output (TRO) increasing from 0.255 in 2023 to 0.281 in 2026, while median relative biomass of fish aged 10 years and older (B10+) rounds to 0.231 in both years. The issue raised in the email discussion is not why the two measures have different levels. It is why they move differently over the same three-year period under the same 2026 reference set.

Short answer. This is a genuine age-composition result, not a calculation error. Fewer fish aged 10–14 in 2026 are offset in B10+ by more fish at older, heavier ages, leaving total age-10+ biomass almost unchanged. TRO also counts reproductive contribution below age 10 and weights older fish much more strongly through maturity and length-based fecundity. Increases at ages 8–9 and 20–24 therefore raise TRO even though aggregate B10+ is flat.

Show reproducible R code
cat(
  '<div class="metric-grid">',
  sprintf(
    '<div class="metric-card"><span class="value">%s</span><span class="label">reference-set draws with TRO increasing</span></div>',
    fmt_pct(p_tro_up)
  ),
  sprintf(
    '<div class="metric-card"><span class="value">%s</span><span class="label">reference-set draws with B10+ increasing</span></div>',
    fmt_pct(p_b10_up)
  ),
  sprintf(
    '<div class="metric-card"><span class="value">+%s</span><span class="label">centre-fit TRO change</span></div>',
    fmt_pct(centre$change_pct[centre$Metric == "Relative TRO"] / 100)
  ),
  sprintf(
    '<div class="metric-card"><span class="value">+%s</span><span class="label">centre-fit B10+ change</span></div>',
    fmt_pct(centre$change_pct[centre$Metric == "Relative B10+"] / 100)
  ),
  '</div>'
)
100.0%reference-set draws with TRO increasing
50.6%reference-set draws with B10+ increasing
+10.4%centre-fit TRO change
+0.3%centre-fit B10+ change

What the two quantities measure

The model calculates both quantities from season-1 numbers-at-age, but applies different age-specific weights:

  • B10+ is the sum of numbers aged 10 and older multiplied by the LL1 weight-at-age schedule. Fish younger than 10 contribute exactly zero.
  • TRO is the sum across all ages of numbers multiplied by relative reproductive output per fish. That weight combines length-based maturity with a fitted length-based fecundity relationship and is normalized to one at the terminal age.

There is no reproductive senescence term in this calculation. Per-fish TRO weight increases with age in the fitted model. Moreover, the 2023 and 2026 weight-at-age vectors are identical for both TRO and B10+, so the contrast is entirely caused by the fitted numbers-at-age and their distribution among ages—not by a change in the measurement rules between years.

Evidence across the 2,000 reference-set draws

The accepted balanced nine-cell MCMC-grid cache contains 2,000 draws for every model year. The table below gives the equal-tailed 95% interval across those draws.

Show reproducible R code
draw_summary |>
  mutate(
    `Median (95% interval)` = sprintf(
      "%.3f (%.3f–%.3f)", median, lower, upper
    )
  ) |>
  select(Metric, Year, `Median (95% interval)`) |>
  knitr::kable(align = c("l", "r", "r"))
Table 1: Relative TRO and B10+ in 2023 and 2026 across the accepted 2,000-draw reference set.
Metric Year Median (95% interval)
Relative B10+ 2023 0.231 (0.171–0.310)
Relative B10+ 2026 0.231 (0.171–0.313)
Relative TRO 2023 0.255 (0.189–0.338)
Relative TRO 2026 0.281 (0.209–0.373)

The paired-draw median change is 0.026 for relative TRO, or 10.3%, compared with 0.0001 for relative B10+, or 0.0%. TRO increases in every draw; the sign of the small B10+ change is almost evenly divided.

Show reproducible R code
ggplot(paired_draws, aes(x = B10_change, y = TRO_change)) +
  geom_hline(yintercept = 0, color = "#707b84", linewidth = 0.4) +
  geom_vline(xintercept = 0, color = "#707b84", linewidth = 0.4) +
  geom_point(color = tro_colour, alpha = 0.28, size = 1.45) +
  annotate(
    "label",
    x = quantile(paired_draws$B10_change, 0.03),
    y = quantile(paired_draws$TRO_change, 0.95),
    label = paste0("TRO increased in ", fmt_pct(p_tro_up), " of draws"),
    hjust = 0, color = "#17212b", fill = "white", label.size = 0
  ) +
  annotate(
    "label",
    x = quantile(paired_draws$B10_change, 0.03),
    y = quantile(paired_draws$TRO_change, 0.83),
    label = paste0("B10+ increased in ", fmt_pct(p_b10_up), " of draws"),
    hjust = 0, color = "#17212b", fill = "white", label.size = 0
  ) +
  scale_x_continuous(labels = scales::label_number(accuracy = 0.005)) +
  scale_y_continuous(labels = scales::label_number(accuracy = 0.005)) +
  labs(
    x = "Change in relative B10+ (2026 − 2023)",
    y = "Change in relative TRO (2026 − 2023)"
  )
Figure 1: Paired change from 2023 to 2026 in each of the 2,000 reference-set draws. All TRO changes are positive, whereas B10+ changes straddle zero.

The trajectories show that the divergence is a recent feature of the fitted age structure rather than an isolated 2026 calculation.

Show reproducible R code
metric_colours <- c("Relative TRO" = tro_colour, "Relative B10+" = b10_colour)
ggplot(trajectory_summary, aes(x = Year, y = median, color = Metric, fill = Metric)) +
  geom_vline(xintercept = c(2023, 2026), linetype = "dashed", color = "#87929c", linewidth = 0.4) +
  geom_ribbon(aes(ymin = lower, ymax = upper), alpha = 0.16, color = NA) +
  geom_line(linewidth = 0.85) +
  scale_color_manual(values = metric_colours) +
  scale_fill_manual(values = metric_colours) +
  scale_x_continuous(breaks = c(2000, 2005, 2010, 2015, 2020, 2023, 2026)) +
  scale_y_continuous(labels = scales::label_number(accuracy = 0.05)) +
  labs(x = NULL, y = "Relative status", color = NULL, fill = NULL) +
  theme(legend.position = "bottom")
Figure 2: Relative TRO and B10+ trajectories across the accepted 2,000-draw reference set, overlaid on a common axis. Lines are medians and ribbons are equal-tailed 95% intervals. The dashed guides mark 2023 and 2026.

What changed in the centre fit

The centre-cell maximum-likelihood fit provides the age detail needed to interpret the reference-set result. Its aggregate changes are:

Show reproducible R code
centre |>
  mutate(
    `2023` = case_when(
      Metric %in% c("Relative TRO", "Relative B10+") ~ sprintf("%.3f", value_2023),
      Metric == "Age-10+ numbers" ~ paste0(fmt1(value_2023 / 1e6), " million"),
      TRUE ~ paste0(fmt1(value_2023 * 1000), " kg")
    ),
    `2026` = case_when(
      Metric %in% c("Relative TRO", "Relative B10+") ~ sprintf("%.3f", value_2026),
      Metric == "Age-10+ numbers" ~ paste0(fmt1(value_2026 / 1e6), " million"),
      TRUE ~ paste0(fmt1(value_2026 * 1000), " kg")
    ),
    Change = sprintf("%+.1f%%", change_pct)
  ) |>
  select(Metric, `2023`, `2026`, Change) |>
  knitr::kable(align = c("l", "r", "r", "r"))
Table 2: Centre-fit changes from 2023 to 2026. Mean weight is calculated within the age-10+ group.
Metric 2023 2026 Change
Relative TRO 0.242 0.267 +10.4%
Relative B10+ 0.220 0.220 +0.3%
Age-10+ numbers 2.2 million 2.1 million -3.5%
Mean weight of age-10+ fish 96.6 kg 100.5 kg +4.0%

Numbers aged 10+ fall by 3.5%, while their abundance-weighted mean body weight rises by 4.0%. Those opposing effects almost cancel in B10+.

Show reproducible R code
age_state |>
  select(Age, `2023` = N_2023, `2026` = N_2026) |>
  pivot_longer(c(`2023`, `2026`), names_to = "Year", values_to = "Number") |>
  ggplot(aes(x = Age, y = Number, color = Year)) +
  geom_vline(xintercept = 9.5, linetype = "dashed", color = "#707b84", linewidth = 0.45) +
  geom_line(linewidth = 0.8) +
  geom_point(size = 1.7) +
  annotate(
    "text", x = 10.1, y = max(age_state$N_2023, age_state$N_2026) * 0.8,
    label = "B10+ begins", hjust = 0, color = "#51606f", size = 3.3
  ) +
  scale_color_manual(values = c("2023" = "#59636e", "2026" = tro_colour)) +
  scale_y_log10(
    labels = scales::label_number(scale_cut = scales::cut_short_scale())
  ) +
  scale_x_continuous(breaks = seq(0, 30, 2)) +
  labs(x = "Age", y = "Number of fish (log scale)", color = NULL)
Figure 3: Centre-fit season-1 numbers-at-age in 2023 and 2026. The logarithmic vertical scale keeps the older ages visible; the vertical guide marks the B10+ threshold.

The clearest shifts are a decline at ages 10–14, increases at ages 8–9, and an increase around ages 20–24. These shifts affect the two metrics differently.

Show reproducible R code
age_contributions <- age_state |>
  transmute(
    Age,
    `TRO contribution change` = delta_TRO,
    `B10+ contribution change (t)` = delta_B10
  ) |>
  pivot_longer(-Age, names_to = "Metric", values_to = "Change") |>
  mutate(Direction = if_else(Change >= 0, "Increase", "Decrease"))

ggplot(age_contributions, aes(x = Age, y = Change, fill = Direction)) +
  geom_hline(yintercept = 0, color = "#4f5963", linewidth = 0.4) +
  geom_vline(xintercept = 9.5, linetype = "dashed", color = "#87929c", linewidth = 0.4) +
  geom_col(width = 0.8) +
  facet_wrap(~Metric, ncol = 1, scales = "free_y") +
  scale_fill_manual(values = direction_colours) +
  scale_x_continuous(breaks = seq(0, 30, 2)) +
  scale_y_continuous(
    labels = scales::label_number(scale_cut = scales::cut_short_scale())
  ) +
  labs(x = "Age", y = "Change in contribution", fill = NULL)
Figure 4: Centre-fit change from 2023 to 2026 in each age’s contribution to TRO and B10+. TRO units are the model’s normalized reproductive-output units; B10+ is tonnes. Ages below 10 make no contribution to B10+.

The age-group totals make the offsets explicit:

Show reproducible R code
age_group_summary |>
  transmute(
    `Age group` = Age_group,
    `Numbers, 2023` = scales::comma(round(N_2023)),
    `Numbers, 2026` = scales::comma(round(N_2026)),
    `Numbers change` = sprintf("%+.1f%%", N_change_pct),
    `TRO change` = scales::comma(round(TRO_change)),
    `B10+ change (t)` = scales::comma(round(B10_change_t))
  ) |>
  knitr::kable(align = c("l", rep("r", 5)))
Table 3: Centre-fit age-group decomposition of the 2023–2026 change. TRO is in normalized reproductive-output units and B10+ is tonnes.
Age group Numbers, 2023 Numbers, 2026 Numbers change TRO change B10+ change (t)
0–3 13,007,376 8,909,368 -31.5% -136 0
4–7 3,177,375 4,466,009 +40.6% 23,782 0
8–9 731,165 1,174,424 +60.6% 73,903 0
10–14 1,286,740 948,369 -26.3% -147,346 -28,778
15–19 683,432 778,378 +13.9% 67,406 10,057
20–24 128,324 307,847 +139.9% 157,390 21,233
25–29 101,802 85,373 -16.1% -15,758 -2,042
30+ 16,958 18,910 +11.5% 1,953 248

Ages 8–9 contribute 45.8% of the net TRO increase but are excluded from B10+. Ages 20–24 contribute 97.6% of the net TRO increase. Their gain is partly offset by the decline at ages 10–14; in B10+, that age-10–14 biomass loss is almost completely balanced by biomass gains at ages 15–24.

This pattern is consistent with cohort progression. The relatively abundant 2018 cohort is age 8 in 2026, while stronger 2005–2006 cohorts contribute at ages 20–21. At the same time, the cohorts occupying ages 10–14 in 2026 are collectively weaker. The metrics therefore differ because of which cohorts they include and how strongly they weight older fish.

Why the reviewers are concerned

The email sequence indicates a verification and interpretability concern, rather than evidence that rebuilding has stopped:

  1. B10+ was added to the 2026 assessment table to make it comparable with the previous assessment; it had not initially been a reported output.
  2. Norio and Ann independently noticed that B10+ rounded to the same value in 2023 and 2026 while TRO rose.
  3. A generic explanation of the different definitions did not answer their question. They wanted the actual numbers-at-age behind the two years—and, if useful, the intervening trajectory—to verify the result and explain it to ESC participants.

The concern is reasonable because readers often use both metrics as broad indicators of the adult stock. A flat B10+ alongside a clearly increasing TRO looks surprising until the age structure is shown, and it could otherwise be mistaken for a table or calculation error.

Did the same thing happen in the previous assessment?

Not in the published headline comparison. Table 4 of the 2023 assessment reported the following medians and 80% intervals:

Show reproducible R code
tibble(
  `Assessment year` = c(2020L, 2023L),
  `Relative TRO, median (80% interval)` = c("0.20 (0.16–0.24)", "0.23 (0.21–0.29)"),
  `Relative B10+, median (80% interval)` = c("0.17 (0.14–0.21)", "0.22 (0.19–0.26)")
) |>
  knitr::kable(align = c("r", "r", "r"))
Table 4: Published stock-status comparison in the 2023 assessment (ESC28, Table 4).
Assessment year Relative TRO, median (80% interval) Relative B10+, median (80% interval)
2020 0.20 (0.16–0.24) 0.17 (0.14–0.21)
2023 0.23 (0.21–0.29) 0.22 (0.19–0.26)

Both published medians increased between those rows—about 15% for TRO and 29% for B10+—so the current “rising TRO, flat B10+” contrast was not present there. There is an important caveat: the 2020 row was carried from the 2020 assessment, rather than recalculated as a 2020 state under the 2023 model, and the 2023 paper did not publish a full B10+ trajectory. The table therefore does not prove that the two metrics never diverged internally. The 2026 table makes the issue more visible because it compares 2023 and 2026 states under the same current reference set.

Suggested assessment wording

Between 2023 and 2026, declines in the number of fish aged 10–14 were offset in B10+ by increased abundance at older ages and greater mean weight within the age-10+ group. TRO nevertheless increased because it includes reproductive contribution from fish younger than age 10 and weights older fish much more strongly according to maturity and length-based fecundity. Increases at ages 8–9 and 20–24 therefore produced a substantial increase in TRO while aggregate B10+ remained almost unchanged.

Reproducibility

This note is generated directly from:

  • the accepted 2,000-draw balanced grid state cache, ESC31/runs/grid_mcmc/esc31_grid_dense_mixed_accepted_20260723_failed_cells_1500/grid_combined_state_2000_draws.rds;
  • the centre-cell fit, ESC31/runs/esc31_base.rds; and
  • the current sibling sbt package source used to rebuild and verify the saved objective before extracting the centre-fit report.

No model was refitted and no assessment artifact was altered to produce this diagnostic.