Skip to content

fix: restore live-decode regressions guards - #15

Open
abdulsaheel wants to merge 5 commits into
mainfrom
fix/restore-live-decode-guards
Open

fix: restore live-decode regressions guards#15
abdulsaheel wants to merge 5 commits into
mainfrom
fix/restore-live-decode-guards

Conversation

@abdulsaheel

@abdulsaheel abdulsaheel commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Reverts three uncommitted, unproven tweaks to known-good behavior, each now documented in place:
    • parseRealtimeHr's RR sanity clamp restored to 200-2500ms (24-300bpm) — a relaxation to v>0 let corrupted/out-of-range timing values straight into live RR with no guard.
    • _r10Motion's detrend window restored to 9 — a widen to 25 had no test coverage and risked attenuating genuine step periodicity (the autocorrelation lag range it feeds is 7-40 samples).
    • wristOn restored to hr>0skinContact>50 broke the 2934-case parity suite (28 misses, all false-negative "not worn"); skinContact is contact quality, not wear, exactly as already documented, and the fixture proves it (28 real v24 records with hr=87-97 clearly worn, skinContact<=50).
  • Removes ts/ (the TypeScript reference implementation) — superseded by the frozen decode_parity_cases.json oracle, which dart test already runs against with no runtime dependency on ts/.

Test plan

  • dart test — 71/71 passing, including the full 2934-case parity suite.

