shly
Methodology · v2.3

State of Health Methodology

Version v2.3 Last updated 2026-05-17 Status Public · audit-replayable Lire en français →

1. What Geotab Data Feed provides

Sohly’s primary distribution is the Geotab Marketplace. Each fleet customer authorizes Sohly as a MyGeotab integration; the relevant telemetry lands in their MyGeotab database and Sohly subscribes to two object types via the Data Feed.

Data point Source Available Quality
State of Charge (SoC) StatusData (DiagnosticId BatteryStateOfChargeId) Yes BMS-reported; ±1–3 % typical
Pack voltage StatusData (PrimaryBatteryVoltageId) Yes BMS-reported; the signal that carries the curve shape
Pack current StatusData (charging-current diagnostic) Yes (most modern EVs) Sign discriminates charge vs discharge
Pack / cell temperature StatusData (battery-temperature diagnostic) Most modern EVs BMS-reported; the physically correct temperature for chemistry correction
Odometer StatusData (OdometerId) Yes Reliable
GPS / location LogRecord Yes 1–60 s cadence depending on device profile
Ignition state LogRecord Yes Discriminates trip vs key-off vs charging
Charge-session boundaries Derived (ignition + charging-current sign) Computed The unit of analysis for the curve regime (§4.1)
Per-session energy added (kWh) Derived (∫ I dt over the session) Computed Replaces Smartcar’s BMW/MINI-only charge/records
Nominal battery capacity OEM spec table keyed by {make, model, year} Static Factory spec — not current degraded capacity
Internal resistance / impedance Estimated from CC-phase voltage intercept (§9.2) Computed Direct degradation signature

What’s different from the consumer-Smartcar regime

Critical caveats


2. Why a single range/SoC snapshot is not enough

The original consumer-Smartcar formula was:

SoH = Range / (SoC × NominalRange)

Equivalent to: EstimatedFullRange / NominalRange. It looks plausible but has a fatal signal-to-noise problem. The physics is the same under Geotab — the difference is that Geotab streams the curve, so Sohly does not depend on this formula. This section is retained because it justifies why the Curve regime exists at all (§4.1, §9) and why the Snapshot regime (§4.2) needs multi-sample averaging and temperature filtering to be defensible.

Confounders and their measured magnitudes

Factor Range impact Source
Temperature: -7°C with heating -41% AAA testing
Temperature: -18°C urban -50 to -59% U.S. DOE 2024
Temperature: +35°C with AC -17% Consumer Reports
Speed: 110 km/h vs 105 km/h -25–30% vs EPA estimate Geotab
Driving style: aggressive vs smooth -10–30% field data
Terrain: uphill loaded vs flat -20–40% field data
HVAC without heat pump -41% DOE
Cold battery (not warmed up) -10–15% BMS studies

Composite worst case: a healthy battery on a cold day at high speed can read 60–70% of nominal range. The formula would output SoH ≈ 65% — a massive false positive.

Composite best case: a healthy battery on a mild spring day in city traffic with regen can read 110%+ of nominal range. The formula outputs SoH > 100% — physically uninterpretable.

The core problem


3. What the science says

Geotab (22,700 EVs, multi-year): compute SoH from energy in/out across thousands of sessions — never from a single snapshot.

Recurrent Auto: longitudinal fleet comparison, multiple readings per day, thousands of peer vehicles per climate zone. Their own docs state a single reading can move due to driving style or a highway trip — without any cell degradation.

Nature Communications (2025): 300 EVs, 3 years, 1.2M charging snippets, deep learning multi-modal approach — meaningful SoH only through longitudinal large-scale data.

IEEE/SAE consensus: OCV-based methods require 2+ hours of rest. Coulomb counting requires many cycles. ML models need voltage curves, current, temperature, and many charging cycles.

Legal standards (ISO 12405, SAE J2950, UL 1974): all require full cycle testing, impedance measurement, or sustained monitoring. No standard recognises range/SoC snapshot estimation as sufficient for certification.


4. Data regimes — automatic, per session

A vehicle on the Sohly platform does not pick a “tier” — each individual session (or each scoring window) is classified by a regime gate based on what the data feed actually delivered. The customer-facing product is the same; the methodology backing each prediction is regime-dependent and labelled in the report.

The three regimes, in increasing data quality:

4.1 Curve regime (preferred) — full charging-curve ML

Trigger: a charging session produced ≥0.1 Hz pack voltage + pack current samples, ≥20 % SoC delta, ≥10 minutes duration, and no ignition interruption.

Method: the five physics-derived features from §9.2 (CC slope, time-to-CV, CV taper time-constant, internal-resistance estimate, peak temperature rise) feed three independent LightGBM quantile boosters; the conformalized-quantile-regression calibrator widens the output to guaranteed coverage (§9.4).

