Double-verified finding
Two independent read-only audits reproduced this on exact fork integration base 032983d2bed9bf50dfcfc4d06ef707f3818cdc26. Current upstream main still contains the same behavior.
Both platform twins define a usable calibration day as:
That filter drives the visible stepsCal point lines and StepsEstimateEngine.status. But calibrate(..., manualOverride: > 0) / calibrate(..., manualOverride > 0) returns before applying it and sets sampleDays to every supplied point. The trace then prints that unfiltered count and also describes a user-set coefficient as a motion-weighted median.
Exact reproduction
Call the public calibration trace with these points and manualOverride = 9.5:
(motion: 0.5, steps: 500) — below the motion floor
(motion: 10, steps: 0) — no positive phone reference
(motion: 10, steps: 1000) — usable
Both platforms emit exactly one voting point:
stepsCal point motion=10.0 phoneRef=1000 ratio=100.0 (steps/motion votes weighted by motion)
but the fit line says:
stepsCal fit k=9.5 sampleDays=3 confidence=1.0 manual=true (k = motion-weighted median of steps/motion)
For the same input, status(...) reports a manual calibration with sampleDays=1.
Severity and reachability
P2 — diagnostic/provenance integrity. The coefficient, confidence, and estimated step values are not changed. The incorrect count is nevertheless persisted by the production orchestration and exported in the Steps diagnostic trace.
The path is production-reachable on both platforms: manual overrides are wired into the 60-day calibration pass, and calibration points admit any positive motion before the stricter motion >= 1.0 usability gate. The manual UI currently hides the count, limiting immediate user-visible impact.
Red tests
Add mirrored Swift and Kotlin tests with the three-point fixture above. Before the fix they must fail by proving:
calibrate(... manualOverride: 9.5).sampleDays == 1
status(... manualOverride: 9.5).sampleDays == 1
- exactly one
stepsCal point line is emitted
- the exact fit line contains
sampleDays=1
- the manual fit line says
(user-set k) and does not claim a weighted-median fit
Keep an all-usable manual fixture to prove the ordinary count remains unchanged.
Acceptance
- One canonical usability predicate determines manual
sampleDays everywhere.
- The returned calibration, status, persisted profile metadata, and trace agree on the usable-day count.
- A manual coefficient is described as user-set, never as a fitted weighted median.
- Manual coefficient precedence, confidence
1.0, estimates, and the automatic calibration path remain unchanged.
- Mirrored platform tests and the Steps differential trace rollout pass.
Routing
This is a production/helper fix, so implement it as a narrow upstream PR against ryanbr/noop; do not carry a fork-only production patch. After the maintainer merges it, sync upstream into drift/meta through the fork's merge workflow.
Final duplicate search across open/closed fork and upstream issues/PRs found no exact duplicate.
Double-verified finding
Two independent read-only audits reproduced this on exact fork integration base
032983d2bed9bf50dfcfc4d06ef707f3818cdc26. Current upstreammainstill contains the same behavior.Both platform twins define a usable calibration day as:
motion >= 1.0steps > 0That filter drives the visible
stepsCal pointlines andStepsEstimateEngine.status. Butcalibrate(..., manualOverride: > 0)/calibrate(..., manualOverride > 0)returns before applying it and setssampleDaysto every supplied point. The trace then prints that unfiltered count and also describes a user-set coefficient as a motion-weighted median.Exact reproduction
Call the public calibration trace with these points and
manualOverride = 9.5:(motion: 0.5, steps: 500)— below the motion floor(motion: 10, steps: 0)— no positive phone reference(motion: 10, steps: 1000)— usableBoth platforms emit exactly one voting point:
but the fit line says:
For the same input,
status(...)reports a manual calibration withsampleDays=1.Severity and reachability
P2 — diagnostic/provenance integrity. The coefficient, confidence, and estimated step values are not changed. The incorrect count is nevertheless persisted by the production orchestration and exported in the Steps diagnostic trace.
The path is production-reachable on both platforms: manual overrides are wired into the 60-day calibration pass, and calibration points admit any positive motion before the stricter
motion >= 1.0usability gate. The manual UI currently hides the count, limiting immediate user-visible impact.Red tests
Add mirrored Swift and Kotlin tests with the three-point fixture above. Before the fix they must fail by proving:
calibrate(... manualOverride: 9.5).sampleDays == 1status(... manualOverride: 9.5).sampleDays == 1stepsCal pointline is emittedsampleDays=1(user-set k)and does not claim a weighted-median fitKeep an all-usable manual fixture to prove the ordinary count remains unchanged.
Acceptance
sampleDayseverywhere.1.0, estimates, and the automatic calibration path remain unchanged.Routing
This is a production/helper fix, so implement it as a narrow upstream PR against
ryanbr/noop; do not carry a fork-only production patch. After the maintainer merges it, sync upstream intodrift/metathrough the fork's merge workflow.Final duplicate search across open/closed fork and upstream issues/PRs found no exact duplicate.