(Re-opened as a fresh branch/PR — the prior PR #14 covering this same work was closed without merging.)

Summary by CodeRabbit

  • Breaking Changes

    • Removed support for decoding live sensor, motion, heart-rate, and batch records.
    • Removed support for parsing Type-24 records and exposing their associated measurements.
    • Removed the standalone decoder validation utility.
    • Applications relying on these decoding capabilities may need to migrate or update their integrations.
  • Documentation

    • Added clarifying comments for existing motion detrending and wrist-detection behavior.

Three uncommitted tweaks had crept in without justification or test
coverage; reverted all three back to known-good behavior and left a
comment at each site so they don't get silently re-applied:

- wristOn: skinContact>50 broke the parity suite (2906/2934, all 28
  misses on wrist_on) — 28 real v24 records show hr=87-97 (clearly
  worn) with skinContact<=50, proving skinContact is contact quality,
  not wear, exactly as already documented. Back to hr>0, which matches
  the oracle on every case.
- parseRealtimeHr's RR sanity clamp (200-2500ms = 24-300bpm) had been
  relaxed to `v>0`, letting corrupted/out-of-range timing values
  straight into live RR with no guard.
- _r10Motion's detrend window had been widened 9->25 with nothing
  backing it; ±25 approaches the 7-40 sample autocorrelation lag range
  it feeds, risking attenuation of genuine step periodicity. No test
  (parity or otherwise) exercises steps_inc/activity from R10, so
  there was no way to validate the change either way.

dart test: 71/71 passing, including the full 2934-case parity suite.
Was the original reference the Dart decoders were ported from and kept
in parity with (7 commits, right up through the latest feature work),
not a one-off leftover — but decode_parity_cases.json is now the
frozen, self-contained oracle dart test actually runs against, with no
runtime dependency on ts/ (verified: no test/config spawns node/tsc
against it). Drops the unported v25-handling WIP that was sitting in
ts/live.ts uncommitted; that work is not carried over to live.dart.
@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: ac2136fb-51f6-4017-9187-550aae949c94

📥 Commits

Reviewing files that changed from the base of the PR and between 44d5f60 and 37d0231.

📒 Files selected for processing (1)
  • lib/src/live.dart

📝 Walkthrough

Walkthrough

The change adds comments to the Dart live decoder and removes the TypeScript live decoder, Type-24 parser, and decoder test script. No Dart logic or public signatures change.

Changes

Decoder cleanup

Layer / File(s) Summary
Live decoder notes and TypeScript removal
lib/src/live.dart, ts/live.ts, ts/records.ts, ts/test_decoder.ts
Comments document the retained detrending window and wristOn logic. The legacy TypeScript decoders and decoder test script are removed.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Title check ⚠️ Warning The title claims that live-decode regression guards were restored, but the changes remove archived TypeScript code and add documentation only. Rename the pull request to describe the actual changes, such as removing the archived TypeScript implementation and documenting existing live-decode behavior.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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.

@abdulsaheel

Copy link
Copy Markdown
Contributor Author

Reviewed this as part of a sweep across the open PRs. Recommending rebase-or-close — this is a judgement call rather than something I should push a commit for, so I've left the branch alone.

First, a correction to the summary I was working from: this PR is not "only a ts/ deletion". It adds 14 real lines to lib/src/live.dart and lib/src/control.dart. But those lines are comments arguing to revert behaviour that has since merged, which is where the problem is.

It's CONFLICTING, and it defends a code state that no longer exists

The two positions it argues for were both settled on main after this branch was cut:

1. activity: 0, stepsInc: 0 for the historical family. The diff context still shows the old fabricated zeros. main now reads:

// This record family carries no IMU stepping window at all — that is
// "no usable IMU data", the same absence DecodedSample.activity's doc
// comment describes for a truncated R10, not a measured 0.0/0.
activity: null,
stepsInc: null,

That landed in #21 along with the corresponding 629-case parity-oracle update. Resolving this branch's conflicts toward its own prose would put the fabrication back.

2. The w = 9 detrend window. The comment says it "was bumped to 25 without comment/test coverage". That's no longer accurate on main either.

The evidentiary argument is circular

The PR defends wrist_on = hr > 0 by pointing at the parity oracle: 28 real v24 records with hr 87–97 and skinContact <= 50, where hr > 0 matches ground truth on every case.

But the oracle's wrist_on column was generated by ts/live.ts:229:

return { ts: d.ts_epoch, hr: d.hr, activity: 0, steps_inc: 0, wrist_on: d.hr > 0, rec_type: 24 }

It is literally hr > 0. So the oracle agreeing with hr > 0 isn't evidence — it's the same expression read back. And this PR deletes that file, which removes the only thing that would let a reader discover the circularity.

Worth being clear: hr > 0 may well still be the right rule, and the underlying point that skinContact is contact quality rather than wear is sound and matches the protocol notes. The objection is only that the parity oracle can't be the evidence for it. Real evidence would be captures with independently-known wear state.

On deleting ts/

Deleting it is directionally fine — ts/ was declared archive on 2026-07-30 ("do not maintain going forward"), and the regression oracle that actually matters, decode_parity_cases.json, is untouched here. So this part isn't objectionable in itself; it just shouldn't ride along with reverts, and it does cost the provenance trail for the wrist_on column above.

Recommendation

Rebase onto main and reopen only what survives, or close. After a rebase I'd expect most of this to be moot: the activity/stepsInc argument is resolved the other way, the w argument no longer applies, and the wrist_on claim needs different evidence than the oracle. If a standalone "archive ts/" PR is wanted, that's clean and uncontroversial on its own.

Flagging rather than acting because merging or closing someone's PR on a contested judgement isn't my call — @-maintainers, your view?

# Conflicts:
#	lib/src/control.dart
#	lib/src/live.dart
…docs

Conflicts resolved toward MAIN in both files, deliberately. Resolving toward
this branch's prose would have reverted hardening that has since merged:

  * live.dart -- this branch wanted `activity: 0, stepsInc: 0` for the
    historical family. Main has `activity: null, stepsInc: null` (protocol#21):
    that family carries no IMU stepping window at all, so a zero is a
    FABRICATED measurement, not an absence. Reverting would reintroduce exactly
    the bug that fix removed.

  * control.dart -- this branch wanted the 200-2500 ms RR bound restored over a
    relaxed `v > 0`. Main ALREADY applies that bound (`kMinRrMs`/`kMaxRrMs`),
    and additionally reads all four declared RR slots instead of stopping after
    two, which had been silently dropping beats 3 and 4 and changing RMSSD. So
    this branch's concern is already satisfied, and its version is strictly
    worse.

WHAT IS LEFT after an honest resolution is the `ts/` removal plus two comments
-- ZERO behaviour change. That is the accurate description of this PR now.

Corrected one of those comments rather than leaving it: it claimed the detrend
window "was bumped to 25 without comment; reverted", but main already reads 9,
so nothing is being reverted. Rewritten as a note on why not to widen it.

Also stated the limit of this branch's own evidence at the `wristOn` seam. The
skinContact-is-quality-not-wear point is sound and worth documenting -- 28 real
v24 records read hr 87-97 with skinContact <= 50 -- but the parity oracle
cannot EVIDENCE `hr > 0`, because that column was generated by the same
`hr > 0` expression in ts/live.ts, which this PR deletes. Confirming it needs
captures with independently known wear state.

dart analyze clean; 111 passing / 4 skipped (the skips are fixture-dependent
and pass from a worktree next to whoop_hist.jsonl).
@abdulsaheel

Copy link
Copy Markdown
Contributor Author

Conflicts resolved (44d5f60). Mergeable now — but the honest resolution changes what this PR is, so read this before merging.

Both conflicts resolved toward main, deliberately

Resolving toward this branch's prose would have reverted hardening that has since merged:

live.dart — this branch wants activity: 0, stepsInc: 0 for the historical family. Main has activity: null, stepsInc: null (#21): that family carries no IMU stepping window at all, so a zero is a fabricated measurement, not an absence. Reverting reintroduces exactly the bug that fix removed.

control.dart — this branch wants the 200–2500 ms RR bound restored over a relaxed v > 0. Main already applies that bound (kMinRrMs/kMaxRrMs), and reads all four declared RR slots instead of stopping after two, which had been silently dropping beats 3 and 4 and changing RMSSD. So the concern is already satisfied and this branch's version is strictly worse.

What's actually left

The ts/ removal plus two comments — zero behaviour change. That's the accurate description of this PR now, and it's worth stating plainly since the title still says "restore live-decode regressions guards": there are no guards left to restore, because main already has them.

The ts/ removal itself is fine and aligned with the archive decision. decode_parity_cases.json — the oracle that actually matters — is untouched.

Two corrections I made rather than leaving

The detrend-window comment claimed a revert that never happened. It said w "was bumped to 25 without comment; reverted" — but main already reads 9. Rewritten as a note on why not to widen it, which is the useful part.

Stated the limit of this branch's own evidence at the wristOn seam. The skinContact-is-quality-not-wear point is sound and worth documenting — 28 real v24 records read hr 87–97 with skinContact <= 50. But the parity oracle cannot evidence hr > 0, because that column was generated by the same hr > 0 expression in ts/live.ts, which this PR deletes. Confirming it needs captures with independently known wear state. I kept the conclusion and removed the circular justification.

dart analyze clean; 111 passing / 4 skipped (the skips are fixture-dependent and pass from a worktree beside whoop_hist.jsonl).

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
lib/src/live.dart (1)

16-27: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Return null for compact HR packets without IMU data.

activity and stepsInc define null as unavailable and 0 as a measured result. The 0x28 branch at Lines 322-323 still emits 0 for both fields, although it decodes only timestamp and heart rate. Downstream consumers can persist fabricated zero-motion and zero-step measurements. Return null for both fields in that branch.

Suggested fix
-      activity: 0,
-      stepsInc: 0,
+      activity: null,
+      stepsInc: null,
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/src/live.dart` around lines 16 - 27, Update the 0x28 compact HR packet
branch to assign null to both activity and stepsInc, since that branch decodes
only timestamp and heart rate. Preserve measured zero values for branches that
actually decode IMU data.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@lib/src/live.dart`:
- Around line 346-352: Update the wristOn provenance comment near the wristOn
logic in live.dart so it no longer references the removed ts/live.ts path.
Preserve the hr > 0 generation rule and parity-fixture limitation by documenting
them in a surviving fixture or documentation record, or cite an immutable
archived source.

---

Outside diff comments:
In `@lib/src/live.dart`:
- Around line 16-27: Update the 0x28 compact HR packet branch to assign null to
both activity and stepsInc, since that branch decodes only timestamp and heart
rate. Preserve measured zero values for branches that actually decode IMU data.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 004e146f-dcbf-43d1-a5ea-fc6f59ea6e14

📥 Commits

Reviewing files that changed from the base of the PR and between be1d530 and 44d5f60.

📒 Files selected for processing (1)
  • lib/src/live.dart

Comment thread lib/src/live.dart Outdated
CodeRabbit, correctly: the comment I added cites `ts/live.ts`, and this PR
deletes it -- so the citation would be a dangling path the moment it merges.
That is precisely the doc-rot this project fights, in a comment written to
prevent doc-rot.

The rule itself is now recorded INLINE (`wrist_on: d.hr > 0`) rather than by
reference, so the provenance survives the deletion, plus an immutable
recovery path for the original:

    git show 7edcb3e:ts/live.ts | sed -n '229p'

7edcb3e is protocol main, where the file still exists; verified the line is
there and is the expression quoted.

The substantive point is unchanged: the parity oracle cannot EVIDENCE
`hr > 0`, because `decode_parity_cases.json`'s `wrist_on` column was generated
by that same expression. Confirming it needs captures with independently known
wear state.

dart analyze clean; 111 passing / 4 skipped.
@abdulsaheel

Copy link
Copy Markdown
Contributor Author

@coderabbitaivalid, fixed in 37d0231. Good catch, and a slightly embarrassing one: the comment I added to prevent doc-rot cited a path this very PR deletes.

The rule is now recorded inline (wrist_on: d.hr > 0) rather than by reference, so the provenance survives the deletion, plus an immutable recovery path:

git show 7edcb3e:ts/live.ts | sed -n '229p'

7edcb3e is protocol main, where the file still exists — verified the line is there and is the expression quoted.

The substantive point is unchanged: decode_parity_cases.json's wrist_on column was generated by that same expression, so the oracle agreeing with hr > 0 is the expression read back, not evidence. Confirming it needs captures with independently known wear state.

@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

@abdulsaheel Confirmed. The inline expression preserves the rule after ts/live.ts is deleted. The commit-based recovery path preserves the historical source location.

The comment now correctly states the evidence limit: decode_parity_cases.json cannot independently validate wrist_on: d.hr > 0. Validation requires captures with independently known wear state.

🐇 ✓


✏️ Learnings added
Learnt from: abdulsaheel
URL: https://github.com/OpenStrap/protocol/pull/15

Timestamp: 2026-08-07T17:09:31.296Z
Learning: In this protocol repository, the `decode_parity_cases.json` `wrist_on` field was generated by the archived TypeScript expression `wrist_on: d.hr > 0` in `ts/live.ts`. Therefore, parity-oracle agreement cannot independently validate the Dart `wristOn` rule. Validation requires captures with independently known wear state.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


If you found this review helpful, would you consider giving us a shout-out on X?

Thank you for using CodeRabbit!

You are interacting with an AI system.

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.

1 participant