Output: P05/P50/P95 SoH triple plus a risk band (LOW / MEDIUM / HIGH per §9.6). Accuracy: mean interval width 1.73 pp SoH on the validation set (§9.5). Tight enough to underwrite residual-value pricing. Cadence: once per qualifying charging session — typically daily or every other day for an active fleet vehicle.

4.2 Snapshot regime (fallback) — continuous multi-sample averaging

Trigger: the feed delivered SoC, odometer, and (ideally) BMS pack temperature, but the per-session voltage/current density was insufficient for the Curve regime — for example, an OEM whose StatusData only publishes SoC on ignition events, or a charging session with <10 minutes of bus activity.

Method: 1. Collect ≥10 SoC + range snapshots over a rolling window (no fixed 14-day cap — Geotab vehicles stream continuously, so the window slides until enough qualifying points accumulate). 2. Apply temperature filtering using the BMS pack temperature when available, otherwise the GPS-keyed Open-Meteo lookup (§7, §7bis). 3. Discard outliers (>2 σ). 4. Average the temperature-filtered range against the OEM nominal range table. 5. Cross-reference against the cross-fleet cohort (§8).

Output: Range Score with a confidence band that depends on whether BMS temperature was available (band tightens when it is). No CQR guarantee — the report carries an explicit regime = snapshot flag and the residual-value pipeline (§11) treats P05 as score − band/2, not as a calibrated quantile.

4.3 Cold-start regime — cohort prior only

Trigger: insufficient data for either of the above (< 3 qualifying sessions of any kind, vehicle just joined the fleet, or the cross-fleet cohort has < 10 peers in this {make, model, year} band).

Method: the Geotab-published 2.3 %/year linear degradation curve (§8) is the prior, conditioned only on age and the static OEM capacity table. No vehicle-specific signal is used.

Output: a point estimate framed as “expected SoH for a vehicle of this age, no vehicle-specific data yet”. Percentile is suppressed. The pricing pipeline (§11) widens the residual-value band substantially to absorb the absence of evidence.

Transitions and report labelling

A vehicle’s regime is per-session, not per-vehicle. A given vehicle can land in Cold-start on Monday, Curve on Tuesday’s charging session, and Snapshot on Wednesday if a partial bus dropout truncated the curve. Sohly’s pricing layer aggregates across the latest N sessions per vehicle, weighting Curve evidence more heavily than Snapshot, and falling back to the prior when the running aggregate is still thin.

Every certificate / report carries a regime field, plus the count of sessions in each regime that contributed to the displayed SoH range. Auditors see what produced each number.


The product surface is continuous SoH telemetry, not a one-shot certificate, and the contract is at the fleet level, not per vehicle.

Customer-facing onboarding

  1. Fleet customer installs Sohly from the Geotab Marketplace and approves the MyGeotab add-in’s data scopes (StatusData, LogRecord for the diagnostics listed in §1).
  2. A Sohly account is provisioned for the fleet. Existing vehicles backfill from the customer’s MyGeotab database (typically 90 days of history available depending on retention plan); new vehicles begin streaming on first ignition.
  3. Per-vehicle scoring begins in the Cold-start regime (§4.3); each vehicle transitions automatically to Snapshot or Curve as the feed produces qualifying data.
  4. A fleet dashboard surfaces per-vehicle SoH ranges, residual-value forecasts (§11), and the “sell these first” ranking. The deterministic JSON feed is the canonical product; PDF reports and the LLM-written paragraph (§11.6) are optional outputs.

No Stripe consumer checkout. No per-vehicle OAuth. No 14-day passive-collection clock. Billing is per vehicle-year per the contract, invoiced to the fleet.

Data Processing Agreement (DPA)

The fleet’s DPA with Sohly establishes:

Why this replaces the consumer 14-day passive-collection flow

The 14-day window only existed to gather enough Smartcar reads over varied weather and SoC to fight signal-to-noise in the snapshot formula. The Geotab feed dissolves that constraint: a fleet vehicle produces qualifying data continuously, and the regime gate (§4) moves it to the higher-quality methodology as soon as the data supports it. There is no point in time at which “the score is final” — the score is the running aggregate of the latest qualifying sessions, refreshed as new sessions land.


6. Impact on product & marketing

Sohly is sold to leasers, insurers, fleet operators, and used-EV marketplaces (see STRATEGY.md §4 for the ranked segments). The marketing copy must match the methodology and the contract — not a consumer-grade certificate, not a single-number SoH claim.

Don’t say Do say
“Battery Health Certificate” “Continuous SoH telemetry” / “Battery Health Data Feed”
“State of Health: 92 %” “P05/P50/P95 SoH triple, conformal 90 % coverage”
“Certified by Sohly” “Independently produced, methodology-transparent SoH range with audit-replayable provenance”
“Battery Range Score” “Per-vehicle residual-value forecast at 3/6/12/24-month horizons”

