Skip to content

feat(energy): price walking into Active energy from measured cadence (v77) - #283

Merged
abdulsaheel merged 2 commits into
OpenStrap:mainfrom
DropTabl:feat/walking-cadence-active-energy
Aug 26, 2026
Merged

feat(energy): price walking into Active energy from measured cadence (v77)#283
abdulsaheel merged 2 commits into
OpenStrap:mainfrom
DropTabl:feat/walking-cadence-active-energy

Conversation

@DropTabl

@DropTabl DropTabl commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Unblocked. OpenStrap/analytics#52 merged; all three pin locations (pubspec.yaml ref, pubspec.lock, kAnalyticsPin beside kAlgoVersion) moved together to its main merge commit a077a4a, and the branch is rebased onto current main. The hop is exactly #52 (calories.dart + tests) — the number it moves IS this PR's change, priced in by the v77 bump + migration.

The bug (user report: "Walking calories are not counted")

A Walking workout records fine, its steps count fine, and the day's Active energy never moves. Not a wiring or sync failure: MOT-02's HR-flex gate deliberately bills nothing below the ACSM moderate floor (its own changelog: billed wake minutes → 4.9%, median daily active → 48 kcal), and a walk at ~95 bpm lives entirely below it. The session's bout number — which bills sub-gate samples at the resting rate — made the gap read like a sync bug.

The fix (edge half)

The analytics half (OpenStrap/analytics#52) prices sub-gate minutes from measured cadence — CADENCE-Adults' published 100/110/120/130 spm ↔ 3/4/5/6 METs line, the one gait signal this platform actually has after MT-05 ruled the 1 Hz accel out. This PR wires it:

  • lib/compute/step_cadence.dart — the ONE windows→minutes cadence mapping, fed by the day's resolved live_coverage spans (credited, never raw rows, so band/phone overlap can't double-price a step). Partial coverage pro-rates down: a walk's boundary minute under-bills rather than half a minute of walking pricing a full MET-minute. Untouched minutes are null — unmeasured, not zero.
  • Both energy passes go through that one mapping: the coordinator's canonical wakeDayEnergy and the pure pipeline's early-read mirror (DayBundleInput carries the spans across the isolate) — so Today's early number and the derived day bill a walk identically instead of the figure growing when the coordinator's pass lands.
  • wakeDayEnergy filters the cadence series in the same pass as its off-skin HR filter; a dropped HR entry without its cadence would price every later cadence against the wrong minute (pinned by test).
  • The TDEE block discloses the term (live_coverage_pedometer in inputs_used, walking kcal in the note) only on days it actually priced something.
  • kAlgoVersion 76 → 77, changelog above the constant. A day with no pedometer coverage derives byte-identical to v76. No settings, no UI changes — Home's "Active energy" card and the Health export pick the figure up through the existing seam.

What it means for the reported walk

Tracked Walking workout, phone present (so the 100 Hz pedometer covered it), HR below the gate: the walk's minutes now bill (MET − 1) basal-minutes each — e.g. an hour at 110 spm ≈ 220 kcal for an 80 kg profile, previously 0. Walks with no live link stay unpriced (gen4 has no durable 24/7 cadence) — honest abstention, unchanged.

Tests

  • step_cadence_test.dart — the mapping: whole/partial minutes, overlap summing, measured-zero vs null, degenerate spans.
  • daily_energy_consistency_test.dart — the reported walk bills; total − active == basal (the Health-export invariant) survives the walking term; cadence stays aligned across the off-skin filter.
  • flutter analyze clean; full suite green — 3,037 passed / 0 failed against the flipped a077a4a pin, rebased on current main.

Companion: OpenStrap/analytics#52 (the model + citations). Fixes the "Walking calories are not counted" report.

@coderabbitai

coderabbitai Bot commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

Next included review available in 24 minutes.

View limit details

Limit details: You’ve used all 2 included reviews currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 62ead3d5-62c5-4be1-b73b-5dc349c21981

📥 Commits

Reviewing files that changed from the base of the PR and between 83375f2 and 916a54b.

⛔ Files ignored due to path filters (3)
  • pubspec.lock is excluded by !**/*.lock
  • test/daily_energy_consistency_test.dart is excluded by !test/**
  • test/step_cadence_test.dart is excluded by !test/**
📒 Files selected for processing (4)
  • lib/compute/derivation_engine.dart
  • lib/compute/onehz_pipeline.dart
  • lib/compute/step_cadence.dart
  • pubspec.yaml

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

DropTabl and others added 2 commits August 26, 2026 15:26
…(v77)

The reported bug ("Walking calories are not counted"): a Walking workout
records fine, its steps count fine, and the day's Active energy never moves.
Root cause is not a wiring or sync failure — MOT-02's HR-flex gate
deliberately bills nothing below the ACSM moderate floor, and a walk lives
entirely below it. The session's own bout number (which bills sub-gate
samples at the resting rate) made the gap read like a sync bug.

The analytics half (OpenStrap/analytics#52) prices sub-gate minutes from
MEASURED cadence — CADENCE-Adults' published 100/110/120/130 spm ↔ 3/4/5/6
METs line, the one gait signal this platform actually has after MT-05 ruled
the 1 Hz accel out. This is the edge half:

- lib/compute/step_cadence.dart: the ONE windows→minutes cadence mapping,
  from the day's RESOLVED live_coverage spans (credited, never raw rows, so
  band/phone overlap cannot double-price a step). Partial coverage pro-rates
  DOWN — a walk's boundary minute under-bills rather than half a minute of
  walking pricing a full MET-minute; untouched minutes are null, unmeasured.
- Both energy passes feed it through that one mapping: the coordinator's
  canonical wakeDayEnergy AND the pipeline's early-read mirror
  (DayBundleInput carries the spans across the isolate), so the early read
  and the derived day bill a walk identically instead of the number growing
  when the coordinator's pass lands.
- wakeDayEnergy filters the cadence series in the SAME pass as the off-skin
  HR filter — dropping an HR entry without its cadence would price every
  later cadence against the wrong minute (pinned by test).
- The TDEE block discloses the term (live_coverage_pedometer in inputs_used,
  walking kcal in the note) only on days it actually priced something.
- kAlgoVersion 76 → 77. A day with no pedometer coverage derives
  byte-identical to v76.

PIN GATE: the pubspec analytics pin is NOT flipped yet — it must move to the
OpenStrap/analytics#52 merge SHA (together with kAnalyticsPin) before this
can merge, per invariant OpenStrap#5. Draft until then.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015TxisrChNFxtVMXFFNRGtV
… exists to consume

All three pin locations move together to analytics main @ a077a4a, the
OpenStrap/analytics#52 merge commit: the cadence→MET walking term in
Calories.dailyEnergy (CADENCE-Adults). Unlike the usual repin this one
exists to move a number — that number IS this branch's change, priced in
by the kAlgoVersion 77 bump and the v77 migration. The hop is exactly
OpenStrap#52 and nothing else (calories.dart + its tests).
@DropTabl
DropTabl force-pushed the feat/walking-cadence-active-energy branch from 940808e to 916a54b Compare August 26, 2026 13:34
@DropTabl DropTabl changed the title feat(energy): price walking into Active energy from measured cadence (v77) [blocked on analytics#52] feat(energy): price walking into Active energy from measured cadence (v77) Aug 26, 2026
@DropTabl
DropTabl marked this pull request as ready for review August 26, 2026 13:34
@abdulsaheel
abdulsaheel merged commit ded44a3 into OpenStrap:main Aug 26, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants