Skip to content

Stryd LBSS v2 Field Setup

methylone edited this page Jun 7, 2026 · 1 revision

Stryd LBSS v2 Field Setup — StrydLBSSv2 & EccLBSS

日本語版: Stryd LBSS v2 Field Setup (日本語)

Since v0.6.0 the LBSS-based PMC defaults to the custom field StrydLBSSv2 (env LBSS_FIELD). Unlike StrydILR / StrydLBSSmod, it is not in the Intervals.icu community search box — you build it on your own account. This page is the copy-paste guide (~15 min). How the formula was identified — and why it reproduces Stryd's native LBSS at 0.0% error — is in LLM Agent Recipes §3.

If you'd rather not build anything, set LBSS_FIELD=StrydLBSSmod and keep using the community field. Everything below is optional.

What you are building

Field Formula (per activity) Anchor Role
StrydLBSSv2 (100/3600) · Σ ILRᵢ / CI 1 h at ILR = CI → 100 Faithful replica of Stryd's native LBSS (β = 1, linear). Source for the LBSS PMC.
EccLBSS (optional) (100/3600) · Σ max(0, (ILRᵢ − CI) / CI) 1 h at ILR = 2×CI → 100 Linear excess above CI — isolates downhill / high-impact seconds that the β = 1 sum dilutes. Flat easy ≈ 0.06–0.16/h vs. hilly ≈ 1.05–2.06/h in our data.

Two fields because one metric can't be both Stryd-faithful and discriminate eccentric overload: β = 1 means the native LBSS deliberately does not overweight impact spikes.

Prerequisites

  • A Stryd pod, with the community StrydILR field added and populating (INSTALL.md → Setting up the Stryd custom fields). Both scripts read the per-second StrydILR stream. Stryd ILR data exists from roughly November 2025 onward.
  • Your Critical Impact (CI) value — Step 1.

Step 1 — Find your CI

CI is your Impact Loading Rate at critical power, in bw/s (the load-side analogue of CP). Best available source first:

  1. You already maintain an ILR@CP value (the community ILR@CP Calculator field used by StrydILRTreshold) → reuse it.
  2. Read Stryd's own per-activity critical_impact with a browser agent (Recipes §2) and take the median over recent weeks. This is the value the formula was calibrated against.
  3. Estimate it from Intervals streams alone by regression (Recipes §4); a deterministic estimate_critical_impact server tool is on the roadmap.

Don't over-tune it: a CI error scales LBSS linearly and uniformly, so your CTL/TSB trends survive a wrong-but-stable CI. Consistency beats precision. (For one athlete we observed ~56–72 bw/s depending on shoes.)

Step 2 — Create the two fields

In Intervals.icu: Settings → Sport Settings → RUN → CUSTOM FIELDS opens the Activity Fields dialog. Click ADD FIELD — the magnifying-glass FIELD button next to it is the community search; that is not the one you want here. The field editor has three tabs (TYPE / DESCRIPTION / SCRIPT):

Setting (TYPE tab) StrydLBSSv2 EccLBSS
Code StrydLBSSv2 EccLBSS
Name Stryd LBSS v2 Ecc LBSS
Type Numeric Numeric
Format .1f .2f

Scripts go in the SCRIPT tab — paste as-is, then replace 57.4 with your CI (same value in both):

// StrydLBSSv2 — Stryd-faithful LBSS replica (beta = 1). Anchor: 1 h at CI = 100
{
  let CI = 57.4; // ← your CI (bw/s); keep identical in EccLBSS
  let s = streams.get("StrydILR").data;
  let tot = 0;
  for (let i = 0; i < s.length; i++) {
    let v = s[i];
    if (v && v > 0) tot += v / CI;
  }
  tot * 100.0 / 3600.0;
}
// EccLBSS — linear excess above CI (downhill / overload discrimination). Anchor: 1 h at 2xCI = 100
{
  let CI = 57.4; // ← same value as StrydLBSSv2
  let s = streams.get("StrydILR").data;
  let tot = 0;
  for (let i = 0; i < s.length; i++) {
    let v = s[i];
    if (v && v > CI) tot += (v - CI) / CI;
  }
  tot * 100.0 / 3600.0;
}

Script-environment pitfalls (each one cost us a failed attempt):

  • The field Code must be CamelCase — underscores are rejected (StrydLBSS_v2StrydLBSSv2).
  • No top-level let/const (script scope is shared; you get "Variable already declared") → the whole script is wrapped in { … }. Keep the braces.
  • A Numeric field must return a number (last expression; strings fail validation).
  • Don't call Object.keys(activity) — host-object enumeration throws. Named access (activity.gear_id etc.) is fine.
  • Activities without a StrydILR stream (rides, pre-Stryd history) just produce no value — harmless, the server treats missing as null.

Recommended Description (DESCRIPTION tab) — make the field self-describing with machine-readable JSON (a planned server feature will auto-discover fields by role; this also documents your parameters for free):

{"icu_role": "lbss", "ver": 2, "formula": "sum(ILR/CI)/36", "ci": 57.4, "beta": 1.0, "calibrated": "2026-06-07"}

(for EccLBSS: {"icu_role": "ecc_lbss", "ver": 1, "formula": "sum(max(0,(ILR-CI)/CI))/36", "ci": 57.4, "calibrated": "2026-06-07"})

Advanced — per-shoe CI table. Scripts can read activity.gear_id, so if your activities have gear assigned (Recipes §6 bootstraps that from Stryd), CI can follow the shoe and re-analysis stays idempotent when CI changes:

{
  let CI_BY_GEAR = { "12345": 57.4, "67890": 69.0 }; // gear_id → CI
  let g = activity.gear_id;
  let CI = (g && CI_BY_GEAR[g]) ? CI_BY_GEAR[g] : 57.4; // fallback: current value
  // … rest identical
}

Step 3 — Backfill history

New activities compute the fields automatically. For existing ones:

  1. Open Activities in list view, select a batch (e.g. one month).
  2. EDIT → Analyze, check "Keep existing intervals", OK.
  3. Repeat per batch — pause between batches; rapid-fire triggers 429 Too Many Requests.

Backfill as far as StrydILR exists (≈ Nov 2025) — the PMC's 42-day EMA needs a few months of history before CTL is trustworthy.

Step 4 — Verify

  • Sanity anchor: an easy flat ~1 h run with average ILR near your CI should score near 100 (more precisely ≈ 100 × hours × avgILR / CI).
  • From the server: get_current_pmc with include_legacy: truelbss (v2) and lbss_legacy (StrydLBSSmod) should both populate, with different values. The scales are not comparable (v2 ran ≈ 1.3× mod for one athlete — don't assume that ratio); never splice v2 CTL onto a mod CTL history.

Maintenance

  • Review CI monthly and on shoe / CP changes. Stryd's own CI is time-varying (we observed it move within 56–61 over three months, and 72→56 across a shoe transition).
  • Editing the constant rewrites history on the next re-analysis — either re-analyze everything so the whole history uses one CI, or use the gear table above and never touch past entries.
  • Keep StrydLBSSv2 and EccLBSS on the same CI at all times.
  • Server side: LBSS_FIELD / LBSS_FIELD_LEGACY / ILR_FIELD env vars, per-call lbss_field override, and include_legacy for a parallel-run period — see INSTALL.md → Optional settings.

Formula and procedures validated against Stryd-native values on real data (single athlete, 2026-06-07; 10-activity calibration set, 0.0% error). Read the numbers as a method demo, not a population guarantee. This project is not affiliated with Stryd or Intervals.icu.

Clone this wiki locally