The optional per-vehicle PDF report exists for end-of-lease handover paperwork, used-EV listing pages, and warranty-claim packaging — but it is a thin output of the feed, not the product. The product is the deterministic JSON feed, the dashboard, and the fleet-ranked “sell these first” list.

Why “feed” beats “certificate” in this segment

A leasing or insurance customer underwrites portfolios, not vehicles one by one. They consume the feed programmatically, run it through their own pricing models, and produce decisions across thousands of vehicles. A static PDF certificate per vehicle is friction, not value. The PDF survives as a regulatory artifact (EU Battery Passport readiness — see BACKLOG.md [REGULATORY]) and a customer-facing handover document, not as the unit of sale.


7. Temperature data — BMS first, Open-Meteo fallback

Temperature is a first-order driver of both range estimates and battery chemistry. Sohly uses the best available source per session, in this preference order:

  1. BMS pack temperature from StatusData (the battery-temperature diagnostic, available on most modern EVs). This is the physically correct input — it measures the cells themselves, not the air outside. Used directly in the temperature filter (§7bis) and as a feature in the Curve regime (§9.2, peak_temp_rise_c).
  2. Ambient temperature from Open-Meteo, GPS-keyed via LogRecord coordinates at the session’s start timestamp. Used only when the BMS diagnostic is absent or sparse for the session.

Both are recorded in the per-session feature row so that an audit can trace which source fed the filter and the model.

Open-Meteo plan, endpoint, attribution

Vendor: Open-Meteo, commercial license — API Standard plan (€29/mo HT, 1M calls/mo, Switzerland-based, GDPR adequacy region). Required even though the BMS path covers most sessions: Sohly is a commercial product per Open-Meteo’s TOS the moment it has paying customers.

Volume sanity check: at fleet-pilot scale (≈500 vehicles × ≈1 charging session/day × ≈30 % BMS-missing rate × ≈1 fractional call per session) we consume ≈4,500 calls/mo — still <0.5 % of the Standard tier. Re-cost when monthly volume crosses ~500k calls (typical only above ~50k continuously monitored vehicles with BMS-missing-rate held constant).

VAT handling: Open-Meteo invoices via Stripe. Provide Sohly’s FR VAT ID at billing to trigger reverse-charge — otherwise 20 % TVA is added (€34.80/mo all-in).

Endpoint: GET https://customer-archive-api.open-meteo.com/v1/archive with apikey query param.

Dataset: Météo-France AROME (1.3 km resolution) for sessions in metropolitan France, included in the Standard plan. ERA5-Land (~9 km, 5-day lag) is the fallback outside AROME coverage. The 5-day lag is acceptable — Sohly aggregates over rolling windows of multiple sessions, so individual session lookups can wait for ERA5 finalization without blocking customer-facing reports.

Request shape per session-with-BMS-missing:

?latitude={lat}&longitude={lon}
&start_date={session_date}&end_date={session_date}
&hourly=temperature_2m
&timezone=auto
&apikey={secret}

Location source: GPS coordinates from LogRecord at the session’s first timestamp, rounded to 4 decimal places (≈11 m precision — sufficient for ambient temperature, coarse enough to share cache hits across a depot).

Caching: GCS object keyed by gs://sohly-prod-data/weather_cache/{lat_4dp}/{lon_4dp}/{date}.json. Immutable once written (ERA5 finalized after 5 days). Fleet vehicles often charge at the same depot day after day, so cache hit-rate is materially higher than the consumer case — most of the per-day depot lookups are free after the first call.

Attribution: CC-BY 4.0 — required attribution in the PDF report footer when Open-Meteo data contributed to that vehicle’s score: “Weather data: Open-Meteo.com”.

Region: France-first launch. AROME (Météo-France) gives 1.3 km resolution across metropolitan France. No special regional endpoint required — Open-Meteo selects the model per request coordinates.

Temperature correction curve: see §7bis below.


7bis. Temperature correction strategy

The AAA/DOE figures in §2 bundle three unrelated effects — battery chemistry slowdown, cabin heating load, and cold rolling resistance. Applying them as a single multiplicative correction would inflate range estimates on cold days and mask real degradation. The methodology requires honest uncertainty, not laundered precision. This section applies only to the Snapshot regime (§4.2). The Curve regime (§4.1, §9) consumes pack temperature directly as a feature and needs no correction layer.

Filter-first, correct only as fallback

