Repository navigation
Resilience (2/2): Experimental section in the Healthspan tab - #16
Merged
Merged
Conversation
…n reads Reads repo.dailyMetrics once, runs ResilienceEngine off the main actor for steps (ending yesterday), resting HR and HRV, and computes the rolling trend on demand with the headline result as its newest point.
…method sheet Hero with the recovery time and its 90 % range on a log band beside the cohort ticks, three signal chips (steps published, RHR and HRV labelled as extensions), and a detail screen with the measured autocorrelation and its fit, the last three knocks, the 12-month trend with its range, and the day-to-day swing. Shown only when the Settings toggle is on.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
PR 2 of 2 for the Resilience plan. It is stacked on #15, so merge #15 first.
What changes
This PR adds an Experimental Resilience section to the Healthspan screen. It is shared by macOS and iOS. The section is shown only when Settings → Experimental · Resilience is on, and that toggle is off by default (
noop.resilienceEnabled).Loader.
ResilienceLoader(Strand/Data) is the one funnel. It readsrepo.dailyMetricsonce and runsResilienceEngineoff the main actor for three signals:Nothing is persisted. The 12-month trend (one point every 14 days, computed in parallel) is built when the detail screen opens. Its newest point is the snapshot's own headline result, passed through rather than recomputed, so the chart and the hero cannot disagree.
Hero. It shows "Recovery time ≈ N days" with its 90 % range. The range is drawn on a log day axis, with faint ticks for the cohort averages (about 2 weeks at age 40 and 8 weeks at age 90), captioned as context and not a score. When the upper bound reaches the 180-day ceiling, the range reads "N days or longer". Two other states replace the headline:
Signal chips. Steps is labelled Published. Resting HR and HRV are labelled Extension. Each chip shows its τ and range; tapping one switches the section to that signal.
Detail screen:
Method sheet. It covers the Langevin/recovery-time idea, the difference between cohort and individual results, the bias correction, the extension labels and the references. The references use
Text(verbatim:).Deviation from the plan. The autocorrelation chart does not draw an interval ribbon. The 90 % range belongs to the bias-corrected τ, and the raw points are biased short against it, so drawing both on one chart would show two readouts that seem to disagree (the AGENTS.md rule). The caption states the raw fit's τ and says that the headline corrects it.
Screenshots
These were rendered off-screen with
ImageRendererfrom synthetic data: steps with τ = 14, and resting HR with τ = 6 plus an injected illness-like spike. They are not from real history.The first render of the knocks card showed a knock "back in your usual range after 2 days" next to a "half-life ≈ 41.6 days". That half-life came from a flat fit stuck at the search ceiling. The fix is in the engine (#15,
fix(analytics): report a knock's half-life only when the fit describes its path). The card above is from after the fix.Verification
ResilienceReadoutTests(StrandTests) covers:StrandTests: 1,949 tests, 0 failures, 1 skipped.xcodebuildsucceeds forStrand(macOS) andNOOPiOS.NOOPiOSwas built without the embedded watch app, because the watchOS runtime is not installed locally.StrandPalette,StrandFont,NoopMetrics, and the StrandDesignNoopCard,SectionHeader,SourceBadgeandComingSoon.Localizable.xcstringsin all nine shipped locales (de, es, fr, it, pl, pt-PT, ru, zh-Hans, zh-Hant). Format specifiers were checked against each key, including the escaped%%in interpolated keys and positional arguments where the word order changes.i18n_audit --ci mainanddoc_comment_lintare clean.No BLE, schema, persistence or backup-whitelist changes. The toggle is a local
@AppStoragepreference and is not added to.noopbak.