averagejoematt method

the method · under the hood

The Methods Registry

Every statistic this platform publishes — its formula, the window it runs over, and what it can't tell you — generated straight from the source modules below, not hand-written. If the code changes, this page is built to go stale until a human re-verifies it (see the fingerprint note).

Where this comes from

The modules below back every entry here. The statistical cores are pure, deterministic, stdlib-only Python with no AI and no I/O (ADR-105's "deterministic computation before any LLM verdict" rule, made literal); where an entry documents the declared scope of an LLM-assisted layer, the registered function is that pure policy itself — fingerprinted the same way.

stats_core.py — lambdas/common/stats_core.py

The one sanctioned statistics module (ADR-105, story #529) — pure, stdlib-only, deterministic. Replaced three divergent Pearson-r implementations and two p-value copies that used to disagree with each other.

calibration_core.py — lambdas/experiment/calibration_core.py

The one prediction-calibration scorer (#538, ADR-105) — grades every forecast the platform makes against what actually happened, so the public calibration scoreboard, /api/coach_team, and the coach track-record tool all read the same numbers.

conversation_enrichment.py — lambdas/ai/conversation_enrichment.py

The conversational-capture enrichment layer (#1577) — coach check-in answers, habit reflections, and field-note responses coded by the same LLM enrichment pass as journal text. The entry registered here is its pure, deterministic SCOPE POLICY: these signals are ANALYSIS-ONLY in v1 — they seed hypothesis candidates and are visible as context, but they do not move character or flourishing scoring.

Correlation

5 stats

Pearson correlation (r)

r = Σ(x-x̄)(y-ȳ) / √(Σ(x-x̄)² · Σ(y-ȳ)²), clamped to [-1, 1]

Window
Whatever paired series the caller passes in — the function has no lookback of its own. The platform's primary caller, the weekly correlation compute, passes a 90-day rolling window (LOOKBACK_DAYS).
Limitations
Requires n ≥ min_n (default 3, callers commonly raise this to 5); returns None below that threshold or when either series has zero variance. Measures linear association only — a real non-linear relationship can score near zero. Not corrected for day-to-day autocorrelation on its own (see effective_sample_size) — a raw r without the effective-n-based p-value/CI overstates confidence on daily physiological series.
Minimum n
3
Used by
The correlation engine (/method/intelligence/), tools_training zone-2 and exercise-efficiency correlations, get_cross_source_correlation.

source: stats_core.py::pearson_r

Lag-1 autocorrelation

r1 = Σ(xₜ-x̄)(xₜ₊₁-x̄) / Σ(xₜ-x̄)², clamped to [-1, 1]

Window
Caller-supplied series, in the order given — treats the series as one continuous sequence with no gap-awareness (a missing day is not distinguished from a lag of 1).
Limitations
Returns 0.0 (no detectable memory) below n=3 or zero variance, which makes the downstream effective-n correction a no-op rather than an error. A helper, not a reported statistic on its own — it feeds effective_sample_size.

source: stats_core.py::lag1_autocorr

Effective sample size (n_eff)

Single series: n_eff = n·(1-r1)/(1+r1). Paired (Pyper & Peterman 1998): n_eff = n·(1-r1x·r1y)/(1+r1x·r1y)

Window
Same series/window as the r or mean it's correcting.
Limitations
Clamped to [2, n] — a negative-autocorrelation 'bonus' (n_eff > n) is discarded so the correction only ever moves toward conservatism, never inflates apparent evidence. First-order (Bartlett/AR(1)) only — does not model higher-order or seasonal autocorrelation. The core answer to the ADR-105 rule that daily physiological series (recovery, HRV, weight) are not i.i.d. and raw n overstates the evidence.
Used by
Every correlation surface: correlation_report (mcp/helpers.py), tools_training, the weekly correlation compute.

source: stats_core.py::effective_sample_size

Pearson p-value

Two-tailed via the t-distribution: t = r·√df / √(1-r²), df = n-2; erf-based normal approximation with a small-df shrink z = t·√(df/(df+2)) below df=30

Window
Takes fractional n, so it composes directly with effective_sample_size's autocorrelation-corrected n_eff rather than the raw observation count.
Limitations
None when |r| ≥ 1 or n ≤ 2. Accurate to ~3 decimals for df > 10, conservative (slightly wider) below — a stated intentional trade-off, not an unhandled edge case. Always compute on n_eff for daily series, never raw n (ADR-105).
Used by
correlation_report (the ONE correlation-reporting helper, replacing 6 duplicate copies), tools_training.

source: stats_core.py::pearson_p_value

Correlation confidence interval (Fisher z)

z_r = atanh(r); CI = tanh(z_r ± z_crit·SE), SE = 1/√(n-3)

Window
Same n as the r it's bounding — pass effective_sample_size's n_eff for autocorrelated daily series.
Limitations
None when |r| ≥ 1 or n ≤ 3. Supports exactly four confidence levels (0.80, 0.90, 0.95, 0.99) — anything else raises ValueError rather than silently approximating.
Used by
correlation_report, tools_training correlation reads.

source: stats_core.py::fisher_ci

Interval estimation

2 stats

Moving-block bootstrap CI

Percentile CI over 1000 resamples of contiguous blocks (default block length n^(1/3), floored at 2) — preserves short-range autocorrelation that i.i.d. resampling would destroy

Window
Caller-supplied series (paired or single); a fixed seed (1337) makes the interval reproducible for identical input — same data always yields the same interval.
Limitations
Needs n ≥ 5, and at least 100 of the 1000 replicates must produce a valid statistic or the call returns None (e.g. degenerate resamples with zero variance). Default statistic is Pearson r (paired) or the mean (single series); a custom stat may be passed in. Only 4 confidence levels supported (see fisher_ci).
Minimum n
5
Used by
weight_trend.py (the weight-rate confidence interval on /api/journey, #535).

source: stats_core.py::moving_block_bootstrap_ci

Bootstrap CI for a mean difference

Block-resamples baseline and window series independently (1000 replicates each), returns the percentile CI of mean(window) - mean(baseline)

Window
The two series the caller defines as 'baseline' and 'window' — the n-of-1 experiment primitive: is this metric different in the test window than before it started, and by how much?
Limitations
Both series need n ≥ 5 or the call returns None. Independent block-resampling means it does not account for any correlation between the baseline and window periods themselves (e.g. a slow seasonal drift spanning both).
Minimum n
5
Used by
experiment_design.py (evaluate_design) — the n-of-1 pre-registered experiment analysis (#539).

source: stats_core.py::bootstrap_mean_diff_ci

Effect size

1 stat

Cohen's d (effect size)

d = (mean(window) - mean(baseline)) / pooled SD, pooled SD from both groups' sample variances

Window
The same two caller-supplied series as bootstrap_mean_diff_ci — the two are always reported together (a CI without a size, or a size without a CI, is half the honesty bar).
Limitations
None when either group has n < 2 or pooled SD is 0. A standardized magnitude, not a significance test — always report alongside the bootstrap CI, never alone.
Used by
experiment_design.py (evaluate_design).

source: stats_core.py::cohens_d

Randomization inference

2 stats

Start-point randomization test (SCED)

For every candidate start k the pre-declared window could have produced: T(k) = mean(post_k) - mean(pre_k), washout excluded identically at each; one-sided p = #{T(k) at least as extreme as the observed split} / k_valid

Window
The full daily series of the pre-registered criterion metric, from baseline_days before the declared window's first candidate through the experiment's end — one continuous series, so every candidate start sees the same data.
Limitations
Only valid when the start was actually DRAWN at random from the frozen window (experiment_design.draw_start_date) — applied to a hand-picked start it is decorative, not inferential. Exact under the randomization performed, so no independence assumption is needed (the autocorrelation objection to parametric tests on N=1 daily series does not apply), but resolution is capped at p = 1/k: a 7-14 day window can never report below 1/7-1/14, which is a granularity floor, not high precision. Candidates with a thin arm (< 5 points after gaps) are excluded and reported in n_excluded; a pure pre-existing linear trend scores p ≈ 1 by construction — the coincident-trend confound the design exists to defeat.
Minimum n
2
Used by
experiment_design.randomization_test — the end_experiment close-path analysis for randomized-start designs (#1413); reported on /data/ experiment cards next to the effect + CI, and in the analysis summary sentence.

source: stats_core.py::start_point_randomization_test

BSTS-lite synthetic-control counterfactual (the Ghost)

y_t = beta·[1, controls_t] + mu_t + eps_t with mu a local-level random walk; beta by OLS on the pre-period, level variances by maximum innovations likelihood over a fixed signal-to-noise grid; post-period ghost = frozen level + regression, variance P_T + t·sigma_eta² + sigma_eps² + OLS prediction covariance

Window
The pre-period declared in the FROZEN design spec (pre_days before the intervention start, default 28) for fitting; the washout-trimmed post-period for the effect. Only days where the criterion AND every declared control have values — gaps dropped and counted, never imputed.
Limitations
Spec (controls, pre-window, MAPE gate) is frozen at pre-registration — applied post-hoc it is spec shopping, which the design gate exists to forbid. The ghost is withheld with a stated reason when the pre-period one-step-ahead MAPE exceeds the frozen gate, when fewer than 14 aligned pre-days exist, or when MAPE is unevaluable (criterion near zero). First-order only: a local level plus static regression — no seasonality or dynamic coefficients, so a strong weekly cycle the controls don't carry inflates MAPE and (correctly) withholds the ghost. The effect CI uses the full forecast-error covariance (shared level error correlates post days); the interval widens with distance from start by construction — the model's honesty about extrapolation, drawn as-is on the card.
Minimum n
14
Used by
experiment_design.counterfactual_analysis — the end_experiment close path for designs that froze a counterfactual spec (#1410); rendered on /data/ experiment cards as the ghost trace + widening band, and in the analysis summary sentence.

source: bsts_lite.py::fit_counterfactual

Forecasting

3 stats

EWMA fit (simple exponential smoothing)

level update: levelₜ = levelₜ₋₁ + α·(xₜ - levelₜ₋₁); α chosen by deterministic grid search over 0.05–0.95 (step 0.05) minimizing one-step-ahead squared error when not given explicitly

Window
Caller-supplied series in chronological order.
Limitations
Needs ≥ 4 clean points or returns None. The grid search always picks the same α for the same data (ties go to the smaller α) — deterministic, no randomness, per ADR-105's 'deterministic computation before any LLM verdict' rule.
Used by
ewma_forecast (below); indirectly, the forecast engine.

source: stats_core.py::ewma_fit

EWMA h-step-ahead forecast

Point forecast = final smoothed level; interval width σ_h = σ·√(1 + (h-1)·α²) where σ is the sample SD of one-step-ahead residuals

Window
Caller-supplied series; the platform's forecast engine runs this daily at 0.80 confidence — chosen deliberately below the more common 0.95 so the 'did the interval cover the outcome ~80% of the time?' calibration question is answerable in weeks, not months.
Limitations
Needs ≥ min_n clean points (default 10) and ≥ 3 residuals or returns None. An EXPECTATION extrapolated from the series' own recent pattern — never a causal claim. Supports only the 4 fisher_ci confidence levels.
Minimum n
10
Used by
forecast_engine_lambda.py — daily expectations graded into the calibration ledger.

source: stats_core.py::ewma_forecast

EWMA series (exponentially-weighted moving average)

ewaₜ = α·xₜ + (1-α)·ewaₜ₋₁, with α = 1 - exp(-1/decay_days); optional warm-start seed

Window
Caller-supplied chronological series; EWMA-ACWR runs it with a 7-day (acute) and 28-day (chronic) time-constant over the daily Whoop-strain series.
Limitations
Recent observations are weighted most, older ones decay smoothly — unlike a flat rolling mean, which weights its whole window equally and drops days off a cliff at the edge. ACWR = EWMA(acute)/EWMA(chronic) is a COUPLED ratio: the acute load is a mathematical component of the chronic load, so they move together by construction (Lolli et al. 2019) — a directional signal, not a precise injury predictor. The Gabbett zone thresholds it is compared against are population-derived (ADR-105 r4).
Used by
acwr_compute_lambda.py (EWMA-ACWR, #543); mcp/helpers.compute_ewa.

source: stats_core.py::ewma_series

Multiple comparisons

1 stat

Benjamini-Hochberg FDR correction

Sort p-values ascending; adjusted p(k) = min(1, m/(k+1) · p(k)), then enforced monotone non-decreasing from the largest rank down

Window
Applied across one batch of simultaneous comparisons (e.g. all correlation pairs computed by one tool call) — not across the platform's entire history of tests.
Limitations
None entries (untestable pairs) pass through unadjusted and don't count toward m. Controls the FALSE DISCOVERY rate, not the false-positive rate of any single test — with dozens of simultaneous correlation pairs, some individually-significant p-values will still be flagged non-significant after correction, by design.
Used by
correlation_report (per-tool FDR across every correlation in a batch).

source: stats_core.py::bh_fdr

Calibration

6 stats

Brier score

mean((p - y)²) over all (stated probability, realized outcome) pairs

Window
Every resolved prediction to date for the surface being scored (a coach, the hypothesis engine, or platform-wide) — grows as more predictions resolve, never a fixed lookback.
Limitations
0.0 is perfect, 0.25 is the always-say-50% baseline, 1.0 is confidently-wrong-every-time. None when there are no valid (probability in [0,1], outcome in {0,1}) pairs — an unresolved or too-new coach shows no score rather than a misleading 0.
Used by
The calibration scoreboard (/method/calibration/), /api/calibration, /api/coach_team, the coach track-record MCP tool.

source: stats_core.py::brier_score

Brier skill score

1 - (Brier score / Brier score of the base-rate climatology forecast)

Window
Same pair set as the Brier score it's compared against.
Limitations
None when fewer than 2 pairs or every outcome is identical (skill is undefined against a degenerate base rate). 1.0 is perfect, 0.0 means no better than always guessing the observed base rate, negative means worse than that baseline — the honest 'does stated confidence beat just guessing the average?' number.
Used by
The calibration scoreboard.

source: stats_core.py::brier_skill_score

Reliability curve (calibration bins)

Splits [0,1] into n_bins equal bands (default 10); each non-empty bin reports mean stated confidence vs. observed outcome rate

Window
Same pair set as the Brier score for that surface.
Limitations
Empty list when there are no valid pairs. Bins with very few points can show a noisy observed rate — the bin's n is always reported alongside so a 1-of-1 bin isn't read as strong evidence.
Used by
The calibration scoreboard's reliability curve.

source: stats_core.py::reliability_bins

Calibration summary (n, Brier, reliability, verdict)

Composes brier_score + brier_skill_score + reliability_bins over the same (confidence, outcome) pairs into one summary dict

Window
Every resolved prediction for the coach/surface being scored, to date.
Limitations
Needs at least 1 resolved pair to report accuracy_pct; the calibration verdict (over/under-confident) needs ≥ 5. Below those thresholds the relevant field is None rather than a guess dressed as a number. Also reports `skilled` (Brier skill > 0?) — calibrated (reliability: stated confidence tracks observed rates) and skilled (beats the base rate) are different claims, and the summary carries both so no surface can conflate them (#1370).
Used by
/api/calibration, /api/coach_team's per-coach calibration line.

source: calibration_core.py::score_pairs

Over/under-confident verdict

Weighted mean gap = Σ(bin_n · (bin_mean_confidence - bin_observed_rate)) / Σ(bin_n); gap > 0.15 → over-confident, gap < -0.15 → under-confident; otherwise well-calibrated only when Brier skill > 0 — a skill ≤ 0 surface reads not-yet-skillful instead (#1370)

Window
Same reliability bins as reliability_bins for that surface.
Limitations
Requires n ≥ 5 resolved predictions AND at least one non-empty bin, else 'insufficient_data'. The ±0.15 threshold is a fixed editorial choice (not derived from this platform's own variance) — a documented exception to the ADR-105 'thresholds from personal variance' rule, tracked as a candidate for future recalibration once more predictions resolve. 'Well-calibrated' asserts reliability AND skill: a forecaster whose stated confidence tracks observed rates but whose Brier skill is ≤ 0 (worse than always guessing the base rate) reads 'not_yet_skillful' — reliability alone never earns the flattering verdict (ADR-104/105, #1370). An undefined skill (degenerate base rate) is treated as unknown, not as unskilled.
Minimum n
5

source: calibration_core.py::score_pairs

Coarse credibility label (nascent / not-yet-skillful / developing / reliable / authoritative)

n < 3 → nascent. Brier skill ≤ 0 → not-yet-skillful. Brier ≤ 0.15 AND n ≥ 12 → authoritative. Brier ≤ 0.20 → reliable. Else → developing

Window
Same pair set as the Brier score for that surface.
Limitations
A coarse, backward-compatible label kept for surfaces that pre-date the Brier-based scorer — always shown alongside the underlying Brier score and n, never as a substitute for them. The 0.15/0.20 cut points are fixed, not personal-variance-derived. A negative Brier skill (worse than the base rate) can never render the reliable/authoritative rungs, however good the raw Brier looks against an extreme base rate — it reads not-yet-skillful (#1370).
Used by
Coach cards that need a single at-a-glance credibility word.

source: calibration_core.py::score_pairs

Provenance

1 stat

Conversational enrichment signals — analysis-only scope

scope = analysis_only (v1, #1577): LLM-coded signals from coach check-in answers, habit reflections, field-note responses and — since #1708 — reactions to the coach's weekly Horizons pick (channel prescription_reaction) seed HYPO_CANDIDATE# rows (channel-tagged) and are visible as enriched context, but they do NOT enter character or flourishing scoring. Absence of conversation is 'no data', never a zero (ADR-104). Dedup vs the journal (AC4): content-hash equality, or a ≥40-char normalized containment match.

Window
Rolling 14-day sweep on the daily 6:30 AM enrichment cadence; each record is coded once (re-coded only on a schema bump or explicit force).
Limitations
These numbers are a language model's (Haiku) reading of short conversational prose — never sensor data, and shorter/noisier input than a journal entry. ANALYSIS-ONLY by declared policy: promotion into character or flourishing scoring requires personal-variance thresholds derived from an observed baseline FIRST (ADR-105) plus a human re-review of this entry — the fingerprint below trips if the policy function changes without that review. Causal hints survive only the deterministic verbatim-quote grounding gate (ADR-104). Enrichment pauses at budget tier 1 (internal band, ADR-125); paused windows are visible as unenriched records, never fabricated. The #1708 prescription_reaction channel inherits this scope unchanged: it calibrates the DETERMINISTIC Horizons reaction ledger (counts only, no model verdict) and moves no scoring.
Used by
journal_analyzer_lambda HYPO_CANDIDATE# aggregation (channel provenance on every quote) → the weekly hypothesis engine's candidate seeding; enriched context wherever enriched_* fields are read.

source: conversation_enrichment.py::enrichment_policy

This page is generated by scripts/v4_build_methods.py from lambdas/methods_registry.py, the same registry served machine-readably at /api/methods. Each entry records a hash of the function it documents at the time its prose was last verified; a test in tests/test_methods_registry.py fails if the code changes without the entry being reviewed again — so this registry can be incomplete, but it cannot silently lie about a stat it already documents. generated, not authored