Primary path — filter, do not correct. For each candidate snapshot, read its temperature input (BMS pack temperature when available per §7, else Open-Meteo ambient). Drop the snapshot when T < 5 °C or T > 30 °C. Average the remaining snapshots against OEM nominal range. The filter is conservative in both directions — under the Curve regime we never enter this branch, so the cost of throwing away cold/hot snapshots is only that the Snapshot regime takes a longer rolling window to accumulate qualifying points.

Fallback — soft correction, only when fewer than 5 snapshots pass the filter in the running window. Apply a linear battery-chemistry correction to the filtered-out cold snapshots to recover them:

if T < 5°C:
    correction_factor = 1 + min(0.01 × (5 - T), 0.25)   # +1% per °C below 5°C, capped at +25%
    range_corrected = range_observed × correction_factor

This curve is calibrated against Recurrent / Geotab published SoH-vs-temperature data (battery capacity loss of ~5–8 % at 0 °C, ~15 % at -20 °C), not AAA headline range numbers (which include accessories). It corrects less than the AAA figures would suggest, on purpose — under-correcting is the safer error.

No hot-side fallback. Above 30 °C is rare in France and the AC-load confound is not separable from chemistry effects with the data we have.

Confidence reporting (Snapshot regime)

Scenario temperature_corrected flag Temperature source Confidence band
≥5 snapshots in 5–30 °C window, BMS temp available false bms ±12
≥5 snapshots in 5–30 °C window, Open-Meteo only false open_meteo ±15
<5 in window, fallback applied true mixed ±20
<5 even after fallback false n/a regime falls back to Cold-start (§4.3)

BMS-temperature inputs tighten the confidence band by 3 pp because they remove the ambient-vs-cell-temperature confounder.

Product implication

In a fleet streaming continuously, the “winter vehicle stuck in fallback” problem largely disappears: the rolling window slides past Jan–Feb and accumulates qualifying spring/summer snapshots. The trade is now “first-report latency in winter” rather than “score quality in winter” — a vehicle joining the platform in January may report Cold-start for longer than one joining in May.

What this is not

This is not a real SoH temperature model. It is a defensive filter that preserves the integrity of the multi-sample average in the Snapshot regime. The Curve regime (§9) replaces this section with first-principles physics — temperature is a model feature, not a filter knob.


8. Cohort cold-start strategy

Percentile reporting (“this vehicle is in the top 35 % of MG MG4 2023 peers”) needs a peer cohort. The cohort lookup fails until enough vehicles of the same {make, model, year} have been measured. This section defines the cold-start fallback and the policy for transitioning to a real cohort.

Cohort scope — pooled across all Sohly fleet customers

Sohly’s cohort is pooled across the entire customer base, not built per fleet. A single fleet of 200 vehicles cannot produce a credible peer percentile for a Renault Megane E-Tech 2023 if it only operates two such vehicles; pooling across customers gets the per-{make, model, year} cohort to N ≥ 10 within months instead of years.

The pool stores anonymized snapshots: {make, model, year, age_bucket, mileage_bucket, soh_quantile_triple, regime, capture_month} — no VIN, no GPS, no customer identifier. Customers consent to anonymized contribution by default at fleet onboarding; opt-out is documented in §5 and only narrows that customer’s own percentile reporting (their per-vehicle SoH measurement is unaffected). Sohly never resurfaces a contributor’s data to another customer at row level — only as a percentile over the pooled distribution.

What the “Geotab prior” is

Geotab published a 2020 study of 22,700 EVs across multiple years with a headline finding: EV batteries degrade ≈2.3 % per year on average (public, peer-reviewed). That gives a degradation curve any car’s expected SoH can be plotted against: a 3-year-old EV ≈ 93 %, a 5-year-old ≈ 88 %, a 10-year-old ≈ 77 %.

A Bayesian prior is a starting belief used before the vehicle-specific data arrives. Here:

In the absence of real peer data, assume this car should be at 100 % − (2.3 % × age_in_years) of nominal.

Sohly starts with this published belief and shifts toward the pooled cohort as it grows.

Policy

The N = 10 threshold and the linear 2.3 %/year curve are working assumptions — revisit if percentiles look unstable in production, or if a model-specific degradation curve becomes available.

Why the prior gets dropped at N = 10

Geotab’s 2.3 %/year is an average across all EVs. The Sohly cohort is specific to one {make, model, year}. Once the specific cohort is stable, it is more informative than the broad average — the prior has served its purpose.


9. Curve-derived ML methodology — quantile regression + conformal prediction

Status: working pipeline (synthetic CC-CV data), wired end-to-end with empirical validation in voltcheck-api/ml/. Real-fleet validation pending Geotab Data Feed integration (BACKLOG [VENDOR/GEOTAB]).

