PR #28 landed the durability v1 math as a pure analytics module (analytics/durability.py: the work-above-CP fatigue axis and the work-conditioned power decrement), but neither surface is wired further:
durability_decrement is not exposed through AnalyticsService, has no MetricName member, and has no agent capability resolver — the coach cannot retrieve or cite it, so the metric exists but cannot influence a single answer.
work_above_cp_j finally has a definition, but the DerivedActivityMetric.work_above_cp_j column stays dormant — nothing computes-and-persists it on ingest/sync.
That leaves the product's second fitness axis invisible to the agent — the opposite of the point. Scope for the wiring slice:
AnalyticsService.durability(...) (athlete + activity window → MetricResult), same purity/fail-closed envelope as siblings.
- A
MetricName member + capability resolver so retrieval plans can name it and grounding can verify/cite it (metric, value, as_of like every other citation).
- Populate
work_above_cp_j in the derived-activity path so the axis is queryable per activity.
- Metric-alias entries (plain-language names → canonical key) so a real model's wording resolves.
- Tests per the established pattern (golden + property at the service seam; the pure-module suites already exist), and the metrics reference moves the entry from "module-level" truth to service-exposed truth.
The docs reference (#32 cascade) currently documents both honestly as computed-by-module; this issue closes the gap between "computed" and "usable by the coach."
PR #28 landed the durability v1 math as a pure analytics module (
analytics/durability.py: the work-above-CP fatigue axis and the work-conditioned power decrement), but neither surface is wired further:durability_decrementis not exposed throughAnalyticsService, has noMetricNamemember, and has no agent capability resolver — the coach cannot retrieve or cite it, so the metric exists but cannot influence a single answer.work_above_cp_jfinally has a definition, but theDerivedActivityMetric.work_above_cp_jcolumn stays dormant — nothing computes-and-persists it on ingest/sync.That leaves the product's second fitness axis invisible to the agent — the opposite of the point. Scope for the wiring slice:
AnalyticsService.durability(...)(athlete + activity window → MetricResult), same purity/fail-closed envelope as siblings.MetricNamemember + capability resolver so retrieval plans can name it and grounding can verify/cite it (metric, value, as_oflike every other citation).work_above_cp_jin the derived-activity path so the axis is queryable per activity.The docs reference (#32 cascade) currently documents both honestly as computed-by-module; this issue closes the gap between "computed" and "usable by the coach."