This section specifies the methodology behind the Curve regime (§4.1). When the Geotab Data Feed delivers pack voltage + current at ≥0.1 Hz during a charging session — together with the BMS pack temperature where available (§7) — Sohly extracts five physics-derived features and feeds them into a conformalized quantile-regression pipeline. The Snapshot regime (§4.2) is the graceful fallback when curve density is insufficient. Direct OEM streams (under EU Data Act Article 5, post Sept 2026) are an interchangeable substitute for the Geotab feed at the input boundary. This is the methodology Sohly publishes on the Geotab Marketplace and against which fleet, leasing, and insurance customers underwrite decisions.

9.1 What changes when we have the full charging curve

A CC-CV charging session (the standard Li-ion fast-charge protocol) has two phases: - Constant-Current (CC): the charger holds current fixed; terminal voltage rises as State of Charge and internal resistance both climb. - Constant-Voltage (CV): when terminal voltage reaches the upper limit, the charger pins voltage and current tapers exponentially until cutoff.

State of Health drives the shape of this curve via two coupled effects: 1. Usable capacity shrinks with age — the same charger fills a 70%-SoH pack faster than a 100%-SoH pack, so the voltage curve is steeper. 2. Internal resistance grows — a 70%-SoH pack has roughly 1.8× the internal resistance of a new pack, which lifts the terminal-voltage baseline (by I × R_int) and brings the CV transition forward to a lower SoC.

Aggregate features that a snapshot pipeline can extract (“total energy added”, “charge duration”) throw away nearly all of this signal. The full curve preserves it.

9.2 Five physics-derived features

The feature extractor (voltcheck-api/ml/feature_extraction.py) computes five scalar features per charging session:

Feature What it measures Direction with aging
cc_slope_v_per_min Rate of voltage rise during the CC phase ↑ (smaller capacity → faster SoC rise → steeper dV/dt)
time_to_cv_min Minutes from charge start to CV transition ↓ (higher R_int hits v_max sooner)
cv_taper_tau_min Exponential decay time-constant of CV current ↑ (τ ∝ R_int × capacity; the product grows with age)
internal_resistance_est_ohm R_int estimated from the CC-phase voltage intercept ↑ (direct degradation signature)
peak_temp_rise_c Peak cell temperature minus baseline ↑ (aged packs dissipate more I²R heat)

Each feature is a direct electrochemical signature of degradation, not a proxy. Smoke-testing on simulated curves at SoH ∈ {100, 90, 80, 70}% shows all five features change monotonically in the physically expected direction across the 70–100% range.

9.3 Why we publish a range, not a single number

A point estimate (“SoH = 87%”) sounds precise. It is misleading. The Geotab fleet study’s headline 2.3%/year degradation is an average; individual packs deviate by ±5 percentage points around that line at any given age. Driving style, climate zone, depth-of-discharge habits, and DC-fast-charge frequency all matter, and none of them are summarised by a single number.

Sohly publishes three numbers, fit by independent LightGBM quantile-regression boosters at quantiles 0.05, 0.50, and 0.95: - P50 — median estimate. The most likely SoH given the inputs. - P05 — pessimistic bound. True SoH is at least this high with 95% confidence. - P95 — optimistic bound. True SoH is no higher than this with 95% confidence.

The interval [P05, P95] is the actionable input for downstream decisions: - Residual-value pricing uses the P05 (worst-case capacity). - End-of-policy underwriting uses the interval width as the uncertainty premium. - Operational decisions (replacement scheduling, fleet rotation) use the P50.

9.4 Why the range can be trusted: conformal prediction

Quantile regression alone gives only marginal-in-expectation coverage: across a population of held-out tests, roughly 90% of true SoH values fall inside [P05, P95]. There is no finite-sample guarantee — and “roughly 90%” is not a number an insurer can underwrite against.

Conformalized Quantile Regression (Romano, Patterson, Candès, NeurIPS 2019) wraps the quantile estimator and provides a distribution-free coverage guarantee:

For any data distribution, given an i.i.d. calibration set, the calibrated interval contains the true SoH with marginal probability ≥ 90%.

Mechanically: 1. The training corpus is split 80/20 into a training slice and a held-out calibration slice. 2. The three quantile boosters are fit on the training slice only. 3. On the held-out calibration slice we compute the nonconformity score for each example — how much the true SoH exceeds the predicted P95 or falls below the predicted P05. 4. We take the empirically-90% (finite-sample-corrected) quantile of those scores: this is q_hat, a single scalar. 5. At inference, every interval is widened by q_hat on both sides: [P05 − q_hat, P95 + q_hat].

The guarantee comes from a single-page theorem about exchangeable sequences (Vovk, Gammerman & Shafer, Algorithmic Learning in a Random World, 2005; Romano et al. 2019 for the quantile-regression variant). There is no Gaussian assumption, no parametric model of the residuals, no asymptotic argument. The guarantee holds in finite samples, on any data distribution, on any base predictor.

9.5 What the empirical numbers look like

Validation on a held-out set of 2,000 simulated CC-CV sessions, ambient temperature distribution matched to the training prior (seed=2026, target 90% coverage, calibrator n=400):

Metric Value Reference
Empirical 90% interval coverage 88.1% ± 1.4% Target 90% (95% CI [86.7%, 89.5%] includes target); acceptance floor ≥87% per AI_ML_ROADMAP §6
Median absolute error of P50 0.05 pp SoH Point estimate is essentially unbiased
Mean interval width (P95 − P05) 1.73 pp SoH Tight enough for residual-value pricing without manual review
P50 above true (well-centered if ≈50%) 47% No systematic bias

The 1.73 pp mean spread compares favourably to the AAA / U.S. DOE single-snapshot range-noise figures of ±30–50% (see §2): an actuarial team can price residuals from a 2 pp interval; they cannot from a 50 pp interval.

9.6 Surfacing the limit of the guarantee: risk band

The CQR coverage guarantee is conditional on exchangeability — the test data must come from the same distribution as the calibration set. When a fleet operates in a temperature or driving-style regime underrepresented in the calibration data, coverage drops and the interval widens.

Every Sohly prediction includes a categorical risk_band field derived from the interval width:

Band Interval width Recommended action
LOW < 3 pp SoH Use the P50 directly for operational decisions; price residuals at P05.
MEDIUM 3–8 pp SoH Manual review or additional data pull before high-stakes decisions.
HIGH ≥ 8 pp SoH Do not auto-price. Pull more telemetry; re-run; flag for engineering.

Under in-distribution test data, ~97% of predictions fall in LOW. Under heavy out-of-distribution stress (60% extreme-temperature curves, double the training rate), the HIGH-band rate climbs to ~7% and overall coverage drops to 79.5%. This is by design: the risk band is the model’s self-reported uncertainty signal, and it gets louder when the model’s assumptions break down. An underwriter should treat a HIGH band the way they treat a flagged claim — work the case manually, do not auto-price.

9.7 Auditability and reproducibility

Every Sohly certificate is reproducible:

This is the opposite of the black-box AI pitch many incumbents lean on. We expose the math because actuarial and risk teams cannot underwrite anything they cannot audit.


10. Plain-language summary

Reusable ~200-word block for slide decks, landing-page sections, incubator submissions, and partner conversations. Audience: technically literate but not ML-specialist — fleet manager, lease portfolio analyst, insurance underwriter, incubator programme director.

How Sohly produces a battery State-of-Health number you can underwrite against.

Sohly never publishes a single SoH percentage. It publishes a three-point range: a median estimate (P50), a pessimistic lower bound (P05), and an optimistic upper bound (P95). The bounds come from independent quantile-regression models trained on five physics-derived features extracted from each vehicle’s charging curves — voltage slope during constant-current, time-to-CV transition, tapering time-constant, internal-resistance estimate, peak temperature rise. These features are direct electrochemical signatures of cell degradation, not proxies.

The interval is then calibrated using Conformalized Quantile Regression (Romano, Patterson & Candès, NeurIPS 2019), a method with a distribution-free coverage guarantee: on held-out data the 90% interval contains the true SoH at least 90% of the time, regardless of the underlying data distribution. On the Sohly validation set (2,000 simulated charging sessions, in-distribution), empirical coverage is 88.1% ± 1.4%, median absolute error of the point estimate is 0.05 percentage points, and mean interval width is 1.73 pp SoH — tight enough to price residuals directly.

Every prediction also carries a risk band — LOW, MEDIUM, or HIGH — that flags when interval width grows beyond what’s safe for unattended use. Underwrite the LOW band, manually review the MEDIUM band, never auto-price the HIGH band. The methodology, the algorithm references, and the calibration artefacts are public; only the fleet-specific calibration data is proprietary.


11. Risk pricing — from SoH quantiles to fleet financial decisions

Status: design locked 2026-05-16, implementation pending (BACKLOG [PRICING], [NARRATOR]). This section is the methodology fleet managers and incubator reviewers should evaluate before underwriting any pricing decision Sohly produces.

The SoH quantile interval from §9 is engineering output. It does not tell a fleet manager what to do. Section 11 is the translation layer: SoH quantiles → degradation forecast → residual value in €€€ → action recommendation (HOLD / SELL_NOW / SELL_BEFORE).

11.1 Why we publish € rather than only SoH %

A fleet manager does not buy SoH numbers — they buy arbitrage decisions. The actionable question is “if I hold this vehicle for six more months instead of selling it today, what is the financial downside?” The answer is in euros, not percentage points.

Conformal quantile regression (§9.4) provides exactly the inputs this question needs: - P05 answers “how bad is the worst plausible case?” — the downside an insurer or treasurer prices against. - P50 answers “what is the most likely outcome?” — the operational planning input. - P95 answers “how much upside am I leaving on the table by selling early?” — the opportunity-cost input.

The residual-value function RV(soh, age, mileage) is monotonic in SoH, so the SoH quantile triple pushes through cleanly into a residual-value quantile triple. The coverage guarantee from §9.4 is preserved end-to-end: if the 90 % SoH interval covers the truth 90 % of the time, so does the 90 % residual-value interval.

11.2 Pipeline overview

┌──────────────────────┐    ┌──────────────────────┐    ┌──────────────────────┐
│  SoH history         │ →  │  forecast_soh()      │ →  │  Forecast SoH        │
│  (longitudinal       │    │  per-vehicle slope   │    │  at horizons         │
│   from Geotab        │    │  OR cohort prior     │    │  3/6/12/24 months    │
│   Marketplace,       │    └──────────────────────┘    │  with P05/P50/P95    │
│   or single snapshot)│                                └──────────┬───────────┘
└──────────────────────┘                                           │
                                                                   ▼
┌──────────────────────┐    ┌──────────────────────┐    ┌──────────────────────┐
│  Vehicle metadata    │ →  │  rv()                │ ←  │  push SoH quantiles  │
│  (make/model/year/   │    │  baseline × g(soh)   │    │  through monotonic   │
│   mileage/MSRP)      │    │  warranty cliff      │    │  RV()                │
└──────────────────────┘    └──────────┬───────────┘    └──────────────────────┘
                                       │
                                       ▼
                            ┌──────────────────────┐    ┌──────────────────────┐
                            │  decide()            │ →  │  VehiclePricingReport│
                            │  HOLD / SELL_NOW /   │    │  (structured)        │
                            │  SELL_BEFORE         │    │                      │
                            └──────────────────────┘    └──────────┬───────────┘
                                                                   │
                                                                   ▼
                                                        ┌──────────────────────┐
                                                        │  NarratorSkill (LLM) │
                                                        │  paragraph, FR/EN,   │
                                                        │  numbers verbatim    │
                                                        │  from the report     │
                                                        └──────────────────────┘

Each box is a pure function. The output of every stage is reproducible from the input plus a versioned code path. No state, no hidden inputs, no sampled values until the optional NarratorSkill stage — and the narrator is constrained to cite only the structured report (see §11.6).

11.3 Forecasting SoH with longitudinal data

In a Geotab Marketplace deployment, a vehicle accumulates multiple SoH snapshots over time as charging sessions land in the data feed. Two regimes:

Per-vehicle slope (≥3 snapshots). Fit ordinary least squares on (captured_at, soh_p50). The fitted slope μ_dot is the vehicle’s measured degradation rate; the standard error of the slope quantifies how much we trust it. Project all three quantile lines forward independently to each target horizon:

soh_q(horizon) = soh_q(today) + μ_dot × (horizon − today)
                                where q ∈ {p05, p50, p95}

Widen the band by slope_se × horizon_years to absorb trend-extrapolation uncertainty. This is the regime Geotab Marketplace puts us in for any vehicle that has been streaming for more than a few weeks.

Cohort prior (<3 snapshots). Fall back to the published Geotab 2.3 %/year linear degradation curve (already used as the cold-start prior in §8). The forecast carries an additional fixed uncertainty term that grows with horizon — we are extrapolating from a population average, not from this vehicle’s own trajectory.

Known V0 limitation: the snapshot CQR guarantee (§9.4) is exchangeability-based. Projecting it forward by linear regression does not preserve the coverage guarantee at the forecast horizon — the i.i.d. assumption breaks across the time axis. V1 will re-calibrate CQR on historical (t, t+Δ) pairs to restore a horizon-aware coverage claim. V0 ships with the simpler propagation and the limitation is disclosed in every report.

11.4 Residual value model — baseline curve and warranty cliff

RV(soh, age_months, mileage_km, msrp) = msrp × baseline(age, mileage) × g(soh)

Two factors, both transparent and replaceable.

baseline(age, mileage) — published depreciation curve, parametric:

baseline(age_months, mileage_km) = exp(−α · age_months / 12) · exp(−β · mileage_km / 10_000)

with α ≈ 0.18 (≈ 17 % first-year depreciation, tapering) and β ≈ 0.025. V0 constants are sourced from public Geotab data and Autobiz’s monthly French resale index. V1 swaps these for paid market data (Argus / La Centrale / Indicata) when a customer demands market-grade figures — the function signature is unchanged.

g(soh) — piecewise SoH penalty with the warranty cliff:

SoH band Multiplier Rationale
≥ 85 % 1.00 No buyer concern; range matches expectations
75 – 85 % 1.00 → 0.92 linear Mild range concern
70 – 75 % 0.92 → 0.70 steep Warranty proximity — buyers price in lost coverage
< 70 % 0.70 × (soh / 70) Replacement-cost regime — pack swap is a real risk

The kink at 70 % SoH is the OEM warranty floor for the dominant European EV makers (Renault, Stellantis, Tesla). When the P05 of a vehicle’s SoH forecast crosses this threshold, the lower bound of the residual-value forecast drops sharply — that drop is the arbitrage signal the fleet manager needs.

These constants are placeholders pending validation. They will be revisited per make once a B2B pilot accumulates enough resale data to calibrate the kink. The piecewise structure does not change; only the breakpoints and multipliers do.

11.5 Decision rules — HOLD, SELL_NOW, SELL_BEFORE

The decide() function returns one of three actions plus a rationale. Two parameters control the threshold:

The logic walks each forecast horizon in order:

for horizon T in [3, 6, 12, 24] months:
    if rv_p05(T) < rv_today × (1 − tolerance):
        return SELL_NOW
              rationale = "P05 RV at {T} months is {x} € below today's RV"
              eur_at_risk = rv_today − rv_p05(T)
    if forecast P50 SoH crosses 70 % warranty floor within T:
        crossing_date = solve(soh_p50(t) == 70)
        return SELL_BEFORE(crossing_date − 30 days)
              rationale = "P50 SoH crosses 70 % warranty floor on {crossing_date}"

if no horizon triggers:
    return HOLD
        rationale = "All horizons within {tolerance}% of today's RV"

The rationale string is deterministic — generated from which branch fired and which numbers tripped the threshold. It contains the same € figures that the structured report carries. The optional LLM narrator (§11.6) only rephrases this rationale for tone and language; it does not regenerate the numbers.

11.6 Deterministic engine + LLM narrator — the audit contract

A fleet manager looking at 200 vehicles will not read 200 JSON blobs. They will read 200 short paragraphs. The paragraphs are the product surface. So Sohly produces them — but with a hard constraint that keeps the conformal coverage guarantee intact end-to-end.

The split:

Layer What it produces How it produces it Auditability
PricingSkill (deterministic) All numbers, all dates, the action label Versioned pure functions of structured input Re-run → byte-identical
NarratorSkill (LLM, Claude Haiku) The fleet-manager-facing paragraph Prompt-constrained generation over the structured report LLM may not invent any figure absent from the input

The narrator’s system prompt enforces the rule:

“Tu es un analyste financier expert en dépréciation de flottes automobiles. Tu reçois un rapport structuré (quantiles SoH, RV projetée P05/P50/P95, action recommandée, dates clés, montant à risque). Rédige un paragraphe d’action directe pour le gestionnaire de flotte. Règle absolue : n’invente aucun chiffre — n’utilise que les valeurs présentes dans le rapport, citées au centime près. Ton : direct, sans hedging, max 4 phrases.”

The rule is enforced post-generation by a regex-based check: every euro figure, percentage, and date in the paragraph must appear verbatim in the structured input. Paragraphs that fail the check are rejected and replaced by a deterministic template fallback.

Why the split matters:

The constraint check is also a regression-safety net: if a future prompt change accidentally lets the model paraphrase a number (“about €2,300” vs the exact “€2,317”), the check catches it and the deterministic fallback ships instead.


12. Plain-language summary — risk pricing

Reusable ~200-word block for slide decks, landing-page sections, incubator submissions, and partner conversations with fleet, leasing, and insurance audiences.

How Sohly turns a battery State-of-Health interval into a fleet pricing decision you can act on.

Sohly’s calibrated SoH range (P05 / P50 / P95) is the input, not the product. The product is a per-vehicle financial recommendation — HOLD, SELL NOW, or SELL BEFORE a given date — backed by a residual-value forecast in euros at horizons of 3, 6, 12, and 24 months. The forecast pipeline is fully deterministic: a published baseline depreciation curve is multiplied by a piecewise SoH penalty with a kink at the 70 % warranty floor, the point at which a used-EV buyer prices in the cost of a future pack replacement. The SoH quantiles are pushed through monotonically, so the conformal coverage guarantee from the SoH model carries through to the euro figures.

For each vehicle the report ranks the downside (rv_today − rv_p05_horizon) against today’s residual value; the fleet view returns vehicles sorted by euros at risk, so the manager sees the “sell these first” list rather than 200 individual PDFs. The accompanying paragraph is written by a small LLM (Claude Haiku) under a strict no-invent-numbers constraint — every figure in the prose comes verbatim from the structured report, and a post-generation check enforces it. The euro recommendation, the action label, and the underlying SoH interval are all reproducible, versioned, and auditable. The methodology, the algorithm references, and the depreciation curve constants are public.